Skip to content

Commit e3b837a

Browse files
authored
fix: resetting the NetworkVariable dirty status at end of frame, … (#2344)
* fix: resetting the NetworkVariable dirty status at end of frame, even if a given NetworkVariable is not sent to any client * delaying NetworkShow until end of frame * handling NetworkHide of objects pending their NetworkShow
1 parent 890a1e1 commit e3b837a

File tree

6 files changed

+270
-23
lines changed

6 files changed

+270
-23
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ Additional documentation and release notes are available at [Multiplayer Documen
1212

1313
### Changed
1414

15+
- `NetworkShow()` of `NetworkObject`s are delayed until the end of the frame to ensure consistency of delta-driven variables like `NetworkList`.
16+
- Dirty `NetworkObject` are reset at end-of-frame and not at serialization time.
17+
- `NetworkHide()` of an object that was just `NetworkShow()`n produces a warning, as remote clients will _not_ get a spawn/despawn pair.
1518
- The default listen address of `UnityTransport` is now 0.0.0.0. (#2307)
1619
- Renamed the NetworkTransform.SetState parameter `shouldGhostsInterpolate` to `teleportDisabled` for better clarity of what that parameter does. (#2228)
1720

com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviourUpdater.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,23 @@ internal void NetworkBehaviourUpdate(NetworkManager networkManager)
7272
}
7373
}
7474
}
75+
76+
foreach (var dirtyObj in m_DirtyNetworkObjects)
77+
{
78+
for (int k = 0; k < dirtyObj.ChildNetworkBehaviours.Count; k++)
79+
{
80+
var behaviour = dirtyObj.ChildNetworkBehaviours[k];
81+
for (int i = 0; i < behaviour.NetworkVariableFields.Count; i++)
82+
{
83+
if (behaviour.NetworkVariableFields[i].IsDirty() &&
84+
!behaviour.NetworkVariableIndexesToResetSet.Contains(i))
85+
{
86+
behaviour.NetworkVariableIndexesToResetSet.Add(i);
87+
behaviour.NetworkVariableIndexesToReset.Add(i);
88+
}
89+
}
90+
}
91+
}
7592
// Now, reset all the no-longer-dirty variables
7693
foreach (var dirtyobj in m_DirtyNetworkObjects)
7794
{

com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ internal void MarkNetworkObjectDirty(NetworkObject networkObject)
6767

6868
internal Dictionary<ulong, ConnectionApprovalResponse> ClientsToApprove = new Dictionary<ulong, ConnectionApprovalResponse>();
6969

70+
// Stores the objects that need to be shown at end-of-frame
71+
internal Dictionary<ulong, List<NetworkObject>> ObjectsToShowToClient = new Dictionary<ulong, List<NetworkObject>>();
72+
7073
/// <summary>
7174
/// The <see cref="NetworkPrefabHandler"/> instance created after starting the <see cref="NetworkManager"/>
7275
/// </summary>
@@ -1663,6 +1666,17 @@ private void OnNetworkManagerTick()
16631666
// Do NetworkVariable updates
16641667
BehaviourUpdater.NetworkBehaviourUpdate(this);
16651668

1669+
// Handle NetworkObjects to show
1670+
foreach (var client in ObjectsToShowToClient)
1671+
{
1672+
ulong clientId = client.Key;
1673+
foreach (var networkObject in client.Value)
1674+
{
1675+
SpawnManager.SendSpawnCallForObject(clientId, networkObject);
1676+
}
1677+
}
1678+
ObjectsToShowToClient.Clear();
1679+
16661680
int timeSyncFrequencyTicks = (int)(k_TimeSyncFrequency * NetworkConfig.TickRate);
16671681
if (IsServer && NetworkTickSystem.ServerTime.Tick % timeSyncFrequencyTicks == 0)
16681682
{
@@ -2339,5 +2353,37 @@ internal void ApprovedPlayerSpawn(ulong clientId, uint playerPrefabHash)
23392353
NetworkMetrics.TrackObjectSpawnSent(clientPair.Key, ConnectedClients[clientId].PlayerObject, size);
23402354
}
23412355
}
2356+
2357+
internal void MarkObjectForShowingTo(NetworkObject networkObject, ulong clientId)
2358+
{
2359+
if (!ObjectsToShowToClient.ContainsKey(clientId))
2360+
{
2361+
ObjectsToShowToClient.Add(clientId, new List<NetworkObject>());
2362+
}
2363+
ObjectsToShowToClient[clientId].Add(networkObject);
2364+
}
2365+
2366+
// returns whether any matching objects would have become visible and were returned to hidden state
2367+
internal bool RemoveObjectFromShowingTo(NetworkObject networkObject, ulong clientId)
2368+
{
2369+
var ret = false;
2370+
if (!ObjectsToShowToClient.ContainsKey(clientId))
2371+
{
2372+
return false;
2373+
}
2374+
2375+
// probably overkill, but deals with multiple entries
2376+
while (ObjectsToShowToClient[clientId].Contains(networkObject))
2377+
{
2378+
Debug.LogWarning(
2379+
"Object was shown and hidden from the same client in the same Network frame. As a result, the client will _not_ receive a NetworkSpawn");
2380+
ObjectsToShowToClient[clientId].Remove(networkObject);
2381+
ret = true;
2382+
}
2383+
2384+
networkObject.Observers.Remove(clientId);
2385+
2386+
return ret;
2387+
}
23422388
}
23432389
}

com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -265,9 +265,8 @@ public void NetworkShow(ulong clientId)
265265
throw new VisibilityChangeException("The object is already visible");
266266
}
267267

268+
NetworkManager.MarkObjectForShowingTo(this, clientId);
268269
Observers.Add(clientId);
269-
270-
NetworkManager.SpawnManager.SendSpawnCallForObject(clientId, this);
271270
}
272271

273272

@@ -351,26 +350,28 @@ public void NetworkHide(ulong clientId)
351350
throw new NotServerException("Only server can change visibility");
352351
}
353352

354-
if (!Observers.Contains(clientId))
355-
{
356-
throw new VisibilityChangeException("The object is already hidden");
357-
}
358-
359353
if (clientId == NetworkManager.ServerClientId)
360354
{
361355
throw new VisibilityChangeException("Cannot hide an object from the server");
362356
}
363357

364-
Observers.Remove(clientId);
365-
366-
var message = new DestroyObjectMessage
358+
if (!NetworkManager.RemoveObjectFromShowingTo(this, clientId))
367359
{
368-
NetworkObjectId = NetworkObjectId,
369-
DestroyGameObject = !IsSceneObject.Value
370-
};
371-
// Send destroy call
372-
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, clientId);
373-
NetworkManager.NetworkMetrics.TrackObjectDestroySent(clientId, this, size);
360+
if (!Observers.Contains(clientId))
361+
{
362+
throw new VisibilityChangeException("The object is already hidden");
363+
}
364+
Observers.Remove(clientId);
365+
366+
var message = new DestroyObjectMessage
367+
{
368+
NetworkObjectId = NetworkObjectId,
369+
DestroyGameObject = !IsSceneObject.Value
370+
};
371+
// Send destroy call
372+
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, clientId);
373+
NetworkManager.NetworkMetrics.TrackObjectDestroySent(clientId, this, size);
374+
}
374375
}
375376

376377
/// <summary>

com.unity.netcode.gameobjects/Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,16 @@ public void Serialize(FastBufferWriter writer, int targetVersion)
6464
shouldWrite = false;
6565
}
6666

67+
// The object containing the behaviour we're about to process is about to be shown to this client
68+
// As a result, the client will get the fully serialized NetworkVariable and would be confused by
69+
// an extraneous delta
70+
if (NetworkBehaviour.NetworkManager.ObjectsToShowToClient.ContainsKey(TargetClientId) &&
71+
NetworkBehaviour.NetworkManager.ObjectsToShowToClient[TargetClientId]
72+
.Contains(NetworkBehaviour.NetworkObject))
73+
{
74+
shouldWrite = false;
75+
}
76+
6777
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
6878
{
6979
if (!shouldWrite)
@@ -96,12 +106,6 @@ public void Serialize(FastBufferWriter writer, int targetVersion)
96106
networkVariable.WriteDelta(writer);
97107
}
98108

99-
if (!NetworkBehaviour.NetworkVariableIndexesToResetSet.Contains(i))
100-
{
101-
NetworkBehaviour.NetworkVariableIndexesToResetSet.Add(i);
102-
NetworkBehaviour.NetworkVariableIndexesToReset.Add(i);
103-
}
104-
105109
NetworkBehaviour.NetworkManager.NetworkMetrics.TrackNetworkVariableDeltaSent(
106110
TargetClientId,
107111
NetworkBehaviour.NetworkObject,

0 commit comments

Comments
 (0)