13
votes

I have a multiplayer turn-based strategy game that needs a game manager, controlling current game state (who's turn it is etc.). This manager should be common for every client, it's state should be synchronized on server.

Here's how I'm doing this: The game manager object is NetworkBehaviour and it has NetworkIdentity which is not local player authority nor server authority. I've made a custom NetworkManager and it spawns the Game Manager on client connect, also testing if it is a server. Here's a code:

public override void OnClientConnect(NetworkConnection conn)
    {
        ClientScene.Ready(conn);
        if (NetworkServer.active)
        {
            var manager = Instantiate(MultiplayerManagerPrefab, Vector3.zero, Quaternion.identity) as GameObject;
            var tacticsManager = manager.GetComponent<MultiplayerManagerModel>();
            NetworkServer.RegisterHandler(MsgType.AddPlayer, tacticsManager.CreatePlayerOnServer);
            NetworkServer.Spawn(manager);
        }
        ClientScene.AddPlayer(0);
    }

When I run it on a server it works fine, it creates an instance on a client and synchronizes variables from server to client. But when I try to run commands from client it ignores them, throwing this warning:

Trying to send command for object without authority. UnityEngine.Networking.NetworkBehaviour:SendCommandInternal(NetworkWriter, Int32, String)

Note that this Game Manager is spawned before any player is because it must be responsible for spawning players. What am I doing wrong?

2

2 Answers

14
votes

Beside your code fragment, the warning

Trying to send command for object without authority.

Means that: you are sending command from an object whose authority, your (player) don't have.

What Unity docs states: Commands are sent from player objects on the client to player objects on the server. For security, Commands can only be sent from YOUR player object, so you cannot control the objects of other players.

BUT

Starting with Unity release 5.2 it is possible to send commands from non-player objects that have client authority. These objects must have been spawned with NetworkServer.SpawnWithClientAuthority or have authority set with NetworkIdentity.AssignClientAuthority. Commands sent from these object are run on the server instance of the object, not on the associated player object for the client(more).

So what is the solution: Before sending the Command (or executing command function), Assign authority of that object to your Player. Something like this

assignAuthorityObj.GetComponent<NetworkIdentity>().AssignClientAuthority(this.GetComponent<NetworkIdentity>().connectionToClient);

"this" will be represent to your Player object. After making Command call you can remove authority using this code snippet.

 assignAuthorityObj.GetComponent<NetworkIdentity>().RemoveClientAuthority(this.GetComponent<NetworkIdentity>().connectionToClient);

Again, "this" will be represent to your Player object.

Important Note: I usually assign Authority of an object (If I want to use that) using OnTriggerEnter and remove authority on OnTriggerExit. It depend on specific scenario that at what event you want to acquire or remove an object authority.

4
votes

You can't send a command without local authority.

if (!isLocalPlayer) return;

But, what kind of data are you sending with this command ? In general, and as you said :

"it's state should be synchronized on server"

So, IMHO, you have no reason to send a Command from your player. Commands should be for player input. These inputs will trigger actions in your game. Your manager will get informations from these actions (score update, end of the game...) and send data and/or trigger actions on clients, using ClientRpc

This is the server authority model. If you let a local object send commands, a hacker could easily come in and say to your manager

"Hey, it's my turn. Hey, it's my turn again. Hey, my score is 9999999 and I won the game in 1 second.".