-
Notifications
You must be signed in to change notification settings - Fork 456
Description
Description
Updating collection in a NetworkVariable and then calling CheckDirtyState() doesn't sync latest changes if it's already dirty.
Reproduce Steps
On host (didn't test actual clients)
- Update collection
- Call CheckDirtyState()
- Update collection again
- CheckDirtyState()
Actual Outcome
On client, NetworkVariable .OnValueChanged is called, but collection has only the first change
Expected Outcome
On client, NetworkVariable .OnValueChanged is called and collection has the latest changes
Environment
- OS: Windows 11
- Unity Version: 6000.0.25f1
- Netcode Version: 2.2.0
Additional Context
I had a stats system, which was represented by a simple Dictionary<int, int>.
When I equip an item, it modifies the stats and calls the CheckDirtyState - client gets notified with new value and everything's good.
But if I immediately add another item, which modifies stats some more, and call CheckDirtyState - new value does not get sent unless I do the forceCheck.
Documentation states that CheckDirtyState exits early if it's already dirty. While doing so, it doesn't take the latest changes. I guess the reason for this early exit can be interpreted in two ways:
- If something is dirty, there's no need to check if it's dirty again. Then, at the end of the frame or whenever serialization runs - actual changes would be calculated and synced with the client.
- A way to optimize calling this multiple times
If intended reasoning is 1 - then there's a bug, if 2 - forceCheck should default to true, and "false" becomes logically unnecessary. I could elaborate on that.
Here's an example to test this behaviour:
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
public class TestBehaviour: NetworkBehaviour
{
public NetworkVariable<Dictionary<int,int>> _stats = new NetworkVariable<Dictionary<int, int>>(new Dictionary<int, int>());
private void Awake()
{
}
public override void OnNetworkSpawn()
{
base.OnNetworkSpawn();
if (IsClient)
{
_stats.OnValueChanged += OnValuesChanged;
}
if (IsServer)
{
StartCoroutine(TriggerChanges());
}
}
private IEnumerator TriggerChanges()
{
yield return new WaitForSeconds(1f);
_stats.Value[0] = 1;
_stats.CheckDirtyState();
_stats.Value[0] = 2;
_stats.CheckDirtyState(); // If I add true to this, everything works as expected
}
private void OnValuesChanged(Dictionary<int, int> previousValue, Dictionary<int, int> newValue)
{
Debug.Log($"Values changed on client: {(_stats.Value.ContainsKey(0) ? _stats.Value[0].ToString() : "N/A")}");
}
}
In this example, only this is printed:
Values changed on client: 1