Search code examples
c#unity-game-enginenetwork-programmingmultiplayerunity3d-mirror

Why does the host player not have authority to send server [Command]? Unity - Mirror


I am making a turn based multiplayer game with Unity 2019.3.2f1 and am using Mirror to connect the players. My goal is to add player objects to the GameMaster script (as you will see in my bottom-most script), so that I can keep track of them and update their progress in the Player script. I have followed DapperDino's youtube tutorial (https://www.youtube.com/watch?v=oBRt9OifJvE&list=PLS6sInD7ThM1aUDj8lZrF4b4lpvejB2uB&index=2) and made a lobby copying everything from the tutorial except the spawner system and I've made adjustments to the names of the scenes, removed the map handler and instead simply have 2 scenes. After 2 players enter lobby, click ready and the host clicks Start, the MainMenu scene changes to GameScene and the actual game starts, but before that the function NetworkServer.ReplacePlayerForConnection(conn, gameplayerInstance.gameObject) is called, before the change of the scene. And I get this error:

RemoveClientAuthority can only be called on the server for spawned objects. UnityEngine.Logger:Log(LogType, Object) Mirror.ILoggerExtensions:LogError(ILogger, Object) (at Assets/Mirror/Runtime/LogFactory.cs:58) Mirror.NetworkIdentity:RemoveClientAuthority() (at Assets/Mirror/Runtime/NetworkIdentity.cs:1230) Mirror.NetworkServer:InternalReplacePlayerForConnection(NetworkConnection, GameObject, Boolean) (at Assets/Mirror/Runtime/NetworkServer.cs:843) Mirror.NetworkServer:ReplacePlayerForConnection(NetworkConnection, GameObject, Boolean) (at Assets/Mirror/Runtime/NetworkServer.cs:680) DapperDino.Mirror.Tutorials.Lobby.NetworkManagerLobby:ServerChangeScene(String) (at Assets/Lobby/Scripts/NetworkManagerLobby.cs:181) DapperDino.Mirror.Tutorials.Lobby.NetworkManagerLobby:StartGame() (at Assets/Lobby/Scripts/NetworkManagerLobby.cs:163)

Why should RemoveClientAuthority() be called?

Then I get this warning: Trying to send command for object without authority. Player.CmdAddPlayer

Here is my code. As you see, on update I check if(!hasAuthority) and if the client doesn't, I would return the Update function but it was never so I commented it to debug. The warning I get is on the host process, which baffles me. Shouldn't the host have authority as it is also the server?

using System.Collections.Generic;
using UnityEngine;
using Mirror;


public class Player : NetworkBehaviour
{
    GameMaster gameMaster=null;
    //public NetworkManager networkManager;

    public Country country;
    public List<Territory> controlledTerritories;
    public List<Army> armies = new List<Army>();
    public int gold;
    bool set = false;
    void Start()
    {
        //networkManager = GameObject.FindWithTag("NetworkManager").GetComponent<NetworkManager>();
    }
    [Client]
    void Update()
    {
        //Debug.Log(networkManager);
        if (!set)
        {
            if (!hasAuthority)
            {
                //return;
            }
            Debug.Log("About to call CmdAddPlayer()");
            //This is the furthest I get. I get the warning after trying to call CmdAddPlayer() and it isn't called
            CmdAddPlayer();
        }
    }
    [Command]
    void CmdAddPlayer()
    {
        //Validate logic here
        Debug.Log("Called CmdAddPlayer()");
        RpcAddPlayer();
    }

    [ClientRpc]
    void RpcAddPlayer()
    {
        Debug.Log("Called RpcPlayer()");
        GameObject _mapTmp = GameObject.FindWithTag("GameController");
        GameMaster _gameMasterTmp = null;
        if (_mapTmp != null)
        {
            _gameMasterTmp = _mapTmp.GetComponent<GameMaster>();
        }
        if (gameMaster == null && _gameMasterTmp != null)
        {
            gameMaster = _gameMasterTmp;
            gameMaster.players.Add(this);
            set = true;
        }
    }
}

Solution

  • I'm following the same tutorial and came across the same error.

    Basically apparently its something to do with Mirror's ServerChangeScene.

    You can solve this pretty easily. In DapperDino NetworkManagerLobby script, go to ServerChangeScene override, and get reid of the destroy so that it goes from this:

    NetworkServer.Destroy(conn.identity.gameObject);     
    NetworkServer.ReplacePlayerForConnection(conn, gameplayerInstance.gameObject); 
     
    

    So that it is changed to:

     NetworkServer.ReplacePlayerForConnection(conn, gameplayerInstance.gameObject); 
     
    

    This should fix it. The room player object seems to be destroyed automatically.

    You can find out more about the error here if that doesn't work: https://github.com/vis2k/Mirror/issues/1593

    To answer the second half of the problem, there's a good explanation here:

    Unity3D. Trying to send command for object without authority

    Basically, you may be trying to send a command from an object that doesn't have authority. So what does that mean?

    You can only send a Command from YOUR player object, TO the player object which represents YOU on the other computer.

    It's not clear from your code, but you could be:

    1. EITHER trying to send a command from the 'player' object that belongs to the other player (for example, if you're looping through all the players in the game).
    2. OR you've written a script called 'player' which hasn't been given authority (i suspect it is this one given you've called your object 'player' and not 'NetworkGamePlayer' as in the tutorial.

    If you're trying to do the first, make sure you check if the player is the local player:

        void CallCommandMove(){
           if(!isLocalPlayer){
            return;
             }
           CmdMove();
        }
    

    Alternatively, if you're trying to do the second, you need to give your own player object authority (as in the accepted answer in this thread: Unity3D. Trying to send command for object without authority

    Finally, the easiest approach whilst learning is to simply put your code on the NetworkGamePlayer script.