Skip to content

Commit 9413833

Browse files
fix
This fixes an issue with the synchronization of NetworkVariables when changing ownership where it was possible to have the previous owner be equal to the current owner when using a distributed authority network topology and the owner handled changing the ownership. This includes an additional check for ditry states of any collections base NetworkVariables. This bug was introduced as a regression bug when resolving the issue that any owner read permission NetworkVariables were fully synchronized with the new owner.
1 parent e5362a4 commit 9413833

File tree

7 files changed

+76
-14
lines changed

7 files changed

+76
-14
lines changed

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1185,14 +1185,23 @@ internal void MarkVariablesDirty(bool dirty)
11851185
}
11861186
}
11871187

1188-
internal void MarkOwnerReadVariablesDirty()
1188+
/// <summary>
1189+
/// For owner read permissions, when changing ownership we need to do a full synchronization
1190+
/// of all NetworkVariables that are owner read permission based since the owner is the only
1191+
/// instance that knows what the most current values are.
1192+
/// </summary>
1193+
internal void MarkOwnerReadDirtyAndCheckOwnerWriteIsDirty()
11891194
{
11901195
for (int j = 0; j < NetworkVariableFields.Count; j++)
11911196
{
11921197
if (NetworkVariableFields[j].ReadPerm == NetworkVariableReadPermission.Owner)
11931198
{
11941199
NetworkVariableFields[j].SetDirty(true);
11951200
}
1201+
if (NetworkVariableFields[j].WritePerm == NetworkVariableWritePermission.Owner)
1202+
{
1203+
NetworkVariableFields[j].OnCheckIsDirtyState();
1204+
}
11961205
}
11971206
}
11981207

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,13 @@ internal void NetworkBehaviourUpdate(bool forceSend = false)
108108
foreach (var dirtyobj in m_DirtyNetworkObjects)
109109
{
110110
dirtyobj.PostNetworkVariableWrite(forceSend);
111-
// Once done processing, we set the previous owner id to the current owner id
112-
dirtyobj.PreviousOwnerId = dirtyobj.OwnerClientId;
111+
// If we are force sending, then we are changing ownership and making sure that we
112+
// do a full synchronization of NetworkVariables.
113+
if (forceSend)
114+
{
115+
// Once done processing, we set the previous owner id to the current owner id
116+
dirtyobj.PreviousOwnerId = dirtyobj.OwnerClientId;
117+
}
113118
}
114119
m_DirtyNetworkObjects.Clear();
115120
}

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

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2567,11 +2567,36 @@ internal void MarkVariablesDirty(bool dirty)
25672567
}
25682568
}
25692569

2570-
internal void MarkOwnerReadVariablesDirty()
2570+
/// <summary>
2571+
/// Used when changing ownership, this will mark any owner read permission base NetworkVariables as dirty
2572+
/// and will check if any owner write permission NetworkVariables are dirty (primarily for collections) so
2573+
/// the new owner will get a full state update prior to changing ownership.
2574+
/// </summary>
2575+
/// <remarks>
2576+
/// We have to pass in the original owner and previous owner to "reset" back to the current state of this
2577+
/// NetworkObject in order to preserve the same ownership change flow. By the time this is invoked, the
2578+
/// new and previous owner ids have already been set.
2579+
/// </remarks>
2580+
/// <param name="originalOwnerId">the owner prior to beginning the change in ownership change.</param>
2581+
/// <param name="originalPreviousOwnerId">the previous owner prior to beginning the change in ownership change.</param>
2582+
internal void SynchronizeOwnerNetworkVariables(ulong originalOwnerId, ulong originalPreviousOwnerId)
25712583
{
2584+
var currentOwnerId = OwnerClientId;
2585+
OwnerClientId = originalOwnerId;
2586+
PreviousOwnerId = originalPreviousOwnerId;
25722587
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
25732588
{
2574-
ChildNetworkBehaviours[i].MarkOwnerReadVariablesDirty();
2589+
ChildNetworkBehaviours[i].MarkOwnerReadDirtyAndCheckOwnerWriteIsDirty();
2590+
}
2591+
// Force send a state update for all owner read NetworkVariables
2592+
// and any currently dirty owner write NetworkVariables.
2593+
NetworkManager.BehaviourUpdater.NetworkBehaviourUpdate(true);
2594+
// Now set the new owner identifier back to its original new owner identifier
2595+
OwnerClientId = currentOwnerId;
2596+
// The previous owner should now be the originalOwnerId at this point.
2597+
if (PreviousOwnerId != originalOwnerId)
2598+
{
2599+
Debug.LogError($"[NetworkVaraible][Full Synchronization] Expected {nameof(PreviousOwnerId)} ({PreviousOwnerId}) to be the original owner id ({originalOwnerId})!");
25752600
}
25762601
}
25772602

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -381,11 +381,8 @@ private void HandleOwnershipChange(ref NetworkContext context)
381381

382382
if (originalOwner == networkManager.LocalClientId && !networkManager.DistributedAuthorityMode)
383383
{
384-
// Mark any owner read variables as dirty
385-
networkObject.MarkOwnerReadVariablesDirty();
386-
// Immediately queue any pending deltas and order the message before the
387-
// change in ownership message.
388-
networkManager.BehaviourUpdater.NetworkBehaviourUpdate(true);
384+
// Fully synchronize NetworkVariables with either read or write ownership permissions.
385+
networkObject.SynchronizeOwnerNetworkVariables(originalOwner, networkObject.PreviousOwnerId);
389386
}
390387

391388
// Always invoke ownership change notifications

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,13 @@ public bool CheckDirtyState(bool forceCheck = false)
166166
return isDirty;
167167
}
168168

169+
/// <inheritdoc/>
170+
internal override void OnCheckIsDirtyState()
171+
{
172+
CheckDirtyState();
173+
base.OnCheckIsDirtyState();
174+
}
175+
169176
internal ref T RefValue()
170177
{
171178
return ref m_InternalValue;

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,15 @@ internal ulong OwnerClientId()
373373
return m_NetworkBehaviour.NetworkObject.OwnerClientId;
374374
}
375375

376+
/// <summary>
377+
/// Primarily to check for collections dirty states when doing
378+
/// a fully owner read/write NetworkVariable update.
379+
/// </summary>
380+
internal virtual void OnCheckIsDirtyState()
381+
{
382+
383+
}
384+
376385
/// <summary>
377386
/// Writes the dirty changes, that is, the changes since the variable was last dirty, to the writer
378387
/// </summary>

com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,15 @@ internal void RemoveOwnership(NetworkObject networkObject)
432432

433433
internal void ChangeOwnership(NetworkObject networkObject, ulong clientId, bool isAuthorized, bool isRequestApproval = false)
434434
{
435+
if (clientId == networkObject.OwnerClientId)
436+
{
437+
if (NetworkManager.LogLevel <= LogLevel.Developer)
438+
{
439+
Debug.LogWarning($"[{nameof(NetworkSpawnManager)}][{nameof(ChangeOwnership)}] Attempting to change ownership to Client-{clientId} when the owner is already {networkObject.OwnerClientId}! (Ignoring)");
440+
}
441+
return;
442+
}
443+
435444
// For client-server:
436445
// If ownership changes faster than the latency between the client-server and there are NetworkVariables being updated during ownership changes,
437446
// then notify the user they could potentially lose state updates if developer logging is enabled.
@@ -530,6 +539,10 @@ internal void ChangeOwnership(NetworkObject networkObject, ulong clientId, bool
530539
return;
531540
}
532541

542+
// Save the previous owner to work around a bit of a nasty issue with assuring NetworkVariables are serialized when ownership changes
543+
var originalPreviousOwnerId = networkObject.PreviousOwnerId;
544+
var originalOwner = networkObject.OwnerClientId;
545+
533546
// Used to distinguish whether a new owner should receive any currently dirty NetworkVariable updates
534547
networkObject.PreviousOwnerId = networkObject.OwnerClientId;
535548

@@ -548,10 +561,7 @@ internal void ChangeOwnership(NetworkObject networkObject, ulong clientId, bool
548561
if (networkObject.PreviousOwnerId == NetworkManager.LocalClientId)
549562
{
550563
// Mark any owner read variables as dirty
551-
networkObject.MarkOwnerReadVariablesDirty();
552-
// Immediately queue any pending deltas and order the message before the
553-
// change in ownership message.
554-
NetworkManager.BehaviourUpdater.NetworkBehaviourUpdate(true);
564+
networkObject.SynchronizeOwnerNetworkVariables(originalOwner, originalPreviousOwnerId);
555565
}
556566

557567
var size = 0;

0 commit comments

Comments
 (0)