Skip to content

Commit f59f5e6

Browse files
fix: NetworkObject component should always precede NetworkBehaviour components [MTT-7216] (#2685)
* fix MTT-7216 This resolves the issue where an in-scene placed NetworkObject can have NetworkBehaviour components not invoke the OnNetworkDespawn method when doing a full scene transition (LoadSceneMode.Single) due to the way GameObjects destroy the attached components. * test This is a unit test to validate the re-ordering of the NetworkObject component.
1 parent a981703 commit f59f5e6

File tree

3 files changed

+130
-0
lines changed

3 files changed

+130
-0
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

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

1515
### Fixed
1616

17+
- Fixed issue where a `NetworkBehaviour` component's `OnNetworkDespawn` was not being invoked on the host-server side for an in-scene placed `NetworkObject` when a scene was unloaded (during a scene transition) and the `NetworkBehaviour` component was positioned/ordered before the `NetworkObject` component. (#2685)
1718
- Fixed issue where `SpawnWithObservers` was not being honored when `NetworkConfig.EnableSceneManagement` was disabled. (#2682)
1819
- Fixed issue where `NetworkAnimator` was not internally tracking changes to layer weights which prevented proper layer weight synchronization back to the original layer weight value. (#2674)
1920
- Fixed "writing past the end of the buffer" error when calling ResetDirty() on managed network variables that are larger than 256 bytes when serialized. (#2670)

com.unity.netcode.gameobjects/Editor/NetworkBehaviourEditor.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,48 @@ public static void CheckForNetworkObject(GameObject gameObject, bool networkObje
417417
}
418418
}
419419
}
420+
421+
if (networkObject != null)
422+
{
423+
OrderNetworkObject(networkObject);
424+
}
425+
}
426+
427+
// Assures the NetworkObject precedes any NetworkBehaviour on the same GameObject as the NetworkObject
428+
private static void OrderNetworkObject(NetworkObject networkObject)
429+
{
430+
var monoBehaviours = networkObject.gameObject.GetComponents<MonoBehaviour>();
431+
var networkObjectIndex = 0;
432+
var firstNetworkBehaviourIndex = -1;
433+
for (int i = 0; i < monoBehaviours.Length; i++)
434+
{
435+
if (monoBehaviours[i] == networkObject)
436+
{
437+
networkObjectIndex = i;
438+
break;
439+
}
440+
441+
var networkBehaviour = monoBehaviours[i] as NetworkBehaviour;
442+
if (networkBehaviour != null)
443+
{
444+
// Get the index of the first NetworkBehaviour Component
445+
if (firstNetworkBehaviourIndex == -1)
446+
{
447+
firstNetworkBehaviourIndex = i;
448+
}
449+
}
450+
}
451+
452+
if (firstNetworkBehaviourIndex != -1 && networkObjectIndex > firstNetworkBehaviourIndex)
453+
{
454+
var positionsToMove = networkObjectIndex - firstNetworkBehaviourIndex;
455+
for (int i = 0; i < positionsToMove; i++)
456+
{
457+
UnityEditorInternal.ComponentUtility.MoveComponentUp(networkObject);
458+
}
459+
460+
EditorUtility.SetDirty(networkObject.gameObject);
461+
}
420462
}
421463
}
422464
}

com.unity.netcode.gameobjects/Tests/Editor/NetworkObjectTests.cs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Text.RegularExpressions;
22
using NUnit.Framework;
3+
using Unity.Netcode.Editor;
34
using UnityEngine;
45
using UnityEngine.TestTools;
56

@@ -64,9 +65,95 @@ public void GetBehaviourIndexOne()
6465
Object.DestroyImmediate(gameObject);
6566
}
6667

68+
/// <summary>
69+
/// Verifies that a NetworkObject component that is positioned after a NetworkBehaviour component will
70+
/// be migrated to a component index value that is before the lowest NetworkBehaviour component index value.
71+
/// (The lowest NetworkBehaviour component's index value will also change when this happens)
72+
/// </summary>
73+
[Test]
74+
public void NetworkObjectComponentOrder()
75+
{
76+
var gameObject = new GameObject(nameof(GetBehaviourIndexOne));
77+
// Add the Networkbehaviour first
78+
var networkBehaviour = gameObject.AddComponent<EmptyNetworkBehaviour>();
79+
// Add an empty MonoBehaviour inbetween the NetworkBehaviour and NetworkObject
80+
gameObject.AddComponent<EmptyMonoBehaviour>();
81+
// Add the NetworkObject
82+
var networkObject = gameObject.AddComponent<NetworkObject>();
83+
var componentIndices = GetIndices(gameObject);
84+
85+
// Verify the NetworkObject procedes the NetworkBehaviour
86+
Assert.True(componentIndices.NetworkObjectIndex > componentIndices.NetworkBehaviourIndex, $"[Initial Setup] NetworkObject index ({componentIndices.NetworkObjectIndex}) is not greater than the NetworkBehaviour index ({componentIndices.NetworkBehaviourIndex})!");
87+
88+
// Force-Invoke the CheckForNetworkObject method in order to verify the NetworkObject is moved
89+
NetworkBehaviourEditor.CheckForNetworkObject(gameObject);
90+
var adjustedIndices = GetIndices(gameObject);
91+
92+
Assert.True(ValidateComponentIndices(componentIndices, GetIndices(gameObject)), "NetworkObject did not get migrated below the NetworkBehaviour!");
93+
94+
// Cleanup
95+
Object.DestroyImmediate(gameObject);
96+
}
97+
98+
private bool ValidateComponentIndices(ComponentIndices previous, ComponentIndices current)
99+
{
100+
if (previous.NetworkObjectIndex != current.NetworkObjectIndex && previous.NetworkBehaviourIndex != current.NetworkBehaviourIndex)
101+
{
102+
if (current.NetworkObjectIndex < previous.NetworkObjectIndex && current.NetworkObjectIndex < current.NetworkBehaviourIndex)
103+
{
104+
return true;
105+
}
106+
}
107+
return false;
108+
}
109+
110+
private ComponentIndices GetIndices(GameObject gameObject)
111+
{
112+
// Get the index/order values for the added NetworkBehaviour and NetworkObject
113+
var components = gameObject.GetComponents<MonoBehaviour>();
114+
var componentIndices = new ComponentIndices()
115+
{
116+
NetworkObjectIndex = -1,
117+
NetworkBehaviourIndex = -1
118+
};
119+
for (int i = 0; i < components.Length; i++)
120+
{
121+
if (componentIndices.NetworkObjectIndex != -1 && componentIndices.NetworkBehaviourIndex != -1)
122+
{
123+
break;
124+
}
125+
var component = components[i];
126+
var networkObjectComponent = component as NetworkObject;
127+
if (networkObjectComponent != null)
128+
{
129+
componentIndices.NetworkObjectIndex = i;
130+
continue;
131+
}
132+
var networkBehaviourComponent = component as EmptyNetworkBehaviour;
133+
if (networkBehaviourComponent != null)
134+
{
135+
componentIndices.NetworkBehaviourIndex = i;
136+
continue;
137+
}
138+
}
139+
140+
return componentIndices;
141+
}
142+
143+
private struct ComponentIndices
144+
{
145+
public int NetworkObjectIndex;
146+
public int NetworkBehaviourIndex;
147+
}
148+
67149
public class EmptyNetworkBehaviour : NetworkBehaviour
68150
{
69151

70152
}
153+
154+
public class EmptyMonoBehaviour : MonoBehaviour
155+
{
156+
157+
}
71158
}
72159
}

0 commit comments

Comments
 (0)