Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

SyncVar hook not called with every change

Discussion in 'Multiplayer' started by monkey0506, Jul 24, 2017.

  1. monkey0506

    monkey0506

    Joined:
    May 9, 2016
    Posts:
    2
    I have a basic finite-state machine for controlling turn-based play, but I am finding that SyncVar is practically worthless to me in syncing the state because the hook isn't called with every change.

    Here's what I'm trying:

    Code (CSharp):
    1. class GameController : NetworkBehaviour // instantiated by the client
    2. {
    3.     enum GameState
    4.     {
    5.         TurnStart,
    6.         PlayerTurn,
    7.         TurnEnd
    8.         // etc.
    9.     };
    10.  
    11.     [SyncVar(hook = "OnGameStateChanged")]
    12.     GameState state;
    13.  
    14.     private void OnGameStateChanged(GameState newState)
    15.     {
    16.         state = newState;
    17.         // do some work
    18.     }
    19.  
    20.     private void Update()
    21.     {
    22.         if (!isServer)
    23.         {
    24.             return;
    25.         }
    26.         switch (state)
    27.         {
    28.             case GameState.TurnStart:
    29.                 // do some work
    30.                 state = GameState.PlayerTurn;
    31.                 break;
    32.             case GameState.PlayerTurn:
    33.                 // updated by player Command elsewhere in script
    34.                 break;
    35.             case GameState.TurnEnd:
    36.                 // do some work
    37.                 state = GameState.TurnStart; // start the next player's turn
    38.             default:
    39.                 break;
    40.         }
    41.     }
    42.  
    43.     // example of player Command located in other script
    44.     [Command]
    45.     private void CmdEndTurn()
    46.     {
    47.         state = GameState.TurnEnd;
    48.         // work is done in Update
    49.     }
    50. }
    I am testing this on a connection between one host (server+client) and one other client. I would expect the OnGameStateChanged method to be invoked on the client every time that state is changed. What I'm actually experiencing on the client is it going straight from GameState.PlayerTurn back to GameState.TurnStart, which is to say that the hook is never being invoked with GameState.TurnEnd. This means that my client is missing vital synchronizations.

    What I've had to do instead is remove the SyncVar altogether and just use an RPC:

    Code (CSharp):
    1. class GameController : NetworkBehaviour // instantiated by the client
    2. {
    3.     enum GameState
    4.     {
    5.         TurnStart,
    6.         PlayerTurn,
    7.         TurnEnd
    8.         // etc.
    9.     }
    10.  
    11.     GameState state;
    12.  
    13.     [ClientRpc]
    14.     private void RpcOnGameStateChanged(GameState newState)
    15.     {
    16.         state = newState;
    17.         // do some work
    18.     }
    19.  
    20.     private void Update()
    21.     {
    22.         if (!isServer)
    23.         {
    24.             return;
    25.         }
    26.         GameState oldState = state;
    27.         switch (state)
    28.         {
    29.             case GameState.TurnStart:
    30.                 // do some work
    31.                 state = GameState.PlayerTurn;
    32.                 break;
    33.             case GameState.PlayerTurn:
    34.                 // updated by player Command elsewhere in script
    35.                 break;
    36.             case GameState.TurnEnd:
    37.                 // do some work
    38.                 state = GameState.TurnStart; // start the next player's turn
    39.             default:
    40.                 break;
    41.         }
    42.         if (state != oldState)
    43.         {
    44.             RpcOnGameStateChanged(state);
    45.         }
    46.     }
    47.  
    48.     // example of player Command located in other script
    49.     [Command]
    50.     private void CmdEndTurn()
    51.     {
    52.         state = GameState.TurnEnd;
    53.         // work is done in Update
    54.     }
    55. }
    This workaround works the way I need it to, but is there any way to make a SyncVar call its hook on the clients with every change?
     
  2. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,168
    Synvars has a max send rate. It says at the bottom of the network behaviour in the editor. Default is 0.1. To follow every state. Use command and rpcs.
     
    Last edited: Jul 24, 2017
    Zullar likes this.
  3. monkey0506

    monkey0506

    Joined:
    May 9, 2016
    Posts:
    2
    I did end up using an RPC. I had tried looking for clarification on this behavior before posting, but came up short in my searches. Thanks, now I know understand better when to and not to use SyncVars.