unity-game-engineuser-interfacenetwork-programmingunity3d-unetunity3d-mirror

Mirror/UNET c# - No authority on object even when using Command & ClientRpc - What am I missing?


Long story: I have made a multiplayer chat using Mirror/Unet that works. I have made it so after x number of seconds, the gameobject that displays the chat (“textContainer”) goes inactive. I would like it so that when client 1 presses “Submit”, all the client’s gameobjects for textContainer goes active again. But it only works on the client that presses Submit, and not on ALL clients. I have set up the functions in [Client] [Command] and [ClientRpc] but am still getting an error about no authority on object.
I think it is because client 1 does not have authority to request client 2 to activate their UI panel. I thought the Command and ClientRpc would have fixed this issue?

Short story: So, for simplicity, say when client 1 presses the Input for “Submit”, I would like all client’s textContainer GameObjects to go active.
I am hoping someone might be able to point me in the right direction. Cheers.

This script would be attached to the player prefab.

public GameObject textContainer;

[Client]
void Update()
{
    if (Input.GetAxisRaw("Submit") == 1)
    {
        CmdActivateChatClientRPC();
        textContainer.SetActive(true);

    }
}

[Command]
private void CmdActivateChatClientRPC()
{
    ActivateChatClientRPC();
}

[ClientRpc]
private void ActivateChatClientRPC()
{
    textContainer.SetActive(true);
        
}


Solution

  • You are not checking whether you have the authority over this object.

    void Update()
    {
        // Does your device have the authority over this object?
        // only then you can invoke a Cmd
        if(!hasAuthority) return;
    
        if (Input.GetAxisRaw("Submit") == 1)
        {
            CmdActivateChatClientRPC();
            textContainer.SetActive(true);
        }
    }
    
    [Command]
    private void CmdActivateChatClientRPC()
    {
        ActivateChatClientRPC();
    }
    
    // This will be executed on ALL clients!
    // BUT only for this one instance of this class   
    [ClientRpc]
    private void ActivateChatClientRPC()
    {
        textContainer.SetActive(true);      
    }
    

    If it is rather about only you trying to invoke a command on an object that belongs to the server it gets tricky ;)

    You will have to relay the command through your local player object and then on the server forward the call to the according object e.g. referencing the actual target via it's Network identity.


    To me it sounds a bit like you are having each player prefab has this class and you want to invoke the RPC on all of them.

    This is something that can only be done by the server like e.g.

    void Update()
    {
        // Does your device have the authority over this object?
        // only then you can invoke a Cmd
        if(!hasAuthority) return;
    
        if (Input.GetAxisRaw("Submit") == 1)
        {
            CmdActivateChatClientRPC();
            textContainer.SetActive(true);
        }
    }
    
    [Command]
    private void CmdActivateChatClientRPC()
    {
        foreach (var player in NetworkManager.singleton.client.connection.playerControllers)
        {
            if (player.IsValid)
            {
                player.gameObject.GetComponent<YourScriptName>().ActivateChatClientRPC();
            }
        }
    }
    

    Seems that .client.connection.playerControllers was removed.

    I guess in that case you would need a custom NetworkManager and keep track of them yourself via OnServerConnect and OnServerDisconnect somewhat like

    public CustomNetworkManager : NetworkManager
    {
        public static readonly HashSet<NetworkConnection> players = new HashSet<NetworkConnection>();
    
        public override void OnServerConnect(NetworkConnection conn)
        {
            base.OnServerConnect(conn);
    
            players.Add(conn);
        }
    
        public override void OnServerDisconnect(NetworkConnection conn)
        {
            base.OnServerDisconnect(conn);
    
            players.Remove(conn);
        }
    }
    

    and then instead access

    foreach (var player in CustomNetworkManager.players)
    {
        player.identity.GetComponent<YourScriptName>().ActivateChatClientRPC();
    }