Skip to content

NetworkVariable modified in OnNetworkSpawn (Server) sends default value in initial CreateObjectMessage #3876

@LaPluses

Description

@LaPluses

The following content were rewritten by AI to improve the readability.

Root Cause Analysis was done with Gemini CLI(model: gemini-3-pro), i personally manually check on it, though am not familar with the codebase, so please treat these findings as a starting point for further validation.

Description

When a NetworkVariable is modified within the OnNetworkSpawn method on the Server immediately after spawning a NetworkObject, the connecting Client receives the default/initial value (e.g., 0) in the initial CreateObjectMessage, rather than the modified value (e.g., 2).

Although the correct value is eventually synchronized via a subsequent NetworkVariableDeltaMessage, this causes a race condition where the Client sees the incorrect value during its own OnNetworkSpawn and initial frame execution.

Reproduce Steps

  1. Create a NetworkBehaviour with a NetworkVariable<float> (default value 0).
  2. Override OnNetworkSpawn. Inside the method, check if (IsServer) and set the variable to a new value (e.g., 2).
  3. Connect a Client to the Server/Host.
  4. On the Server, Spawn an instance of this object using NetworkObject.Spawn().
  5. Observe the value of the NetworkVariable on the Client side inside the Client's OnNetworkSpawn method or immediately after spawn.
  6. See error: The Client logs/uses the default value (0) instead of the server-set value (2).

Actual Outcome

The Client receives the default value (0) in the initial spawn packet. The correct value (2) arrives late in a subsequent delta update. This breaks initialization logic dependent on the initial state of NetworkVariables.

Expected Outcome

The Client should receive the modified value (2) contained within the initial CreateObjectMessage so that OnNetworkSpawn on the client has the correct state immediately.

Screenshots

If applicable, add screenshots to help explain your problem.

Environment

  • OS: Windows
  • Unity Version: Unity 6
  • Netcode Version: 2.9
  • Netcode Commit:
  • Netcode Topology: Client-Server

Additional Context

Minimal Reproduction Code:

using Unity.Netcode;
using UnityEngine;

public class TestNetworkBehaviour : NetworkBehaviour
{
    public NetworkVariable<float> TestVal = new();

    public override void OnNetworkSpawn()
    {
        base.OnNetworkSpawn();
        if (IsServer)
        {
            // Server sets the value immediately upon spawn
            TestVal.Value = 2;
        }
        else
        {
            // Client reads the value immediately
            Debug.Log($"[Client] OnNetworkSpawn TestVal = {TestVal.Value}");
            // EXPECTED: 2
            // ACTUAL: 0
        }
    }
}

Root Cause Analysis:

We have investigated the internal execution flow and identified the issue in the serialization logic relative to OnNetworkSpawn timing:

  1. Initialization: When Spawn() is called, NetworkVariable.Initialize captures the default value (0) as m_PreviousValue and sets m_HasPreviousValue = true.
  2. User Code: OnNetworkSpawn is called on the Server. The user sets TestVal.Value = 2. This marks the variable as Dirty, but m_PreviousValue remains 0.
  3. Serialization: When creating the CreateObjectMessage, NetworkVariable<T>.WriteFieldSynchronization is called.
  4. The Flaw: The logic checks:
if (base.IsDirty() && m_HasPreviousValue)
{
    // Because it is Dirty, it assumes a delta update follows.
    // It writes m_PreviousValue (which is 0) to the spawn message.
    NetworkVariableSerialization<T>.Write(writer, ref m_PreviousValue);
}
else
{
    base.WriteFieldSynchronization(writer);
}
  1. Result: The spawn message sends 0. The "new" value 2 is queued for a delta update. The client initializes with 0 before the delta arrives.

Metadata

Metadata

Labels

priority:highThis issue has high priority and we are focusing to resolve itregressionThe issue is a regression (something that was working in previous version)stat:InvestigatingIssue is currently being investigatedstat:awaiting-responseAwaiting response from author. This label should be added manually.stat:importStatus - Issue is going to be saved internallytype:bugBug Report

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions