From 2dd2918e9bb6489b0256e9adf600f504535b2046 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Fri, 3 Apr 2026 15:47:31 -0500 Subject: [PATCH] fix Fixing some issues based on varying destroy order of operations (more recent change where instantiation order does not reflect the order in which they will be destroyed via SceneManager during an in-session scene load (single mode). --- .../Components/Helpers/AttachableBehaviour.cs | 12 +++++++-- .../Components/Helpers/AttachableNode.cs | 26 +++++++++++++++---- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs index 7038e74cd1..bc435e73c2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs @@ -392,7 +392,8 @@ internal void ForceComponentChange(bool isAttaching, bool forcedChange) foreach (var componentControllerEntry in ComponentControllers) { - if (componentControllerEntry.AutoTrigger.HasFlag(triggerType)) + // Only if the component controller still exists and has the appropriate flag. + if (componentControllerEntry.ComponentController && componentControllerEntry.AutoTrigger.HasFlag(triggerType)) { componentControllerEntry.ComponentController.ForceChangeEnabled(componentControllerEntry.EnableOnAttach ? isAttaching : !isAttaching, forcedChange); } @@ -459,7 +460,14 @@ internal void InternalDetach() { if (m_AttachableNode) { - if (m_DefaultParent) + // TODO-FIX: We might track if something has been "destroyed" in order + // to be able to be 100% sure in the event a user disables the world item + // when detatched. Otherwise, we keep this in place and make note of it + // in documentation. + // Issue: + // Edge-case where the parent could be in the middle of being destroyed. + // If not active in the hierarchy, then don't attempt to set the parent. + if (m_DefaultParent && m_DefaultParent.activeInHierarchy) { // Set the original parent and origianl local position and rotation transform.SetParent(m_DefaultParent.transform, false); diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs index d1a2ca9c16..e15b71cb4d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableNode.cs @@ -71,20 +71,36 @@ public override void OnNetworkPreDespawn() { for (int i = m_AttachedBehaviours.Count - 1; i >= 0; i--) { - if (!m_AttachedBehaviours[i]) + var attachable = m_AttachedBehaviours[i]; + if (!attachable) { continue; } // If we don't have authority but should detach on despawn, // then proceed to detach. - if (!m_AttachedBehaviours[i].HasAuthority) + if (!attachable.HasAuthority) { - m_AttachedBehaviours[i].ForceDetach(); + attachable.ForceDetach(); } else { - // Detach the normal way with authority - m_AttachedBehaviours[i].Detach(); + // TODO-FIX: We might track if something has been "destroyed" in order + // to be able to be 100% sure this is specific to being destroyed. + // Otherwise, we keep this in place and make note of it + // in documentation that you cannot detatch from something already despawned. + // Issue: When trying to detatch if the thing attached is no longer + // spawned. Instantiation order recently changed such that + // the attachable =or= the attach node target could be despawned + // and in the middle of being destroyed. Resolution for this + // is to skip over destroyed object (default) and then only sort + // through the things the local NetworkManager instance has authority + // over. Even then, we have to check if the attached object is still + // spawned before attempting to detatch it. + if (attachable.IsSpawned) + { + // Detach the normal way with authority + attachable.Detach(); + } } } }