Skip to content

Commit b642aa2

Browse files
committed
Merge branch 'develop-2.0.0' of https://github.com/Unity-Technologies/com.unity.netcode.gameobjects into fix/2.x/mttb-1080/network-object-length-serialization
2 parents 016058d + f2e776e commit b642aa2

File tree

4 files changed

+179
-3
lines changed

4 files changed

+179
-3
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
### Fixed
1515

1616
- Fixed issues with the `NetworkBehaviour` and `NetworkVariable` length safety checks. (#3405)
17+
- Fixed issue where during a `NetworkObject`'s spawn if you instantiated, spawned, and parented another network prefab under the currently spawning `NetworkObject` the parenting message would not properly defer until the parent `NetworkObject` was spawned. (#3401)
1718

1819
### Changed
1920

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ internal struct ParentSyncMessage : INetworkMessage
66
{
77
public int Version => 0;
88

9-
private const string k_Name = "DestroyObjectMessage";
9+
private const string k_Name = "ParentSyncMessage";
1010

1111
public ulong NetworkObjectId;
1212

@@ -87,12 +87,20 @@ public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int
8787
reader.ReadValueSafe(out Rotation);
8888
reader.ReadValueSafe(out Scale);
8989

90-
// If the target NetworkObject does not exist =or= the target latest parent does not exist then defer the message
91-
if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(NetworkObjectId) || (LatestParent.HasValue && !networkManager.SpawnManager.SpawnedObjects.ContainsKey(LatestParent.Value)))
90+
// If the target NetworkObject does not exist then defer this message until it does.
91+
if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(NetworkObjectId))
9292
{
9393
networkManager.DeferredMessageManager.DeferMessage(IDeferredNetworkMessageManager.TriggerType.OnSpawn, NetworkObjectId, reader, ref context, k_Name);
9494
return false;
9595
}
96+
97+
// If the target parent does not exist, then defer this message until it does.
98+
if (LatestParent.HasValue && !networkManager.SpawnManager.SpawnedObjects.ContainsKey(LatestParent.Value))
99+
{
100+
networkManager.DeferredMessageManager.DeferMessage(IDeferredNetworkMessageManager.TriggerType.OnSpawn, LatestParent.Value, reader, ref context, k_Name);
101+
return false;
102+
}
103+
96104
return true;
97105
}
98106

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
using System.Collections;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using NUnit.Framework;
5+
using Unity.Netcode.TestHelpers.Runtime;
6+
using UnityEngine;
7+
using UnityEngine.TestTools;
8+
9+
namespace Unity.Netcode.RuntimeTests
10+
{
11+
[TestFixture(NetworkTopologyTypes.ClientServer, NetworkSpawnTypes.OnNetworkSpawn)]
12+
[TestFixture(NetworkTopologyTypes.ClientServer, NetworkSpawnTypes.OnNetworkPostSpawn)]
13+
[TestFixture(NetworkTopologyTypes.DistributedAuthority, NetworkSpawnTypes.OnNetworkSpawn)]
14+
[TestFixture(NetworkTopologyTypes.DistributedAuthority, NetworkSpawnTypes.OnNetworkPostSpawn)]
15+
internal class ParentingDuringSpawnTests : IntegrationTestWithApproximation
16+
{
17+
protected override int NumberOfClients => 2;
18+
19+
public enum NetworkSpawnTypes
20+
{
21+
OnNetworkSpawn,
22+
OnNetworkPostSpawn,
23+
}
24+
25+
private NetworkSpawnTypes m_NetworkSpawnType;
26+
27+
private GameObject m_ParentPrefab;
28+
private GameObject m_ChildPrefab;
29+
private NetworkObject m_AuthorityInstance;
30+
private List<NetworkManager> m_NetworkManagers = new List<NetworkManager>();
31+
private StringBuilder m_Errors = new StringBuilder();
32+
33+
public class ParentDuringSpawnBehaviour : NetworkBehaviour
34+
{
35+
public GameObject ChildToSpawn;
36+
37+
public NetworkSpawnTypes NetworkSpawnType;
38+
39+
public Transform ChildSpawnPoint;
40+
41+
private void SpawnThenParent()
42+
{
43+
var child = NetworkObject.InstantiateAndSpawn(ChildToSpawn, NetworkManager, position: ChildSpawnPoint.position, rotation: ChildSpawnPoint.rotation);
44+
if (!child.TrySetParent(NetworkObject))
45+
{
46+
var errorMessage = $"[{ChildToSpawn}] Failed to parent child {child.name} under parent {gameObject.name}!";
47+
Debug.LogError(errorMessage);
48+
}
49+
}
50+
51+
public override void OnNetworkSpawn()
52+
{
53+
if (HasAuthority && NetworkSpawnType == NetworkSpawnTypes.OnNetworkSpawn)
54+
{
55+
SpawnThenParent();
56+
}
57+
58+
base.OnNetworkSpawn();
59+
}
60+
61+
protected override void OnNetworkPostSpawn()
62+
{
63+
if (HasAuthority && NetworkSpawnType == NetworkSpawnTypes.OnNetworkPostSpawn)
64+
{
65+
SpawnThenParent();
66+
}
67+
base.OnNetworkPostSpawn();
68+
}
69+
}
70+
71+
public ParentingDuringSpawnTests(NetworkTopologyTypes networkTopology, NetworkSpawnTypes networkSpawnType) : base(networkTopology)
72+
{
73+
m_NetworkSpawnType = networkSpawnType;
74+
}
75+
76+
protected override void OnServerAndClientsCreated()
77+
{
78+
m_ParentPrefab = CreateNetworkObjectPrefab("Parent");
79+
m_ChildPrefab = CreateNetworkObjectPrefab("Child");
80+
var parentComponet = m_ParentPrefab.AddComponent<ParentDuringSpawnBehaviour>();
81+
parentComponet.ChildToSpawn = m_ChildPrefab;
82+
var spawnPoint = new GameObject();
83+
parentComponet.ChildSpawnPoint = spawnPoint.transform;
84+
parentComponet.ChildSpawnPoint.position = GetRandomVector3(-5.0f, 5.0f);
85+
var rotation = parentComponet.ChildSpawnPoint.rotation;
86+
rotation.eulerAngles = GetRandomVector3(-180.0f, 180.0f);
87+
parentComponet.ChildSpawnPoint.rotation = rotation;
88+
base.OnServerAndClientsCreated();
89+
}
90+
91+
private bool NonAuthorityInstancesSpawnedParent()
92+
{
93+
foreach (var networkManager in m_NetworkManagers)
94+
{
95+
if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(m_AuthorityInstance.NetworkObjectId))
96+
{
97+
return false;
98+
}
99+
}
100+
return true;
101+
}
102+
103+
private bool NonAuthorityInstancesParentedChild()
104+
{
105+
m_Errors.Clear();
106+
if (m_AuthorityInstance.transform.childCount == 0)
107+
{
108+
return false;
109+
}
110+
var authorityChildObject = m_AuthorityInstance.transform.GetChild(0).GetComponent<NetworkObject>();
111+
112+
foreach (var networkManager in m_NetworkManagers)
113+
{
114+
if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(authorityChildObject.NetworkObjectId))
115+
{
116+
m_Errors.AppendLine($"{networkManager.name} has not spawned the child {authorityChildObject.name}!");
117+
return false;
118+
}
119+
var childObject = networkManager.SpawnManager.SpawnedObjects[authorityChildObject.NetworkObjectId];
120+
121+
if (childObject.transform.parent == null)
122+
{
123+
m_Errors.AppendLine($"{childObject.name} does not have a parent!");
124+
return false;
125+
}
126+
127+
if (!Approximately(authorityChildObject.transform.position, childObject.transform.position))
128+
{
129+
m_Errors.AppendLine($"{childObject.name} position {GetVector3Values(childObject.transform.position)} does " +
130+
$"not match the authority's position {GetVector3Values(authorityChildObject.transform.position)}!");
131+
return false;
132+
}
133+
134+
if (!Approximately(authorityChildObject.transform.rotation, childObject.transform.rotation))
135+
{
136+
m_Errors.AppendLine($"{childObject.name} rotation {GetVector3Values(childObject.transform.rotation.eulerAngles)} does " +
137+
$"not match the authority's position {GetVector3Values(authorityChildObject.transform.rotation.eulerAngles)}!");
138+
return false;
139+
}
140+
}
141+
return true;
142+
}
143+
144+
[UnityTest]
145+
public IEnumerator ParentDuringSpawn()
146+
{
147+
m_NetworkManagers.Clear();
148+
var authorityNetworkManager = m_DistributedAuthority ? m_ClientNetworkManagers[0] : m_ServerNetworkManager;
149+
150+
m_NetworkManagers.AddRange(m_ClientNetworkManagers);
151+
if (!UseCMBService())
152+
{
153+
m_NetworkManagers.Add(m_ServerNetworkManager);
154+
}
155+
156+
m_AuthorityInstance = SpawnObject(m_ParentPrefab, authorityNetworkManager).GetComponent<NetworkObject>();
157+
158+
yield return WaitForConditionOrTimeOut(NonAuthorityInstancesSpawnedParent);
159+
AssertOnTimeout($"Not all clients spawned the parent {nameof(NetworkObject)}!");
160+
161+
yield return WaitForConditionOrTimeOut(NonAuthorityInstancesParentedChild);
162+
AssertOnTimeout($"Non-Authority instance had a mismatched value: \n {m_Errors}");
163+
}
164+
}
165+
}

com.unity.netcode.gameobjects/Tests/Runtime/ParentingDuringSpawnTests.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)