Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Resolved How to guarantee the initial NetworkVariable Value on a Client during OnNetworkSpawn()?

Discussion in 'Netcode for GameObjects' started by LaneFox, May 1, 2023.

  1. LaneFox


    Jun 29, 2011
    The documentation is rather confusing on NetworkVariable as it doesn't really explain how to actually initialize them properly. What is the correct pattern to guarantee that a client will spawn the object and read the correct state of a NetworkVariable during OnNetworkSpawn()?

    • Using
      throws warnings, so the docs are probably just outdated there.
    • Using
      throws warnings, so that doesn't seem right.
    • Setting before running
      throws warnings...
    • Setting after running
      throws warnings...
    • Setting them in
      doesn't even set them, since it's used for something else entirely.

    Erm.. When are we supposed to set the initial values of NetworkVariables... ?

    The only way I see that doesn't throw or fail is to just create the object on the network and sit on your hands until everyone is ready, then set the value and wait for it to replicate. If you need to do some init with that value then you need to use OnChanged callbacks to trigger something.

    If that's the case - that seems really bad. This would imply that the initial state is basically guaranteed incorrect, which is some nonsense so I'm assuming I just don't understand it correctly.
    Last edited: May 2, 2023
  2. cerestorm


    Apr 16, 2020
    It was the case if you set network variable values straight after spawning (in the same frame, perhaps longer) then the network object would spawn on the client with those values. For example this would work:
    Code (CSharp):
    1.         public Stack CreateStack(Side side, Vector2Int position)
    2.         {
    3.             Stack stack = SpawnService.Instance().SpawnPrefab<Stack>(SpawnEntityType.Game, false);
    4.             stack.IsControlled = false;
    5.             stack.Side = side;
    6.             stack.Units = 0;
    7.             stack.StackState = StackState.Waiting;
    8.             stack.MovePoints = GameConstants.TIME_ONE_DAY;
    9.             stack.Position = position;
    10.             stack.transform.position = new Vector3(position.x + 0.5f, position.y + 0.5f);
    12.             return stack;
    13.         }
    I'd have to re-test though to be sure it still works that way, but this is from an older project and it's now broken in Netcode 1.4.0 due to some change in CheckObjectVisibility I'm trying to get my head around.

    If you're having no luck I can try something similar in a test project.
  3. LaneFox


    Jun 29, 2011
    It definitely does not seem to work on 1.3.1.

    Code (csharp):
    1.         public void ServerSpawnSomeObject(GameObject prefab, int id, int otherValue)
    2.         {
    3.             GameObject go = Instantiate(prefab);
    4.             StateObject behavior = go.GetComponent<StateObject>();
    5.             behavior.InitializeVars(id, otherValue);
    6.             behavior.NetworkObject.Spawn();
    8. // heres the flow that actually happens...
    9.             // ServerSpawnSomeObject()
    10.             //
    11.             // StateObject.Awake()
    12.             // StateObject.InitializeVars()
    13.             // - NetworkVariable warning about not ready
    14.             // StateObject.OnNetworkSpawn()
    15.             // - NetVars are still default values at this point
    16.             //
    17.             // Client eventually gets the object, and values are default of course.
    18.         }
    No matter where I try to initialize the values it doesn't seem to work. There has to be a way to set the value of a NetworkVariable before shipping off the object. It makes no sense to just ship off network objects assuming that the default state of every NetworkVariable is considered correct, but the API does not offer any actual path to do this. Even setting the variables to dirty doesn't work because they're initialized and overwritten after you do that.
  4. fernando-a-cortez


    Unity Technologies

    Dec 14, 2020

    If you want to read the NetworkVariable's value on OnNetworkSpawn(), then you may modify the NetworkVariable's value on the server before you invoke NetworkObject.Spawn().

    The following warning is inconsequential: NetworkVariable is written to, but doesn't know its NetworkBehaviour yet. Are you modifying a NetworkVariable before the NetworkObject is spawned?
    If you read the NetworkVariable on OnNetworkSpawn() on clients it will be the server's set value pre-spawn.

    However, yes, the recommended pattern is to subscribe to a NetworkVariable's OnValueChanged callback, and define your desired behaviour when that callback is fired on clients.

    I hope that helps!
  5. LaneFox


    Jun 29, 2011
    I am not seeing this behavior. Values set before
    on the server are still
    on Clients during

    Did this change since 1.3.1?
  6. cerestorm


    Apr 16, 2020
    I just tested this in 1.2.0 and 1.4.0 and it worked with both. It's the only way I could get it to spawn on the client with the correct value. I'll have to go over my old project code to see why I thought differently.
  7. RikuTheFuffs-U


    Unity Technologies

    Feb 20, 2020
    Pretty much like this:

    Code (CSharp):
    1. public class ColorManager : NetworkBehaviour
    2.     {
    3.         NetworkVariable<Color32> m_NetworkedColor = new NetworkVariable<Color32>();
    4.         Material m_Material;
    5.         void Awake()
    6.         {
    7.             m_Material = GetComponent<Renderer>().material;
    8.         }
    9.         public override void OnNetworkSpawn()
    10.         {
    11.             base.OnNetworkSpawn();
    12.             if (IsClient)
    13.             {
    14.                     /* in this case, you need to manually load the initial Color to catch up with the state of the network variable.
    15.                      * This is particularly useful when re-connecting or hot-joining a session
    16.                     */
    17.                     OnClientColorChanged(m_Material.color, m_NetworkedColor.Value);
    18.                     m_NetworkedColor.OnValueChanged += OnClientColorChanged;
    20.             }
    21.         }
    22.         public override void OnNetworkDespawn()
    23.         {
    24.             base.OnNetworkDespawn();
    25.             if (IsClient)
    26.             {
    27.                     m_NetworkedColor.OnValueChanged -= OnClientColorChanged;
    28.             }
    29.         }
    31.         void OnClientColorChanged(Color32 previousColor, Color32 newColor)
    32.         {
    33.             m_Material.color = newColor;
    34.         }
    35. }
    LaneFox likes this.
  8. LaneFox


    Jun 29, 2011
    Thanks for the response! This is exactly the pattern we ended up using. We implemented it as needed and bumped up to 1.4. It has been working well so far.

    It would be helpful if the documentation could verify what the system guarantees in terms of data state at critical times, such as when
    occurs. Took me a while to try things until I could make safe assumptions.
    DoN2kcz and RikuTheFuffs like this.