Skip to content

Commit 2eee998

Browse files
fix: NetworkObject.DeferDespawn not respecting DestroyGameObject parameter (#3219)
* fix: NetworkObject.DeferDespawn not respecting DestroyGameObject parameter * test-fix Checking to see if the failure was due to the network prefab being spawned when the host-server starts. * test-fix Assure all NetworkManagers are destroyed at the end of TearDown. * Revert "test-fix" This reverts commit 16659e8. * test-fix-fix Fixing the bug in my previous fix. * Ensure DeferDespawn tick is always reset * revert DestroyObject message change * Update CHANGELOG.md Adding PR number to changelog entry. --------- Co-authored-by: NoelStephensUnity <[email protected]>
1 parent b665499 commit 2eee998

File tree

6 files changed

+81
-10
lines changed

6 files changed

+81
-10
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
1212

1313
### Fixed
1414

15+
- Fixed `NetworkObject.DeferDespawn` to respect the `DestroyGameObject` parameter. (#3219)
1516
- Changed the `NetworkTimeSystem.Sync` method to use half RTT to calculate the desired local time offset as opposed to the full RTT. (#3212)
1617
- Fixed issue where a spawned `NetworkObject` that was registered with a prefab handler and owned by a client would invoke destroy more than once on the host-server side if the client disconnected while the `NetworkObject` was still spawned. (#3200)
1718
- Fixed issue where `NetworkVariableBase` derived classes were not being re-initialized if the associated `NetworkObject` instance was not destroyed and re-spawned. (#3181)

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ public void Handle(ref NetworkContext context)
150150
{
151151
networkObject.DeferredDespawnTick = DeferredDespawnTick;
152152
var hasCallback = networkObject.OnDeferredDespawnComplete != null;
153-
networkManager.SpawnManager.DeferDespawnNetworkObject(NetworkObjectId, DeferredDespawnTick, hasCallback);
153+
networkManager.SpawnManager.DeferDespawnNetworkObject(NetworkObjectId, DeferredDespawnTick, hasCallback, DestroyGameObject);
154154
return;
155155
}
156156
}

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

+7-3
Original file line numberDiff line numberDiff line change
@@ -1450,7 +1450,7 @@ internal void ServerSpawnSceneObjectsOnStartSweep()
14501450
}
14511451

14521452
// Since we are spawing in-scene placed NetworkObjects for already loaded scenes,
1453-
// we need to add any in-scene placed NetworkObject to our tracking table
1453+
// we need to add any in-scene placed NetworkObject to our tracking table
14541454
var clearFirst = true;
14551455
foreach (var sceneLoaded in NetworkManager.SceneManager.ScenesLoaded)
14561456
{
@@ -1588,6 +1588,7 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec
15881588
{
15891589
NetworkObjectId = networkObject.NetworkObjectId,
15901590
DeferredDespawnTick = networkObject.DeferredDespawnTick,
1591+
// DANGO-TODO: Reconfigure Client-side object destruction on despawn
15911592
DestroyGameObject = networkObject.IsSceneObject != false ? destroyGameObject : true,
15921593
IsTargetedDestroy = false,
15931594
IsDistributedAuthority = distributedAuthority,
@@ -1601,6 +1602,7 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec
16011602
}
16021603

16031604
networkObject.IsSpawned = false;
1605+
networkObject.DeferredDespawnTick = 0;
16041606

16051607
if (SpawnedObjects.Remove(networkObject.NetworkObjectId))
16061608
{
@@ -1920,6 +1922,7 @@ internal struct DeferredDespawnObject
19201922
{
19211923
public int TickToDespawn;
19221924
public bool HasDeferredDespawnCheck;
1925+
public bool DestroyGameObject;
19231926
public ulong NetworkObjectId;
19241927
}
19251928

@@ -1931,12 +1934,13 @@ internal struct DeferredDespawnObject
19311934
/// <param name="networkObjectId">associated NetworkObject</param>
19321935
/// <param name="tickToDespawn">when to despawn the NetworkObject</param>
19331936
/// <param name="hasDeferredDespawnCheck">if true, user script is to be invoked to determine when to despawn</param>
1934-
internal void DeferDespawnNetworkObject(ulong networkObjectId, int tickToDespawn, bool hasDeferredDespawnCheck)
1937+
internal void DeferDespawnNetworkObject(ulong networkObjectId, int tickToDespawn, bool hasDeferredDespawnCheck, bool destroyGameObject)
19351938
{
19361939
var deferredDespawnObject = new DeferredDespawnObject()
19371940
{
19381941
TickToDespawn = tickToDespawn,
19391942
HasDeferredDespawnCheck = hasDeferredDespawnCheck,
1943+
DestroyGameObject = destroyGameObject,
19401944
NetworkObjectId = networkObjectId,
19411945
};
19421946
DeferredDespawnObjects.Add(deferredDespawnObject);
@@ -2001,7 +2005,7 @@ internal void DeferredDespawnUpdate(NetworkTime serverTime)
20012005
}
20022006
var networkObject = SpawnedObjects[deferredObjectEntry.NetworkObjectId];
20032007
// Local instance despawns the instance
2004-
OnDespawnObject(networkObject, true);
2008+
OnDespawnObject(networkObject, deferredObjectEntry.DestroyGameObject);
20052009
DeferredDespawnObjects.Remove(deferredObjectEntry);
20062010
}
20072011
}

com.unity.netcode.gameobjects/TestHelpers/Runtime/NetcodeIntegrationTest.cs

+19
Original file line numberDiff line numberDiff line change
@@ -1156,6 +1156,9 @@ protected void ShutdownAndCleanUp()
11561156
// reset the m_ServerWaitForTick for the next test to initialize
11571157
s_DefaultWaitForTick = new WaitForSecondsRealtime(1.0f / k_DefaultTickRate);
11581158
VerboseDebug($"Exiting {nameof(ShutdownAndCleanUp)}");
1159+
1160+
// Assure any remaining NetworkManagers are destroyed
1161+
DestroyNetworkManagers();
11591162
}
11601163

11611164
protected IEnumerator CoroutineShutdownAndCleanUp()
@@ -1195,6 +1198,9 @@ protected IEnumerator CoroutineShutdownAndCleanUp()
11951198
// reset the m_ServerWaitForTick for the next test to initialize
11961199
s_DefaultWaitForTick = new WaitForSecondsRealtime(1.0f / k_DefaultTickRate);
11971200
VerboseDebug($"Exiting {nameof(ShutdownAndCleanUp)}");
1201+
1202+
// Assure any remaining NetworkManagers are destroyed
1203+
DestroyNetworkManagers();
11981204
}
11991205

12001206
/// <summary>
@@ -1244,6 +1250,19 @@ public IEnumerator TearDown()
12441250
VerboseDebug($"Exiting {nameof(TearDown)}");
12451251
LogWaitForMessages();
12461252
NetcodeLogAssert.Dispose();
1253+
1254+
}
1255+
1256+
/// <summary>
1257+
/// Destroys any remaining NetworkManager instances
1258+
/// </summary>
1259+
private void DestroyNetworkManagers()
1260+
{
1261+
var networkManagers = Object.FindObjectsByType<NetworkManager>(FindObjectsSortMode.None);
1262+
foreach (var networkManager in networkManagers)
1263+
{
1264+
Object.DestroyImmediate(networkManager.gameObject);
1265+
}
12471266
}
12481267

12491268
/// <summary>

com.unity.netcode.gameobjects/Tests/Runtime/DistributedAuthority/DeferredDespawningTests.cs

+31-4
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,25 @@
99

1010
namespace Unity.Netcode.RuntimeTests
1111
{
12+
[TestFixture(SetDestroyGameObject.DestroyGameObject)]
13+
[TestFixture(SetDestroyGameObject.DontDestroyGameObject)]
1214
internal class DeferredDespawningTests : IntegrationTestWithApproximation
1315
{
1416
private const int k_DaisyChainedCount = 5;
1517
protected override int NumberOfClients => 2;
1618
private List<GameObject> m_DaisyChainedDespawnObjects = new List<GameObject>();
1719
private List<ulong> m_HasReachedEnd = new List<ulong>();
1820

19-
public DeferredDespawningTests() : base(HostOrServer.DAHost)
21+
public enum SetDestroyGameObject
2022
{
23+
DestroyGameObject,
24+
DontDestroyGameObject,
25+
}
26+
private bool m_DestroyGameObject;
27+
28+
public DeferredDespawningTests(SetDestroyGameObject destroyGameObject) : base(HostOrServer.DAHost)
29+
{
30+
m_DestroyGameObject = destroyGameObject == SetDestroyGameObject.DestroyGameObject;
2131
}
2232

2333
protected override void OnServerAndClientsCreated()
@@ -45,12 +55,28 @@ protected override void OnServerAndClientsCreated()
4555
[UnityTest]
4656
public IEnumerator DeferredDespawning()
4757
{
58+
// Setup for test
59+
DeferredDespawnDaisyChained.DestroyGameObject = m_DestroyGameObject;
4860
DeferredDespawnDaisyChained.EnableVerbose = m_EnableVerboseDebug;
61+
DeferredDespawnDaisyChained.ClientRelativeInstances = new Dictionary<ulong, Dictionary<ulong, DeferredDespawnDaisyChained>>();
62+
63+
// Spawn the initial object
4964
var rootInstance = SpawnObject(m_DaisyChainedDespawnObjects[0], m_ServerNetworkManager);
5065
DeferredDespawnDaisyChained.ReachedLastChainInstance = ReachedLastChainObject;
66+
67+
// Wait for the chain of objects to spawn and despawn
5168
var timeoutHelper = new TimeoutHelper(300);
5269
yield return WaitForConditionOrTimeOut(HaveAllClientsReachedEndOfChain, timeoutHelper);
5370
AssertOnTimeout($"Timed out waiting for all children to reach the end of their chained deferred despawns!", timeoutHelper);
71+
72+
if (m_DestroyGameObject)
73+
{
74+
Assert.IsTrue(rootInstance == null); // Assert.IsNull doesn't work here
75+
}
76+
else
77+
{
78+
Assert.IsTrue(rootInstance != null); // Assert.IsNotNull doesn't work here
79+
}
5480
}
5581

5682
private bool HaveAllClientsReachedEndOfChain()
@@ -88,9 +114,10 @@ private void ReachedLastChainObject(ulong clientId)
88114
internal class DeferredDespawnDaisyChained : NetworkBehaviour
89115
{
90116
public static bool EnableVerbose;
117+
public static bool DestroyGameObject;
91118
public static Action<ulong> ReachedLastChainInstance;
92119
private const int k_StartingDeferTick = 4;
93-
public static Dictionary<ulong, Dictionary<ulong, DeferredDespawnDaisyChained>> ClientRelativeInstances = new Dictionary<ulong, Dictionary<ulong, DeferredDespawnDaisyChained>>();
120+
public static Dictionary<ulong, Dictionary<ulong, DeferredDespawnDaisyChained>> ClientRelativeInstances;
94121
public bool IsRoot;
95122
public GameObject PrefabToSpawnWhenDespawned;
96123
public bool WasContactedByPeviousChainMember { get; private set; }
@@ -182,7 +209,7 @@ private void InvokeDespawn()
182209
{
183210
FailTest($"[{nameof(InvokeDespawn)}] Client is not the authority but this was invoked (integration test logic issue)!");
184211
}
185-
NetworkObject.DeferDespawn(DeferDespawnTick);
212+
NetworkObject.DeferDespawn(DeferDespawnTick, DestroyGameObject);
186213
}
187214

188215
public override void OnDeferringDespawn(int despawnTick)
@@ -241,7 +268,7 @@ private void Update()
241268
continue;
242269
}
243270

244-
// This should happen shortly afte the instances spawns (based on the deferred despawn count)
271+
// This should happen shortly after the instances spawn (based on the deferred despawn count)
245272
if (!IsRoot && !ClientRelativeInstances[clientId][NetworkObjectId].WasContactedByPeviousChainMember)
246273
{
247274
// exit early if the non-authority instance has not been contacted yet

testproject/Assets/Tests/Runtime/NetworkSceneManager/InScenePlacedNetworkObjectTests.cs

+22-2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ protected override bool CanStartServerAndClients()
5353
return m_CanStartServerAndClients;
5454
}
5555

56+
public enum DespawnMode
57+
{
58+
Despawn,
59+
DeferDespawn,
60+
}
61+
5662
/// <summary>
5763
/// This verifies that in-scene placed NetworkObjects will be properly
5864
/// synchronized if:
@@ -61,8 +67,13 @@ protected override bool CanStartServerAndClients()
6167
/// NetworkObject as a NetworkPrefab
6268
/// </summary>
6369
[UnityTest]
64-
public IEnumerator InSceneNetworkObjectSynchAndSpawn()
70+
public IEnumerator InSceneNetworkObjectSynchAndSpawn([Values] DespawnMode despawnMode)
6571
{
72+
if (!m_DistributedAuthority && despawnMode == DespawnMode.DeferDespawn)
73+
{
74+
Assert.Ignore($"Test ignored as DeferDespawn is only valid with Distributed Authority mode.");
75+
}
76+
6677
NetworkObjectTestComponent.VerboseDebug = true;
6778
// Because despawning a client will cause it to shutdown and clean everything in the
6879
// scene hierarchy, we have to prevent one of the clients from spawning initially before
@@ -99,7 +110,16 @@ public IEnumerator InSceneNetworkObjectSynchAndSpawn()
99110

100111
// Despawn the in-scene placed NetworkObject
101112
Debug.Log("Despawning In-Scene placed NetworkObject");
102-
serverObject.Despawn(false);
113+
114+
if (despawnMode == DespawnMode.Despawn)
115+
{
116+
serverObject.Despawn(false);
117+
}
118+
else
119+
{
120+
serverObject.DeferDespawn(1, false);
121+
}
122+
103123
yield return WaitForConditionOrTimeOut(() => NetworkObjectTestComponent.SpawnedInstances.Count == 0);
104124
AssertOnTimeout($"Timed out waiting for all in-scene instances to be despawned! Current spawned count: {NetworkObjectTestComponent.SpawnedInstances.Count()}");
105125

0 commit comments

Comments
 (0)