Search Unity

Third Party How to spawn object and give him an authority? [Mirror]

Discussion in 'Multiplayer' started by Lekret, Mar 10, 2021.

  1. Lekret

    Lekret

    Joined:
    Sep 10, 2020
    Posts:
    354
    I have two events EnteredMenu and GameStarted and want them being called on all clients for all players. I can't make Commands and ClientRPCs directly in the network manager, because it isn't NetworkBehaviour so I made NetworkGameStates class. Then I need to give NetworkGameStates an authority to execute commands, but it simply doesn't work, Command is called, but Rpc isn't. I'm pretty sure I'm doing something wrong, but can't figure what's specifically. I tried a lot of things and approaches, but this doesn't work. Btw I don't get any errors.

    Code (CSharp):
    1. public class ExtRoomManager : NetworkRoomManager
    2. {
    3.     //First variant I tried
    4.     public override void OnRoomServerConnect(NetworkConnection conn)
    5.     {
    6.     //tried one at a time and both at once
    7.         NetworkServer.Spawn(_gameStates.gameObject, conn);
    8.         _gameStates.netIdentity.AssignClientAuthority(conn);
    9.     }
    10.     //End of first
    11.  
    12.     //Second variant I tried
    13.     public override void OnRoomServerConnect(NetworkConnection conn)
    14.     {
    15.         StartCoroutine(InitGameStates());
    16.     }
    17.  
    18.     private IEnumerator InitGameStates()
    19.     {
    20.         yield return new WaitUntil(() => FindObjectsOfType<ExtRoomPlayer>().Any(p => p.isLocalPlayer));
    21.         var localRoomPlayer = FindObjectsOfType<ExtRoomPlayer>().FirstOrDefault(p => p.isLocalPlayer);
    22.  
    23.         //Also tried one at a time and both at once
    24.         NetworkServer.Spawn(_gameStates.gameObject, localRoomPlayer.connectionToClient);
    25.         _gameStates.netIdentity.AssignClientAuthority(localRoomPlayer.connectionToClient);
    26.     }
    27.     //End of second
    28.  
    29.     public void StartGameScene()
    30.     {
    31.         ServerChangeScene(GameplayScene);
    32.         print("Calling Cmd!");
    33.         _gameStates.CmdOnGameStarted();
    34.     }
    35. //...
    36. }
    Code (CSharp):
    1.  
    2. public class NetworkGameStates : NetworkBehaviour
    3. {
    4.     public event UnityAction GameStarted;
    5.     public event UnityAction EnteredMenu;
    6.  
    7.     private void Awake()
    8.     {
    9.         DontDestroyOnLoad(gameObject);
    10.     }
    11.  
    12.     [Command]
    13.     public void CmdOnGameStarted()
    14.     {
    15.         print("Executing command!");
    16.         RpcOnGameStarted();
    17.     }
    18.  
    19.     [ClientRpc]
    20.     private void RpcOnGameStarted()
    21.     {
    22.         print("Remote Procedure Call!");
    23.         GameStarted?.Invoke();
    24.     }
    25. //...
    26. }
     
    Last edited: Mar 10, 2021
  2. Lekret

    Lekret

    Joined:
    Sep 10, 2020
    Posts:
    354
    After hours of frustration I found the problem.

    This code will work as expected. First command+rpc are called, then scene is changed.
    Code (CSharp):
    1. private void OnStartPressed()
    2. {
    3.     NetworkGameStates.Instance.CmdOnGameStarted();
    4.     _manager.ServerChangeScene(_manager.GameplayScene);
    5. }
    Swapped lines of code. RpcMethod called from this "CmdOnGameStarted" won't be executed
    Code (CSharp):
    1. private void OnStartPressed()
    2. {
    3.     _manager.ServerChangeScene(_manager.GameplayScene);
    4.     NetworkGameStates.Instance.CmdOnGameStarted();
    5. }
    Also I tried
    Code (CSharp):
    1. private void OnStartPressed()
    2.     {
    3.         _manager.ServerChangeScene(_manager.GameplayScene);
    4.         StartCoroutine(InvokeAfterTime(NetworkGameStates.Instance.CmdOnGameStarted));
    5.     }
    6.  
    7.     private IEnumerator InvokeAfterTime(UnityAction command)
    8.     {
    9.         //if time = 0.1f then Rpc won't work, if time = 0.2f then Rpc will work.
    10.         yield return new WaitForSeconds(time);
    11.         command.Invoke();
    12.     }
    Seems like clients can't receive Rpc from server during scene loading. You need wait for loading, then wait additional frame, and only then call your command+rpc. Completely unexpected.
    Code (CSharp):
    1. private IEnumerator InvokeAfterTime(UnityAction command)
    2. {
    3.     bool sceneWasLoaded = false;
    4.     UnityAction<Scene, LoadSceneMode> invokeAction = (scene, mode) => sceneWasLoaded = true;
    5.     SceneManager.sceneLoaded += invokeAction;
    6.     yield return new WaitUntil(() => sceneWasLoaded);
    7.     //In my case total wait time was 0.1163006 seconds.
    8.     SceneManager.sceneLoaded -= invokeAction;
    9.     yield return null;
    10.     command.Invoke();
    11. }
     
    Last edited: Mar 10, 2021