Skip to content

Commit 2bf919c

Browse files
authored
Merge branch 'develop-2.0.0' into fix/3559/using-unreliable-deltas-should-disable-certain-features
2 parents 1423080 + 454d04f commit 2bf919c

File tree

8 files changed

+241
-55
lines changed

8 files changed

+241
-55
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
2525
### Fixed
2626

2727
- Fixed `NetworkTransform` issue where a user could enable UseUnreliableDeltas while SwitchTransformSpaceWhenParented was also enabled (and vice versa). (#3875)
28+
- Fixed issue where `NetworkVariable` was not properly synchronizing to changes made by the spawn and write authority during `OnNetworkSpawn` and `OnNetworkPostSpawn`. (#3878)
2829
- Fixed issue where `NetworkManager` was not cleaning itself up if an exception was thrown while starting. (#3864)
2930
- Prevented a `NullReferenceException` in `UnityTransport` when using a custom `INetworkStreamDriverConstructor` that doesn't use all the default pipelines and the multiplayer tools package is installed. (#3853)
3031

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,7 @@ internal void NetworkPostSpawn()
838838
// all spawn related methods have been invoked.
839839
for (int i = 0; i < NetworkVariableFields.Count; i++)
840840
{
841-
NetworkVariableFields[i].OnSpawned();
841+
NetworkVariableFields[i].InternalOnSpawned();
842842
}
843843
}
844844

@@ -891,7 +891,7 @@ internal void InternalOnNetworkPreDespawn()
891891
// all spawn related methods have been invoked.
892892
for (int i = 0; i < NetworkVariableFields.Count; i++)
893893
{
894-
NetworkVariableFields[i].OnPreDespawn();
894+
NetworkVariableFields[i].InternalOnPreDespawn();
895895
}
896896
}
897897

com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -58,28 +58,6 @@ public NetworkList(IEnumerable<T> values = default,
5858
Dispose();
5959
}
6060

61-
internal override void OnSpawned()
62-
{
63-
// If the NetworkList is:
64-
// - Dirty
65-
// - State updates can be sent:
66-
// -- The instance has write permissions.
67-
// -- The last sent time plus the max send time period is less than the current time.
68-
// - User script has modified the list during spawn.
69-
// - This instance is on the spawn authority side.
70-
// When the NetworkObject is finished spawning (on the same frame), go ahead and reset
71-
// the dirty related properties and last sent time to prevent duplicate entries from
72-
// being sent (i.e. CreateObjectMessage will contain the changes so we don't need to
73-
// send a proceeding NetworkVariableDeltaMessage).
74-
if (IsDirty() && CanSend() && m_NetworkObject.IsSpawnAuthority)
75-
{
76-
UpdateLastSentTime();
77-
ResetDirty();
78-
SetDirty(false);
79-
}
80-
base.OnSpawned();
81-
}
82-
8361
/// <inheritdoc cref="NetworkVariable{T}.ResetDirty"/>
8462
public override void ResetDirty()
8563
{

com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -140,27 +140,37 @@ public void Initialize(NetworkBehaviour networkBehaviour)
140140
}
141141
}
142142

143-
/// TODO-API: After further vetting and alignment on these, we might make them part of the public API.
144-
/// Could actually be like an interface that gets automatically registered for these kinds of notifications
145-
/// without having to be a NetworkBehaviour.
146-
#region OnSpawn and OnPreDespawn (ETC)
147-
148143
/// <summary>
149144
/// Invoked after the associated <see cref="NetworkBehaviour.OnNetworkPostSpawn"/> has been invoked.
150145
/// </summary>
151-
internal virtual void OnSpawned()
146+
internal void InternalOnSpawned()
152147
{
153-
148+
// If the NetworkVariableBase derived class is:
149+
// - On the spawn authority side.
150+
// - Dirty.
151+
// - State updates can be sent:
152+
// -- The instance has write permissions.
153+
// -- The last sent time plus the max send time period is less than the current time.
154+
// - User script has modified the list during spawn.
155+
// When the NetworkObject is finished spawning (on the same frame), go ahead and reset
156+
// the dirty related properties and last sent time to prevent duplicate updates from
157+
// being sent (i.e. CreateObjectMessage will contain the changes so we don't need to
158+
// send a proceeding NetworkVariableDeltaMessage).
159+
if (m_NetworkObject.IsSpawnAuthority && IsDirty() && CanWrite() && CanSend())
160+
{
161+
UpdateLastSentTime();
162+
ResetDirty();
163+
SetDirty(false);
164+
}
154165
}
155166

156167
/// <summary>
157168
/// Invoked after the associated <see cref="NetworkBehaviour.OnNetworkPreDespawn"/> has been invoked.
158169
/// </summary>
159-
internal virtual void OnPreDespawn()
170+
internal void InternalOnPreDespawn()
160171
{
161172

162173
}
163-
#endregion
164174

165175
/// <summary>
166176
/// Deinitialize is invoked when a NetworkObject is despawned.

com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkVariableCollectionsTests.cs

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -831,7 +831,7 @@ public IEnumerator TestDictionaryCollections()
831831
{
832832
// Server-side add same key and SerializableObject prior to being added to the owner side
833833
compDictionaryServer.ListCollectionOwner.Value.Add(newEntry.Item1, newEntry.Item2);
834-
// Checking if dirty on server side should revert back to origina known current dictionary state
834+
// Checking if dirty on server side should revert back to original known current dictionary state
835835
compDictionaryServer.ListCollectionOwner.IsDirty();
836836
yield return WaitForConditionOrTimeOut(() => compDictionaryServer.CompareTrackedChanges(ListTestHelperBase.Targets.Owner));
837837
AssertOnTimeout($"Server add to owner write collection property failed to restore on {className} {compDictionaryServer.name}! {compDictionaryServer.GetLog()}");
@@ -840,7 +840,6 @@ public IEnumerator TestDictionaryCollections()
840840
// Server-side add a completely new key and SerializableObject to to owner write permission property
841841
compDictionaryServer.ListCollectionOwner.Value.Add(GetNextKey(), SerializableObject.GetRandomObject());
842842
// Both should be overridden by the owner-side update
843-
844843
}
845844
VerboseDebug($"[{compDictionary.name}][Owner] Adding Key: {newEntry.Item1}");
846845
// Add key and SerializableObject to owner side
@@ -852,15 +851,17 @@ public IEnumerator TestDictionaryCollections()
852851
//////////////////////////////////
853852
// Server Add SerializableObject Entry
854853
newEntry = (GetNextKey(), SerializableObject.GetRandomObject());
854+
855855
// Only test restore on non-host clients (otherwise a host is both server and client/owner)
856856
if (!client.IsServer)
857857
{
858858
// Client-side add same key and SerializableObject to server write permission property
859859
compDictionary.ListCollectionServer.Value.Add(newEntry.Item1, newEntry.Item2);
860-
// Checking if dirty on client side should revert back to origina known current dictionary state
860+
// Checking if dirty on client side should revert back to original known current dictionary state
861861
compDictionary.ListCollectionServer.IsDirty();
862862
yield return WaitForConditionOrTimeOut(() => compDictionary.CompareTrackedChanges(ListTestHelperBase.Targets.Server));
863863
AssertOnTimeout($"Client-{client.LocalClientId} add to server write collection property failed to restore on {className} {compDictionary.name}! {compDictionary.GetLog()}");
864+
864865
// Client-side add the same key and SerializableObject to server write permission property (would throw key exists exception too if previous failed)
865866
compDictionary.ListCollectionServer.Value.Add(newEntry.Item1, newEntry.Item2);
866867
// Client-side add a completely new key and SerializableObject to to server write permission property
@@ -892,7 +893,6 @@ public IEnumerator TestDictionaryCollections()
892893

893894
yield return WaitForConditionOrTimeOut(() => compDictionaryServer.ValidateInstances());
894895
AssertOnTimeout($"[Server] Not all instances of client-{compDictionaryServer.OwnerClientId}'s {className} {compDictionaryServer.name} component match! {compDictionaryServer.GetLog()}");
895-
896896
ValidateClientsFlat(client);
897897
////////////////////////////////////
898898
// Owner Change SerializableObject Entry
@@ -915,7 +915,7 @@ public IEnumerator TestDictionaryCollections()
915915
{
916916
// Server-side update same key value prior to being updated to the owner side
917917
compDictionaryServer.ListCollectionOwner.Value[valueInt] = randomObject;
918-
// Checking if dirty on server side should revert back to origina known current dictionary state
918+
// Checking if dirty on server side should revert back to original known current dictionary state
919919
compDictionaryServer.ListCollectionOwner.IsDirty();
920920
yield return WaitForConditionOrTimeOut(() => compDictionaryServer.CompareTrackedChanges(ListTestHelperBase.Targets.Owner));
921921
AssertOnTimeout($"Server update collection entry value to local owner write collection property failed to restore on {className} {compDictionaryServer.name}! {compDictionaryServer.GetLog()}");
@@ -956,7 +956,7 @@ public IEnumerator TestDictionaryCollections()
956956
{
957957
// Owner-side update same key value prior to being updated to the server side
958958
compDictionary.ListCollectionServer.Value[valueInt] = randomObject;
959-
// Checking if dirty on owner side should revert back to origina known current dictionary state
959+
// Checking if dirty on owner side should revert back to original known current dictionary state
960960
compDictionary.ListCollectionServer.IsDirty();
961961
yield return WaitForConditionOrTimeOut(() => compDictionary.CompareTrackedChanges(ListTestHelperBase.Targets.Server));
962962
AssertOnTimeout($"Client-{client.LocalClientId} update collection entry value to local server write collection property failed to restore on {className} {compDictionary.name}! {compDictionary.GetLog()}");
@@ -1841,10 +1841,10 @@ private bool ChangesMatch(ulong clientId, Dictionary<DeltaTypes, Dictionary<int,
18411841
var deltaTypes = Enum.GetValues(typeof(DeltaTypes)).OfType<DeltaTypes>().ToList();
18421842
foreach (var deltaType in deltaTypes)
18431843
{
1844-
LogMessage($"Comparing {deltaType}:");
1844+
LogMessage($"[Comparing {deltaType}] Local: {local[deltaType].Count} | Other: {other[deltaType].Count}");
18451845
if (local[deltaType].Count != other[deltaType].Count)
18461846
{
1847-
LogMessage($"{deltaType}s count did not match!");
1847+
LogMessage($"[Client-{clientId}] Local {deltaType}s count of {local[deltaType].Count} did not match the other's count of {other[deltaType].Count}!");
18481848
return false;
18491849
}
18501850
if (!CompareDictionaries(clientId, local[deltaType], other[deltaType]))
@@ -1970,6 +1970,18 @@ public void TrackChanges(Targets target, Dictionary<int, SerializableObject> pre
19701970
contextTable[DeltaTypes.Removed] = whatWasRemoved;
19711971
contextTable[DeltaTypes.Changed] = whatChanged;
19721972
contextTable[DeltaTypes.UnChanged] = whatRemainedTheSame;
1973+
1974+
// Log all incoming changes when debug mode is enabled
1975+
if (!IsOwner && IsDebugMode)
1976+
{
1977+
LogMessage($"[{NetworkManager.name}][TrackChanges-> Client-{OwnerClientId}] Collection was updated!");
1978+
LogMessage($"Added: {whatWasAdded.Count} ");
1979+
LogMessage($"Removed: {whatWasRemoved.Count} ");
1980+
LogMessage($"Changed: {whatChanged.Count} ");
1981+
LogMessage($"UnChanged: {whatRemainedTheSame.Count} ");
1982+
UnityEngine.Debug.Log($"{GetLog()}");
1983+
LogStart();
1984+
}
19731985
}
19741986

19751987
public void OnServerListValuesChanged(Dictionary<int, SerializableObject> previous, Dictionary<int, SerializableObject> current)
@@ -2016,30 +2028,43 @@ public void ResetTrackedChanges()
20162028

20172029
protected override void OnNetworkPostSpawn()
20182030
{
2019-
TrackRelativeInstances();
20202031

2032+
TrackRelativeInstances();
20212033
ListCollectionServer.OnValueChanged += OnServerListValuesChanged;
20222034
ListCollectionOwner.OnValueChanged += OnOwnerListValuesChanged;
20232035

20242036
if (!IsDebugMode)
20252037
{
20262038
InitValues();
20272039
}
2040+
2041+
base.OnNetworkPostSpawn();
20282042
}
20292043

20302044
public void InitValues()
20312045
{
20322046
if (IsServer)
20332047
{
20342048
ListCollectionServer.Value = OnSetServerValues();
2035-
ListCollectionOwner.CheckDirtyState();
2049+
ListCollectionServer.CheckDirtyState();
20362050
}
20372051

20382052
if (IsOwner)
20392053
{
20402054
ListCollectionOwner.Value = OnSetOwnerValues();
20412055
ListCollectionOwner.CheckDirtyState();
20422056
}
2057+
2058+
// When running a host, the changes being tracked will not match because clients will be synchronized with changes
2059+
// already applied. This fixing this issue by injecting "added" server targeted changes during initialization on
2060+
// the connected clients' side.
2061+
if (!IsServer)
2062+
{
2063+
if (ListCollectionServer.Value.Count > 0 && NetworkVariableChanges[Targets.Server][DeltaTypes.Added].Count == 0)
2064+
{
2065+
TrackChanges(Targets.Server, new Dictionary<int, SerializableObject>(), ListCollectionServer.Value);
2066+
}
2067+
}
20432068
}
20442069

20452070
public override void OnNetworkDespawn()

0 commit comments

Comments
 (0)