Skip to content

Commit d4a9810

Browse files
fix: networkshow client synchronization duplicate players (#3488)
The PR resolves the issue discovered when investigating the [Forum-1651980 issue](https://discussions.unity.com/t/netcode-for-gameobjects-2-4-0-released/1651980/2). The initial client synchronization pre-serialization preparation was not excluding any spawned `NetworkObject` instances that had a pending visibility update for the client being synchronized. This fix adds this check to that process. [MTTB-1372](https://jira.unity3d.com/browse/MTTB-1372) ## Changelog - Fixed: Issue where the initial client synchronization pre-serialization process was not excluding spawned `NetworkObjects` that already had pending visibility for the client being synchronized. ## Testing and Documentation - Includes the `PlayerSpawnObjectVisibilityTests`. - No documentation changes or additions were necessary. <!-- Uncomment and mark items off with a * if this PR deprecates any API: ### Deprecated API - [ ] An `[Obsolete]` attribute was added along with a `(RemovedAfter yyyy-mm-dd)` entry. - [ ] An [api updater] was added. - [ ] Deprecation of the API is explained in the CHANGELOG. - [ ] The users can understand why this API was removed and what they should use instead. --> ## Backport This requires a backport to v1.0 (#3493)
1 parent 2dc3a3c commit d4a9810

File tree

4 files changed

+101
-1
lines changed

4 files changed

+101
-1
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ Additional documentation and release notes are available at [Multiplayer Documen
1212

1313
### Fixed
1414

15-
- Fixed: Issue where there was a potential for a small memory leak in the `ConnectionApprovedMessage`. (#3486)
15+
- Fixed issue where the initial client synchronization pre-serialization process was not excluding spawned `NetworkObject` instances that already had pending visibility for the client being synchronized. (#3488)
16+
- Fixed issue where there was a potential for a small memory leak in the `ConnectionApprovedMessage`. (#3486)
1617

1718
### Changed
1819

com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,12 @@ internal void AddSpawnedNetworkObjects()
324324
var distributedAuthoritySendingToService = m_NetworkManager.DistributedAuthorityMode && TargetClientId == NetworkManager.ServerClientId;
325325
foreach (var sobj in m_NetworkManager.SpawnManager.SpawnedObjectsList)
326326
{
327+
var spawnedObject = sobj;
328+
// Don't synchronize objects that have pending visibility as that will be sent as a CreateObjectMessage towards the end of the current frame
329+
if (TargetClientId != NetworkManager.ServerClientId && m_NetworkManager.SpawnManager.IsObjectVisibilityPending(TargetClientId, ref spawnedObject))
330+
{
331+
continue;
332+
}
327333
if (sobj.Observers.Contains(TargetClientId) || distributedAuthoritySendingToService)
328334
{
329335
m_NetworkObjectsSync.Add(sobj);
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
using System.Collections;
2+
using System.Text.RegularExpressions;
3+
using NUnit.Framework;
4+
using Unity.Netcode.TestHelpers.Runtime;
5+
using UnityEngine;
6+
using UnityEngine.TestTools;
7+
8+
9+
namespace Unity.Netcode.RuntimeTests
10+
{
11+
[TestFixture(HostOrServer.Server)]
12+
[TestFixture(HostOrServer.Host)]
13+
internal class PlayerSpawnObjectVisibilityTests : NetcodeIntegrationTest
14+
{
15+
protected override int NumberOfClients => 0;
16+
17+
public enum PlayerSpawnStages
18+
{
19+
OnNetworkSpawn,
20+
OnNetworkPostSpawn,
21+
}
22+
23+
public PlayerSpawnObjectVisibilityTests(HostOrServer hostOrServer) : base(hostOrServer) { }
24+
25+
public class PlayerVisibilityTestComponent : NetworkBehaviour
26+
{
27+
public PlayerSpawnStages Stage;
28+
29+
private void Awake()
30+
{
31+
var networkObject = GetComponent<NetworkObject>();
32+
// Assure the player prefab will not spawn with observers.
33+
// This assures that when the server/host spawns the connecting client's
34+
// player prefab, the spawn object will initially not be spawnd on the client side.
35+
networkObject.SpawnWithObservers = false;
36+
}
37+
38+
public override void OnNetworkSpawn()
39+
{
40+
ShowToClient(PlayerSpawnStages.OnNetworkSpawn);
41+
base.OnNetworkSpawn();
42+
}
43+
44+
protected override void OnNetworkPostSpawn()
45+
{
46+
ShowToClient(PlayerSpawnStages.OnNetworkPostSpawn);
47+
base.OnNetworkPostSpawn();
48+
}
49+
50+
private void ShowToClient(PlayerSpawnStages currentStage)
51+
{
52+
if (!HasAuthority || Stage != currentStage)
53+
{
54+
return;
55+
}
56+
NetworkObject.NetworkShow(OwnerClientId);
57+
}
58+
}
59+
60+
protected override void OnCreatePlayerPrefab()
61+
{
62+
m_PlayerPrefab.AddComponent<PlayerVisibilityTestComponent>();
63+
base.OnCreatePlayerPrefab();
64+
}
65+
66+
/// <summary>
67+
/// Tests the scenario where under a client-server network topology if a player prefab
68+
/// is spawned by the server with no observers but the player prefab itself has server
69+
/// side script that will network show the spawned object to the owning client.
70+
///
71+
/// Because NetworkShow will defer the CreateObjectMessage until the late update, the
72+
/// server/host needs to filter out including anything within the synchronization
73+
/// message that already has pending visibility.
74+
/// </summary>
75+
/// <param name="spawnStage">Spawn stages to test</param>
76+
/// <returns>IEnumerator</returns>
77+
[UnityTest]
78+
public IEnumerator NetworkShowOnSpawnTest([Values] PlayerSpawnStages spawnStage)
79+
{
80+
m_PlayerPrefab.GetComponent<PlayerVisibilityTestComponent>().Stage = spawnStage;
81+
82+
yield return CreateAndStartNewClient();
83+
84+
yield return new WaitForSeconds(0.25f);
85+
86+
NetcodeLogAssert.LogWasNotReceived(LogType.Warning, new Regex("but it is already in the spawned list!"));
87+
var client = GetNonAuthorityNetworkManager();
88+
Assert.True(client.LocalClient.PlayerObject != null, $"Client-{client.LocalClientId} does not have a player object!");
89+
}
90+
}
91+
}

com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/PlayerSpawnObjectVisibilityTests.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)