Skip to content

Commit 6b9f9b5

Browse files
fix: Synchronize despawn to client (#3514)
<!-- Replace this block with what this PR does and why. Describe what you'd like reviewers to know, how you applied the engineering principles, and any interesting tradeoffs made. Delete bullet points below that don't apply, and update the changelog section as appropriate. --> <!-- Add short version of the JIRA ticket to the PR title (e.g. "feat: new shiny feature [MTT-123]") --> [MTTB-1377](https://jira.unity3d.com/browse/MTTB-1377) ## Changelog - Fixed: Send the DestroyGameObject parameter to the client. ## Testing and Documentation - Includes unit tests. <!-- 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 <!-- If this is a backport: - Add the following to the PR title: "\[Backport\] ..." . - Link to the original PR. If this needs a backport - state this here If a backport is not needed please provide the reason why. If the "Backports" section is not present it will lead to a CI test failure. --> No backport needed as this is 2.x functionality. --------- Co-authored-by: Noel Stephens <[email protected]>
1 parent f70eab1 commit 6b9f9b5

27 files changed

+1223
-1062
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
1616

1717
### Fixed
1818

19+
- Fixed synchronizing the destroyGameObject parameter to clients for InScenePlaced network objects. (#3514)
1920
- Fixed distributed authority related issue where enabling the `NetworkObject.DestroyWithScene` would cause errors when a destroying non-authority instances due to loading (single mode) or unloading scene events. (#3500)
2021

2122
### Changed

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

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Unity.Collections.LowLevel.Unsafe;
88
using Unity.Profiling;
99
using UnityEngine;
10+
using Object = UnityEngine.Object;
1011

1112
namespace Unity.Netcode
1213
{
@@ -1126,36 +1127,15 @@ internal void OnClientDisconnectFromServer(ulong clientId)
11261127
{
11271128
if (!playerObject.DontDestroyWithOwner)
11281129
{
1129-
// If a player NetworkObject is being despawned, make sure to remove all children if they are marked to not be destroyed
1130-
// with the owner.
1131-
if (NetworkManager.DAHost)
1132-
{
1133-
// Remove any children from the player object if they are not going to be destroyed with the owner
1134-
var childNetworkObjects = playerObject.GetComponentsInChildren<NetworkObject>();
1135-
foreach (var child in childNetworkObjects)
1136-
{
1137-
// TODO: We have always just removed all children, but we might think about changing this to preserve the nested child hierarchy.
1138-
if (child.DontDestroyWithOwner && child.transform.transform.parent)
1139-
{
1140-
// If we are here, then we are running in DAHost mode and have the authority to remove the child from its parent
1141-
child.AuthorityAppliedParenting = true;
1142-
child.TryRemoveParentCachedWorldPositionStays();
1143-
}
1144-
}
1145-
}
1146-
1147-
if (NetworkManager.PrefabHandler.ContainsHandler(playerObject.GlobalObjectIdHash))
1148-
{
1149-
// Despawn but don't destroy. DA Host will act like the service and send despawn notifications.
1150-
NetworkManager.SpawnManager.DespawnObject(playerObject, false, NetworkManager.DistributedAuthorityMode);
1151-
// Let the prefab handler determine if it will be destroyed
1152-
NetworkManager.PrefabHandler.HandleNetworkPrefabDestroy(playerObject);
1153-
}
1154-
else if (playerObject.IsSpawned)
1130+
if (playerObject.IsSpawned)
11551131
{
11561132
// Call despawn to assure NetworkBehaviour.OnNetworkDespawn is invoked on the server-side (when the client side disconnected).
11571133
// This prevents the issue (when just destroying the GameObject) where any NetworkBehaviour component(s) destroyed before the NetworkObject would not have OnNetworkDespawn invoked.
1158-
NetworkManager.SpawnManager.DespawnObject(playerObject, true, NetworkManager.DistributedAuthorityMode);
1134+
NetworkManager.SpawnManager.DespawnObject(playerObject, true, true);
1135+
}
1136+
else
1137+
{
1138+
Object.Destroy(playerObject.gameObject);
11591139
}
11601140
}
11611141
else if (!NetworkManager.ShutdownInProgress)
@@ -1179,18 +1159,13 @@ internal void OnClientDisconnectFromServer(ulong clientId)
11791159
// If destroying with owner, then always despawn and destroy (or defer destroying to prefab handler)
11801160
if (!ownedObject.DontDestroyWithOwner)
11811161
{
1182-
if (NetworkManager.PrefabHandler.ContainsHandler(ownedObject.GlobalObjectIdHash))
1162+
if (ownedObject.IsSpawned)
11831163
{
1184-
if (ownedObject.IsSpawned)
1185-
{
1186-
// Don't destroy (prefab handler will determine this, but always notify
1187-
NetworkManager.SpawnManager.DespawnObject(ownedObject, false, true);
1188-
}
1189-
NetworkManager.PrefabHandler.HandleNetworkPrefabDestroy(ownedObject);
1164+
NetworkManager.SpawnManager.DespawnObject(ownedObject, true, true);
11901165
}
11911166
else
11921167
{
1193-
NetworkManager.SpawnManager.DespawnObject(ownedObject, true, true);
1168+
Object.Destroy(ownedObject.gameObject);
11941169
}
11951170
}
11961171
else if (!NetworkManager.ShutdownInProgress)

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

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,13 @@ namespace Unity.Netcode
66
internal struct DestroyObjectMessage : INetworkMessage, INetworkSerializeByMemcpy
77
{
88
private const int k_OptimizeDestroyObjectMessage = 1;
9-
public int Version => k_OptimizeDestroyObjectMessage;
9+
private const int k_AllowDestroyGameInPlaced = 2;
10+
public int Version => k_AllowDestroyGameInPlaced;
1011

1112
private const string k_Name = "DestroyObjectMessage";
1213

1314
public ulong NetworkObjectId;
1415

15-
/// <summary>
16-
/// Used to communicate whether to destroy the associated game object.
17-
/// Should be false if the object is InScenePlaced and true otherwise
18-
/// </summary>
19-
public bool DestroyGameObject;
2016
private byte m_DestroyFlags;
2117

2218
internal int DeferredDespawnTick;
@@ -25,32 +21,29 @@ internal struct DestroyObjectMessage : INetworkMessage, INetworkSerializeByMemcp
2521

2622
internal bool IsDistributedAuthority;
2723

28-
private const byte k_ClientTargetedDestroy = 0x01;
29-
private const byte k_DeferredDespawn = 0x02;
30-
3124
internal bool IsTargetedDestroy
3225
{
33-
get => GetFlag(k_ClientTargetedDestroy);
26+
get => ByteUtility.GetBit(m_DestroyFlags, 0);
3427

35-
set => SetFlag(value, k_ClientTargetedDestroy);
28+
set => ByteUtility.SetBit(ref m_DestroyFlags, 0, value);
3629
}
3730

3831
private bool IsDeferredDespawn
3932
{
40-
get => GetFlag(k_DeferredDespawn);
33+
get => ByteUtility.GetBit(m_DestroyFlags, 1);
4134

42-
set => SetFlag(value, k_DeferredDespawn);
35+
set => ByteUtility.SetBit(ref m_DestroyFlags, 1, value);
4336
}
4437

45-
private bool GetFlag(int flag)
38+
/// <summary>
39+
/// Used to communicate whether to destroy the associated game object.
40+
/// Should be false if the object is InScenePlaced and true otherwise
41+
/// </summary>
42+
public bool DestroyGameObject
4643
{
47-
return (m_DestroyFlags & flag) != 0;
48-
}
44+
get => ByteUtility.GetBit(m_DestroyFlags, 2);
4945

50-
private void SetFlag(bool set, byte flag)
51-
{
52-
if (set) { m_DestroyFlags = (byte)(m_DestroyFlags | flag); }
53-
else { m_DestroyFlags = (byte)(m_DestroyFlags & ~flag); }
46+
set => ByteUtility.SetBit(ref m_DestroyFlags, 2, value);
5447
}
5548

5649
public void Serialize(FastBufferWriter writer, int targetVersion)
@@ -74,6 +67,10 @@ public void Serialize(FastBufferWriter writer, int targetVersion)
7467
BytePacker.WriteValueBitPacked(writer, DeferredDespawnTick);
7568
}
7669
}
70+
else if (targetVersion >= k_AllowDestroyGameInPlaced)
71+
{
72+
writer.WriteByteSafe(m_DestroyFlags);
73+
}
7774

7875
if (targetVersion < k_OptimizeDestroyObjectMessage)
7976
{
@@ -103,10 +100,15 @@ public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int
103100
ByteUnpacker.ReadValueBitPacked(reader, out DeferredDespawnTick);
104101
}
105102
}
103+
else if (receivedMessageVersion >= k_AllowDestroyGameInPlaced)
104+
{
105+
reader.ReadByteSafe(out m_DestroyFlags);
106+
}
106107

107108
if (receivedMessageVersion < k_OptimizeDestroyObjectMessage)
108109
{
109-
reader.ReadValueSafe(out DestroyGameObject);
110+
reader.ReadValueSafe(out bool destroyGameObject);
111+
DestroyGameObject = destroyGameObject;
110112
}
111113

112114
if (networkManager.SpawnManager.SpawnedObjects.ContainsKey(NetworkObjectId))
@@ -169,7 +171,7 @@ public void Handle(ref NetworkContext context)
169171
}
170172

171173
// Otherwise just despawn the NetworkObject right now
172-
networkManager.SpawnManager.OnDespawnNonAuthorityObject(networkObject);
174+
networkManager.SpawnManager.OnDespawnNonAuthorityObject(networkObject, DestroyGameObject);
173175
networkManager.NetworkMetrics.TrackObjectDestroyReceived(context.SenderId, networkObject, context.MessageSize);
174176
}
175177

@@ -208,7 +210,7 @@ private void HandleDeferredDespawn(ref NetworkManager networkManager, ref Networ
208210
{
209211
networkObject.DeferredDespawnTick = DeferredDespawnTick;
210212
var hasCallback = networkObject.OnDeferredDespawnComplete != null;
211-
networkManager.SpawnManager.DeferDespawnNetworkObject(NetworkObjectId, DeferredDespawnTick, hasCallback);
213+
networkManager.SpawnManager.DeferDespawnNetworkObject(NetworkObjectId, DeferredDespawnTick, hasCallback, DestroyGameObject);
212214
}
213215
}
214216
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ namespace Unity.Netcode
33
/// <summary>
44
/// The permission types for reading a var
55
/// </summary>
6+
/// <remarks>
7+
/// Only relevant when using the client/server network topology.
8+
/// In distributed authority mode everyone can always read.
9+
/// </remarks>
610
public enum NetworkVariableReadPermission
711
{
812
/// <summary>

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

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1280,13 +1280,7 @@ public SceneEventProgressStatus UnloadScene(Scene scene)
12801280
}
12811281
}
12821282

1283-
sceneEventProgress.LoadSceneMode = LoadSceneMode.Additive;
1284-
1285-
// Any NetworkObjects marked to not be destroyed with a scene and reside within the scene about to be unloaded
1286-
// should be migrated temporarily into the DDOL, once the scene is unloaded they will be migrated into the
1287-
// currently active scene.
12881283
var networkManager = NetworkManager;
1289-
SceneManagerHandler.MoveObjectsFromSceneToDontDestroyOnLoad(ref networkManager, scene);
12901284

12911285
var sceneEventData = BeginSceneEvent();
12921286
sceneEventData.SceneEventProgressId = sceneEventProgress.Guid;
@@ -1297,10 +1291,16 @@ public SceneEventProgressStatus UnloadScene(Scene scene)
12971291

12981292
// This will be the message we send to everyone when this scene event sceneEventProgress is complete
12991293
sceneEventProgress.SceneEventType = SceneEventType.UnloadEventCompleted;
1294+
sceneEventProgress.LoadSceneMode = LoadSceneMode.Additive;
13001295

13011296
sceneEventProgress.SceneEventId = sceneEventData.SceneEventId;
13021297
sceneEventProgress.OnSceneEventCompleted = OnSceneUnloaded;
13031298

1299+
// Any NetworkObjects marked to not be destroyed with a scene and reside within the scene about to be unloaded
1300+
// should be migrated temporarily into the DDOL, once the scene is unloaded they will be migrated into the
1301+
// currently active scene.
1302+
SceneManagerHandler.MoveObjectsFromSceneToDontDestroyOnLoad(ref networkManager, scene);
1303+
13041304
if (!RemoveServerClientSceneHandle(sceneEventData.SceneHandle, scene.handle))
13051305
{
13061306
Debug.LogError($"Failed to remove {SceneNameFromHash(sceneEventData.SceneHash)} scene handles [Server ({sceneEventData.SceneHandle})][Local({scene.handle})]");
@@ -2831,6 +2831,27 @@ internal bool IsSceneEventInProgress()
28312831
return false;
28322832
}
28332833

2834+
internal bool IsSceneUnloading(NetworkObject networkObject)
2835+
{
2836+
if (!NetworkManager.NetworkConfig.EnableSceneManagement)
2837+
{
2838+
return false;
2839+
}
2840+
2841+
foreach (var sceneEventEntry in SceneEventProgressTracking)
2842+
{
2843+
var name = SceneNameFromHash(sceneEventEntry.Value.SceneHash);
2844+
var scene = SceneManager.GetSceneByName(name);
2845+
var sameScene = name == networkObject.gameObject.scene.name && scene.handle == networkObject.SceneOriginHandle;
2846+
2847+
if (!sceneEventEntry.Value.HasTimedOut() && sceneEventEntry.Value.IsUnloading() && sceneEventEntry.Value.Status == SceneEventProgressStatus.Started && sameScene)
2848+
{
2849+
return true;
2850+
}
2851+
}
2852+
return false;
2853+
}
2854+
28342855
/// <summary>
28352856
/// Handles notifying clients when a NetworkObject has been migrated into a new scene
28362857
/// </summary>

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,14 @@ internal void ClientFinishedSceneEvent(ulong clientId)
223223
}
224224
}
225225

226+
/// <summary>
227+
/// Returns whether the SceneEventType is related to an unloading event.
228+
/// </summary>
229+
internal bool IsUnloading()
230+
{
231+
return SceneEventType is SceneEventType.Unload or SceneEventType.UnloadComplete or SceneEventType.UnloadEventCompleted;
232+
}
233+
226234
/// <summary>
227235
/// Determines if the scene event has finished for both
228236
/// client(s) and server.

0 commit comments

Comments
 (0)