diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index bdc42ccb2c..466cc59122 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -12,6 +12,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed +- Fixed a crash when calling TrySetParent with a null Transform (#2625) - Fixed issue where a `NetworkTransform` using full precision state updates was losing transform state updates when interpolation was enabled. (#2624) - Fixed issue where `NetworkObject.SpawnWithObservers` was not being honored for late joining clients. (#2623) - Fixed issue where invoking `NetworkManager.Shutdown` multiple times, depending upon the timing, could cause an exception. (#2622) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 3643d44a57..cd98c11e20 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -733,6 +733,12 @@ internal void SetNetworkParenting(ulong? latestParent, bool worldPositionStays) /// Whether or not reparenting was successful. public bool TrySetParent(Transform parent, bool worldPositionStays = true) { + // If we are removing ourself from a parent + if (parent == null) + { + return TrySetParent((NetworkObject)null, worldPositionStays); + } + var networkObject = parent.GetComponent(); // If the parent doesn't have a NetworkObjet then return false, otherwise continue trying to parent diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/NetworkObjectParentingTests.cs b/testproject/Assets/Tests/Runtime/ObjectParenting/NetworkObjectParentingTests.cs index 65fb476822..1b1257a6c3 100644 --- a/testproject/Assets/Tests/Runtime/ObjectParenting/NetworkObjectParentingTests.cs +++ b/testproject/Assets/Tests/Runtime/ObjectParenting/NetworkObjectParentingTests.cs @@ -309,6 +309,49 @@ public IEnumerator SetParentTryAPI() Assert.That(m_Cube_NetObjs[setIndex + 1].parent, Is.EqualTo(m_Dude_LeftArm_NetObjs[setIndex + 1])); Assert.That(m_Cube_NetBhvs[setIndex + 1].ParentNetworkObject, Is.EqualTo(m_Dude_LeftArm_NetObjs[setIndex + 1].GetComponent())); } + + Transform nullTransform = null; + GameObject nullGameObject = null; + NetworkObject nullNetworkObject = null; + + + Assert.That(m_Cube_NetObjs[0].GetComponent().TrySetParent(nullTransform)); + Assert.That(m_Cube_NetBhvs[0].ParentNetworkObject, Is.EqualTo(null)); + + nextFrameNumber = Time.frameCount + 2; + yield return new WaitUntil(() => Time.frameCount >= nextFrameNumber); + + for (int setIndex = 0; setIndex < k_ClientInstanceCount; setIndex++) + { + Assert.That(m_Cube_NetObjs[setIndex + 1].parent, Is.EqualTo(null)); + Assert.That(m_Cube_NetBhvs[setIndex + 1].ParentNetworkObject, Is.EqualTo(null)); + } + + + Assert.That(m_Cube_NetObjs[0].GetComponent().TrySetParent(nullGameObject)); + Assert.That(m_Cube_NetBhvs[0].ParentNetworkObject, Is.EqualTo(null)); + + nextFrameNumber = Time.frameCount + 2; + yield return new WaitUntil(() => Time.frameCount >= nextFrameNumber); + + for (int setIndex = 0; setIndex < k_ClientInstanceCount; setIndex++) + { + Assert.That(m_Cube_NetObjs[setIndex + 1].parent, Is.EqualTo(null)); + Assert.That(m_Cube_NetBhvs[setIndex + 1].ParentNetworkObject, Is.EqualTo(null)); + } + + + Assert.That(m_Cube_NetObjs[0].GetComponent().TrySetParent(nullNetworkObject)); + Assert.That(m_Cube_NetBhvs[0].ParentNetworkObject, Is.EqualTo(null)); + + nextFrameNumber = Time.frameCount + 2; + yield return new WaitUntil(() => Time.frameCount >= nextFrameNumber); + + for (int setIndex = 0; setIndex < k_ClientInstanceCount; setIndex++) + { + Assert.That(m_Cube_NetObjs[setIndex + 1].parent, Is.EqualTo(null)); + Assert.That(m_Cube_NetBhvs[setIndex + 1].ParentNetworkObject, Is.EqualTo(null)); + } } } }