UNET: How do I properly handle Client Authority with interactable Objects?
I'm currently working on a VR game and one of the basic interactions should be the manipulation of objects. A player should be able to grab an object and throw it and another player should be able to catch it. For this to work the player should have authority over the object he holds/interacts with. To test this out I've created a very basic scene but ran into some problems. I can change PlayerAuthority sometimes but most of the time the player who is also the server (host) has authority.
As a quick and dirty method I'm using triggers to change authority. When a Local Player touches the object he gets authority and can interact with the object. When he leaves the trigger authority will be revoked. This is how the trigger on my test object looks like:
The controller of the local player has the tag "VRController" and the local player himself has the tag "VRLocalPlayer".
void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("VRController"))
{
if (!isServer)
{
GameObject player = GameObject.FindGameObjectWithTag("VRLocalPlayer");
player.GetComponent<VRPlayerController>().CmdSetAuth(netId, player.GetComponent<NetworkIdentity>());
}
}
}
void OnTriggerExit(Collider other)
{
if (other.gameObject.CompareTag("VRController"))
{
if (!isServer)
{
GameObject player = GameObject.FindGameObjectWithTag("VRLocalPlayer");
player.GetComponent<VRPlayerController>().CmdRemAuth(netId, player.GetComponent<NetworkIdentity>());
}
}
}
On the actual Local Player Object are the Commands:
[Command]
public void CmdSetAuth(NetworkInstanceId objectId, NetworkIdentity player)
{
var iObject = NetworkServer.FindLocalObject(objectId);
var networkIdentity = iObject.GetComponent<NetworkIdentity>();
networkIdentity.AssignClientAuthority(player.connectionToClient);
}
[Command]
public void CmdRemAuth(NetworkInstanceId objectId, NetworkIdentity player)
{
var iObject = NetworkServer.FindLocalObject(objectId);
var networkIdentity = iObject.GetComponent<NetworkIdentity>();
networkIdentity.RemoveClientAuthority(player.connectionToClient);
}
This works sometimes but only under certain conditions. Most of the time a client can see what the Host does but not vice versa. With this script enabled the host is also able to see what another client does. But when the host touches an object adn puts it back down the client can pick it up on his end but the host won't be able to see it.
This is the first time I'm messing with Unet or multiplayer games in general, so I'm pretty sure I'm forgetting alot of things here.
I implemented some changes today to make it all more robust and maybe even fix it.
I moved away from using trigger event and just use a function that activates when an object is actually grabbed. Now I can execute code when I grab an object and when I release it. When I grab it I send the command to assign authority to the player that is grabbing the object und when the player releases it I send the command to release it.
This doesn't change a whole lot other than it doesn't get triggered multiple times in a row. Just once when you grab and once when you release. This actually works better too even so it doesn't work yet. One player can grab an object und move it and the other player can grab an object and move it too. But when a player releases the object and trys to grab the object the other player was holding (but isn't holding anymore) he can't move it. It seems like the object is still connected to the other client.
The first AssignclientAuthority call seems to work as expected but the RemoveClientAuthority doesn't work at all or doesn't work in a way I'm expecting it to work. I have no idea what is causing this. The first half of what I'm trying to do works but not the secand half.
In short once an object is assigned to a player the player can move his object as much as he wants and the other player will see it. But when he puts the object down and the other player trys to move it, the object is locked in place or move a little bit and then snaps back into position. I'm not syncing the postion of any objects manually I'm just using the Transform Component.
Hi, Have you found a solution to this? I have been having pretty much the same problem - client cannot pick up an object, or if they could the server wouldn't see what they were doing and it would snap back to the servers known position of it.
Answer by Glomby · Nov 16, 2016 at 05:24 PM
It's still not perfect but I have a working solution now.
Basically the way it works now is like this: Each object that is interactable has a script attached which contains an event listener. Whenever a player grabs an object this event will be called. This is how it looks like:
private void OnGrab(object sender, InteractableObjectEventArgs e)
{
player = GameObject.FindGameObjectWithTag("VRLocalPlayer");
playerID = player.GetComponent<NetworkIdentity>();
player.GetComponent<VRPlayerController>().CmdSetAuth(netId, playerID);
}
The local player object has a tag to identify it. This is what this script is looking for first. Then I need the netID of the player and the object. The player object has a player controller script attached and this is what I'm calling next.
[Command]
public void CmdSetAuth(NetworkInstanceId objectId, NetworkIdentity player)
{
var iObject = NetworkServer.FindLocalObject(objectId);
var networkIdentity = iObject.GetComponent<NetworkIdentity>();
var otherOwner = networkIdentity.clientAuthorityOwner;
if (otherOwner == player.connectionToClient)
{
return;
}else
{
if (otherOwner != null)
{
networkIdentity.RemoveClientAuthority(otherOwner);
}
networkIdentity.AssignClientAuthority(player.connectionToClient);
}
}
This is where I manage the actual authorities. So whenever a player grabs an object this function checks if the player already has authority and does nothing. If the player doesn't have authority it checks if anyone else has authority and removes it and lastly gives the authority to the player who grabs the object.
This makes sure that only one player at a time has authority. It works pretty well but it could still need some refinements.
Answer by mr_pablo · May 12, 2017 at 04:30 PM
I am having the same issue, however, my spawned object is always classed as being the server (which i guess it is...)
But I need the object to be spawned by the server, but hand over authority?
One player can get authority, and even though it is released after interacting, the next player cannot get authority, despite requesting it, using your code.