Skip to content

Commit b8eed68

Browse files
fix: NetworkObject.SpawnWithObservers was not being honored for late joining clients [MTT-6934] (#2623)
* fix This resolves the issue where NetworkObjects with SpawnWithObservers set to false was not being honored for late joining clients. This also resolves an issue when scene management is disabled the NetworkObjects' observers were not being updated for newly connected clients. * test This validates the fix for the late joining client observer issue.
1 parent 6ae793a commit b8eed68

File tree

4 files changed

+55
-7
lines changed

4 files changed

+55
-7
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@ Additional documentation and release notes are available at [Multiplayer Documen
1010

1111
### Added
1212

13+
1314
### Fixed
14-
- Fixed a failing UTP test that was failing when you install Unity Transport package 2.0.0 or newer.
15+
16+
- Fixed issue where `NetworkObject.SpawnWithObservers` was not being honored for late joining clients. (#2623)
1517

1618
## Changed
1719

20+
1821
## [1.5.1] - 2023-06-07
1922

2023
### Added

com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,8 @@ internal void HandleConnectionApproval(ulong ownerClientId, NetworkManager.Conne
628628
};
629629
if (!NetworkManager.NetworkConfig.EnableSceneManagement)
630630
{
631+
// Update the observed spawned NetworkObjects for the newly connected player when scene management is disabled
632+
NetworkManager.SpawnManager.UpdateObservedNetworkObjects(ownerClientId);
631633
if (NetworkManager.SpawnManager.SpawnedObjectsList.Count != 0)
632634
{
633635
message.SpawnedObjectsList = NetworkManager.SpawnManager.SpawnedObjectsList;
@@ -651,12 +653,12 @@ internal void HandleConnectionApproval(ulong ownerClientId, NetworkManager.Conne
651653
SendMessage(ref message, NetworkDelivery.ReliableFragmentedSequenced, ownerClientId);
652654
message.MessageVersions.Dispose();
653655

654-
// If scene management is enabled, then let NetworkSceneManager handle the initial scene and NetworkObject synchronization
656+
// If scene management is disabled, then we are done and notify the local host-server the client is connected
655657
if (!NetworkManager.NetworkConfig.EnableSceneManagement)
656658
{
657659
InvokeOnClientConnectedCallback(ownerClientId);
658660
}
659-
else
661+
else // Otherwise, let NetworkSceneManager handle the initial scene and NetworkObject synchronization
660662
{
661663
NetworkManager.SceneManager.SynchronizeNetworkObjects(ownerClientId);
662664
}

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -952,27 +952,35 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec
952952
}
953953

954954
/// <summary>
955-
/// Updates all spawned <see cref="NetworkObject.Observers"/> for the specified client
955+
/// Updates all spawned <see cref="NetworkObject.Observers"/> for the specified newly connected client
956956
/// Note: if the clientId is the server then it is observable to all spawned <see cref="NetworkObject"/>'s
957957
/// </summary>
958+
/// <remarks>
959+
/// This method is to only to be used for newly connected clients in order to update the observers list for
960+
/// each NetworkObject instance.
961+
/// </remarks>
958962
internal void UpdateObservedNetworkObjects(ulong clientId)
959963
{
960964
foreach (var sobj in SpawnedObjectsList)
961965
{
966+
// If the NetworkObject has no visibility check then prepare to add this client as an observer
962967
if (sobj.CheckObjectVisibility == null)
963968
{
964-
if (!sobj.Observers.Contains(clientId))
969+
// If the client is not part of the observers and spawn with observers is enabled on this instance or the clientId is the server
970+
if (!sobj.Observers.Contains(clientId) && (sobj.SpawnWithObservers || clientId == NetworkManager.ServerClientId))
965971
{
966972
sobj.Observers.Add(clientId);
967973
}
968974
}
969975
else
970976
{
977+
// CheckObject visibility overrides SpawnWithObservers under this condition
971978
if (sobj.CheckObjectVisibility(clientId))
972979
{
973980
sobj.Observers.Add(clientId);
974981
}
975-
else if (sobj.Observers.Contains(clientId))
982+
else // Otherwise, if the observers contains the clientId (shouldn't happen) then remove it since CheckObjectVisibility returned false
983+
if (sobj.Observers.Contains(clientId))
976984
{
977985
sobj.Observers.Remove(clientId);
978986
}

com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectOnSpawnTests.cs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,26 @@ private bool CheckClientsSideObserverTestObj()
6161
return true;
6262
}
6363

64+
/// <summary>
65+
/// Assures the <see cref="ObserverSpawnTests"/> late joining client has all
66+
/// NetworkPrefabs required to connect.
67+
/// </summary>
68+
protected override void OnNewClientCreated(NetworkManager networkManager)
69+
{
70+
foreach (var networkPrefab in m_ServerNetworkManager.NetworkConfig.Prefabs.Prefabs)
71+
{
72+
if (!networkManager.NetworkConfig.Prefabs.Contains(networkPrefab.Prefab))
73+
{
74+
networkManager.NetworkConfig.Prefabs.Add(networkPrefab);
75+
}
76+
}
77+
base.OnNewClientCreated(networkManager);
78+
}
6479

65-
80+
/// <summary>
81+
/// This test validates <see cref="NetworkObject.SpawnWithObservers"/> property
82+
/// </summary>
83+
/// <param name="observerTestTypes">whether to spawn with or without observers</param>
6684
[UnityTest]
6785
public IEnumerator ObserverSpawnTests([Values] ObserverTestTypes observerTestTypes)
6886
{
@@ -92,6 +110,23 @@ public IEnumerator ObserverSpawnTests([Values] ObserverTestTypes observerTestTyp
92110
m_ObserverTestType = ObserverTestTypes.WithObservers;
93111
yield return WaitForConditionOrTimeOut(CheckClientsSideObserverTestObj);
94112
AssertOnTimeout($"{k_WithObserversError} {k_ObserverTestObjName} object!");
113+
114+
// Validate that a late joining client does not see the NetworkObject when it spawns
115+
yield return CreateAndStartNewClient();
116+
117+
m_ObserverTestType = ObserverTestTypes.WithoutObservers;
118+
// Just give a little time to make sure nothing spawned
119+
yield return s_DefaultWaitForTick;
120+
yield return WaitForConditionOrTimeOut(CheckClientsSideObserverTestObj);
121+
AssertOnTimeout($"{(withoutObservers ? k_WithoutObserversError : k_WithObserversError)} {k_ObserverTestObjName} object!");
122+
123+
// Now validate that we can make the NetworkObject visible to the newly joined client
124+
m_ObserverTestNetworkObject.NetworkShow(m_ClientNetworkManagers[NumberOfClients].LocalClientId);
125+
126+
// Validate the NetworkObject is visible to all connected clients (including the recently joined client)
127+
m_ObserverTestType = ObserverTestTypes.WithObservers;
128+
yield return WaitForConditionOrTimeOut(CheckClientsSideObserverTestObj);
129+
AssertOnTimeout($"{k_WithObserversError} {k_ObserverTestObjName} object!");
95130
}
96131
}
97132
/// <summary>

0 commit comments

Comments
 (0)