Skip to content

Commit 819a603

Browse files
update & test
This resolves an issue discovered after peer review where using just `HasAuthority` as part of the check within `NetworkList.OnSpawned` while using a distributed authority topology and spawning with ownership (i.e. Client-A spawns with ownership where Client-B is the owner) would result in any changes made on the authority side during spawn would not be synchronized. The solution was to add `NetworkObject.IsSpawnAuthority` (internal) that is only set on the spawn authority side (i.e. the side that generates the `CreateObjectMessage`).
1 parent 412639a commit 819a603

File tree

4 files changed

+56
-19
lines changed

4 files changed

+56
-19
lines changed

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,16 @@ public uint PrefabIdHash
7676
/// </remarks>
7777
public List<NetworkTransform> NetworkTransforms { get; private set; }
7878

79+
/// <summary>
80+
/// Set to true if this instance is the original/first instance created and spawned which
81+
/// means the <see cref="CreateObjectMessage"/> was generated from this instance and sent
82+
/// to all other clients.
83+
///
84+
/// Client-Server: This will be true for all instances on the server or host.
85+
/// Distributed Authority: This will be true on the client that created the first instance
86+
/// and spawned it (even if spawning with ownership being assigned to a different client).
87+
/// </summary>
88+
internal bool IsSpawnAuthority;
7989

8090
#if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D
8191
/// <summary>
@@ -2008,6 +2018,7 @@ internal void ResetOnDespawn()
20082018
{
20092019
// Always clear out the observers list when despawned
20102020
Observers.Clear();
2021+
IsSpawnAuthority = false;
20112022
IsSpawned = false;
20122023
DeferredDespawnTick = 0;
20132024
m_LatestParent = null;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ internal override void OnSpawned()
7171
// the dirty related properties and last sent time to prevent duplicate entries from
7272
// being sent (i.e. CreateObjectMessage will contain the changes so we don't need to
7373
// send a proceeding NetworkVariableDeltaMessage).
74-
if (IsDirty() && CanSend() && m_NetworkBehaviour.HasAuthority)
74+
if (IsDirty() && CanSend() && m_NetworkObject.IsSpawnAuthority)
7575
{
7676
UpdateLastSentTime();
7777
ResetDirty();

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,9 @@ internal void AuthorityLocalSpawn([NotNull] NetworkObject networkObject, ulong n
10541054
Debug.LogError("Spawning NetworkObjects with nested NetworkObjects is only supported for scene objects. Child NetworkObjects will not be spawned over the network!");
10551055
}
10561056
}
1057+
1058+
networkObject.IsSpawnAuthority = true;
1059+
10571060
// Invoke NetworkBehaviour.OnPreSpawn methods
10581061
networkObject.NetworkManagerOwner = NetworkManager;
10591062
networkObject.InvokeBehaviourNetworkPreSpawn();

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

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -293,9 +293,10 @@ private int[] Shuffle(List<int> list)
293293
}
294294

295295
private List<NetworkObject> m_SpawnedObjects = new List<NetworkObject>();
296-
internal const int SpawnCount = 10;
296+
internal const int ValueCount = 10;
297297
internal bool IsOwnerWriteTest;
298298
internal NetworkManager LateJoinedClient;
299+
internal static List<int> OwnerWriteExpectedValues = new List<int>();
299300

300301
protected override void OnNewClientCreated(NetworkManager networkManager)
301302
{
@@ -314,13 +315,13 @@ protected override void OnNewClientCreated(NetworkManager networkManager)
314315
public IEnumerator OwnerWriteTests()
315316
{
316317
IsOwnerWriteTest = true;
317-
var authorityBetworkManager = GetAuthorityNetworkManager();
318+
var authorityNetworkManager = GetAuthorityNetworkManager();
318319
m_SpawnedObjects.Clear();
319-
m_ExpectedValues.Clear();
320+
OwnerWriteExpectedValues.Clear();
320321
// Set our initial expected values as 0 - 9
321-
for (int i = 0; i < SpawnCount; i++)
322+
for (int i = 0; i < ValueCount; i++)
322323
{
323-
m_ExpectedValues.Add(i);
324+
OwnerWriteExpectedValues.Add(i);
324325
}
325326

326327
// Each spawned instance will be owned by each NetworkManager instance in order
@@ -354,16 +355,40 @@ public IEnumerator OwnerWriteTests()
354355

355356
// Now have all of the clients update their list values to randomly assigned values
356357
// in order to verify changes to owner write NetworkLists are synchronized properly.
357-
m_ExpectedValues.Clear();
358-
for (int i = 0; i < SpawnCount; i++)
358+
OwnerWriteExpectedValues.Clear();
359+
for (int i = 0; i < ValueCount; i++)
359360
{
360-
m_ExpectedValues.Add(Random.Range(10, 100));
361+
OwnerWriteExpectedValues.Add(Random.Range(10, 100));
361362
}
362363
UpdateOwnerWriteValues();
363364

364365
// Verify all spawned object instances have the expected owner write NetworkList values
365366
yield return WaitForConditionOrTimeOut(OnVerifyOwnerWriteData);
366367
AssertOnTimeout("Detected invalid count or value on one of the spawned instances!");
368+
369+
// Verifies that spawning with ownership in distributed authority mode work properly.
370+
// Where:
371+
// Client-A spawns with ownership assigned to Client-B
372+
// Client-B applies values at spawn.
373+
// All clients then should be updated with those new values applied.
374+
if (m_DistributedAuthority)
375+
{
376+
var prefabNetworkObject = m_ListObjectPrefab.GetComponent<NetworkObject>();
377+
foreach (var networkManager in m_NetworkManagers)
378+
{
379+
var instance = Object.Instantiate(m_ListObjectPrefab).GetComponent<NetworkObject>();
380+
SpawnInstanceWithOwnership(instance, authorityNetworkManager, networkManager.LocalClientId);
381+
m_SpawnedObjects.Add(instance);
382+
}
383+
384+
// Verify all NetworkManager instances spawned the objects
385+
yield return WaitForSpawnedOnAllOrTimeOut(m_SpawnedObjects);
386+
AssertOnTimeout("Not all instances were spawned on all clients!");
387+
388+
// Verify all spawned object instances have the expected owner write NetworkList values
389+
yield return WaitForConditionOrTimeOut(OnVerifyOwnerWriteData);
390+
AssertOnTimeout("Detected invalid count or value on one of the spawned instances!");
391+
}
367392
}
368393

369394
private void UpdateOwnerWriteValues()
@@ -373,9 +398,9 @@ private void UpdateOwnerWriteValues()
373398
var owningNetworkManager = m_NetworkManagers.Where((c) => c.LocalClientId == spawnedObject.OwnerClientId).First();
374399
var networkObjectId = spawnedObject.NetworkObjectId;
375400
var listComponent = owningNetworkManager.SpawnManager.SpawnedObjects[networkObjectId].GetComponent<NetworkListTest>();
376-
for (int i = 0; i < SpawnCount; i++)
401+
for (int i = 0; i < ValueCount; i++)
377402
{
378-
listComponent.OwnerWriteList[i] = m_ExpectedValues[i];
403+
listComponent.OwnerWriteList[i] = OwnerWriteExpectedValues[i];
379404
}
380405
}
381406
}
@@ -400,16 +425,16 @@ private bool OnVerifyOwnerWriteData(StringBuilder errorLog)
400425
return false;
401426
}
402427

403-
if (listComponent.OwnerWriteList.Count != SpawnCount)
428+
if (listComponent.OwnerWriteList.Count != ValueCount)
404429
{
405-
errorLog.Append($"[Client-{networkManager.LocalClientId}] List component has the incorrect number of items. Expected: {SpawnCount}, Have: {listComponent.TheList.Count}");
430+
errorLog.Append($"[Client-{networkManager.LocalClientId}] List component has the incorrect number of items. Expected: {ValueCount}, Have: {listComponent.TheList.Count}");
406431
return false;
407432
}
408433

409-
for (int i = 0; i < SpawnCount; i++)
434+
for (int i = 0; i < ValueCount; i++)
410435
{
411436
var actual = listComponent.OwnerWriteList[i];
412-
var expected = m_ExpectedValues[i];
437+
var expected = OwnerWriteExpectedValues[i];
413438
if (expected != actual)
414439
{
415440
errorLog.Append($"[Client-{networkManager.LocalClientId}] Incorrect value at index {i}, expected: {expected}, actual: {actual}");
@@ -450,9 +475,9 @@ public override void OnNetworkSpawn()
450475
{
451476
if (IsOwner)
452477
{
453-
for (int i = 0; i < NetworkListTests.SpawnCount; i++)
478+
for (int i = 0; i < NetworkListTests.ValueCount; i++)
454479
{
455-
OwnerWriteList.Add(i);
480+
OwnerWriteList.Add(NetworkListTests.OwnerWriteExpectedValues[i]);
456481
}
457482
}
458483
base.OnNetworkSpawn();
@@ -474,8 +499,6 @@ internal class NetworkListTestPredicate : ConditionalPredicateBase
474499

475500
private readonly NetworkListTest m_NonAuthorityInstance;
476501

477-
private string m_TestStageFailedMessage;
478-
479502
/// <summary>
480503
/// Determines if the condition has been reached for the current NetworkListTestState
481504
/// </summary>

0 commit comments

Comments
 (0)