Skip to content

Commit 2cf2019

Browse files
authored
Merge branch 'develop' into pvp-fixes-develop-v2
2 parents ded6aaa + 8c9490b commit 2cf2019

File tree

8 files changed

+210
-11
lines changed

8 files changed

+210
-11
lines changed

.github/workflows/backport-verification.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ jobs:
1818
runs-on: ubuntu-latest
1919
steps:
2020
- name: Checkout code
21-
uses: actions/checkout@v3
21+
uses: actions/checkout@v4
2222

2323
- name: Check PR description
24-
uses: actions/github-script@v6
24+
uses: actions/github-script@v7
2525
with:
2626
script: |
2727
const pr = context.payload.pull_request;

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 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. (#3403)
1920
- Fixed issue where in-scene placed `NetworkObjects` could fail to synchronize its transform properly (especially without a `NetworkTransform`) if their parenting changes from the default when the scene is loaded and if the same scene remains loaded between network sessions while the parenting is completely different from the original hierarchy. (#3388)
2021
- Fixed an issue in `UnityTransport` where the transport would accept sends on invalid connections, leading to a useless memory allocation and confusing error message. (#3383)
2122
- Fixed issue where `NetworkAnimator` would log an error if there was no destination transition information. (#3384)

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,14 @@ public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int
8888
networkManager.DeferredMessageManager.DeferMessage(IDeferredNetworkMessageManager.TriggerType.OnSpawn, NetworkObjectId, reader, ref context);
8989
return false;
9090
}
91+
92+
// If the target parent does not exist, then defer this message until it does.
93+
if (LatestParent.HasValue && !networkManager.SpawnManager.SpawnedObjects.ContainsKey(LatestParent.Value))
94+
{
95+
networkManager.DeferredMessageManager.DeferMessage(IDeferredNetworkMessageManager.TriggerType.OnSpawn, LatestParent.Value, reader, ref context);
96+
return false;
97+
}
98+
9199
return true;
92100
}
93101

com.unity.netcode.gameobjects/TestHelpers/Runtime/IntegrationTestWithApproximation.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,26 @@ public abstract class IntegrationTestWithApproximation : NetcodeIntegrationTest
88
{
99
private const float k_AproximateDeltaVariance = 0.01f;
1010

11+
/// <summary>
12+
/// Returns a <see cref="Vector3"/> as a formatted string.
13+
/// </summary>
14+
/// <param name="vector3">reference of <see cref="Vector3"/> to return as a formatted string.</param>
15+
/// <returns><see cref="string"/></returns>
16+
protected string GetVector3Values(ref Vector3 vector3)
17+
{
18+
return $"({vector3.x:F6},{vector3.y:F6},{vector3.z:F6})";
19+
}
20+
21+
/// <summary>
22+
/// Returns a <see cref="Vector3"/> as a formatted string.
23+
/// </summary>
24+
/// <param name="vector3"><see cref="Vector3"/> to return as a formatted string.</param>
25+
/// <returns><see cref="string"/></returns>
26+
protected string GetVector3Values(Vector3 vector3)
27+
{
28+
return GetVector3Values(ref vector3);
29+
}
30+
1131
protected virtual float GetDeltaVarianceThreshold()
1232
{
1333
return k_AproximateDeltaVariance;

com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSpawnManyObjectsTests.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System.Collections;
2-
using NUnit.Framework;
32
using Unity.Netcode.TestHelpers.Runtime;
43
using UnityEngine;
54
using UnityEngine.TestTools;
@@ -47,19 +46,23 @@ protected override void OnServerAndClientsCreated()
4746
}
4847

4948
[UnityTest]
50-
// When this test fails it does so without an exception and will wait the default ~6 minutes
51-
[Timeout(360000)] // Tracked in MTT-11360
5249
public IEnumerator WhenManyObjectsAreSpawnedAtOnce_AllAreReceived()
5350
{
51+
var timeStarted = Time.realtimeSinceStartup;
5452
for (int x = 0; x < k_SpawnedObjects; x++)
5553
{
5654
NetworkObject serverObject = Object.Instantiate(m_PrefabToSpawn.Prefab).GetComponent<NetworkObject>();
5755
serverObject.NetworkManagerOwner = m_ServerNetworkManager;
5856
serverObject.Spawn();
5957
}
58+
59+
var timeSpawned = Time.realtimeSinceStartup - timeStarted;
60+
// Provide plenty of time to spawn all 1500 objects in case the CI VM is running slow
61+
var timeoutHelper = new TimeoutHelper(30);
6062
// ensure all objects are replicated
61-
yield return WaitForConditionOrTimeOut(() => SpawnObjecTrackingComponent.SpawnedObjects == k_SpawnedObjects);
62-
AssertOnTimeout($"Timed out waiting for the client to spawn {k_SpawnedObjects} objects!");
63+
yield return WaitForConditionOrTimeOut(() => SpawnObjecTrackingComponent.SpawnedObjects == k_SpawnedObjects, timeoutHelper);
64+
65+
AssertOnTimeout($"Timed out waiting for the client to spawn {k_SpawnedObjects} objects! Time to spawn: {timeSpawned} | Time to timeout: {timeStarted - Time.realtimeSinceStartup}", timeoutHelper);
6366
}
6467
}
6568
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
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(NetworkSpawnTypes.OnNetworkSpawn)]
12+
[TestFixture(NetworkSpawnTypes.OnNetworkPostSpawn)]
13+
internal class ParentingDuringSpawnTests : IntegrationTestWithApproximation
14+
{
15+
protected override int NumberOfClients => 2;
16+
17+
public enum NetworkSpawnTypes
18+
{
19+
OnNetworkSpawn,
20+
OnNetworkPostSpawn,
21+
}
22+
23+
private NetworkSpawnTypes m_NetworkSpawnType;
24+
25+
private GameObject m_ParentPrefab;
26+
private GameObject m_ChildPrefab;
27+
private NetworkObject m_AuthorityInstance;
28+
private List<NetworkManager> m_NetworkManagers = new List<NetworkManager>();
29+
private StringBuilder m_Errors = new StringBuilder();
30+
31+
public class ParentDuringSpawnBehaviour : NetworkBehaviour
32+
{
33+
public GameObject ChildToSpawn;
34+
35+
public NetworkSpawnTypes NetworkSpawnType;
36+
37+
public Transform ChildSpawnPoint;
38+
39+
private void SpawnThenParent()
40+
{
41+
var child = NetworkObject.InstantiateAndSpawn(ChildToSpawn, NetworkManager, position: ChildSpawnPoint.position, rotation: ChildSpawnPoint.rotation);
42+
if (!child.TrySetParent(NetworkObject))
43+
{
44+
var errorMessage = $"[{ChildToSpawn}] Failed to parent child {child.name} under parent {gameObject.name}!";
45+
Debug.LogError(errorMessage);
46+
}
47+
}
48+
49+
public override void OnNetworkSpawn()
50+
{
51+
if (IsServer && NetworkSpawnType == NetworkSpawnTypes.OnNetworkSpawn)
52+
{
53+
SpawnThenParent();
54+
}
55+
56+
base.OnNetworkSpawn();
57+
}
58+
59+
protected override void OnNetworkPostSpawn()
60+
{
61+
if (IsServer && NetworkSpawnType == NetworkSpawnTypes.OnNetworkPostSpawn)
62+
{
63+
SpawnThenParent();
64+
}
65+
base.OnNetworkPostSpawn();
66+
}
67+
}
68+
69+
public ParentingDuringSpawnTests(NetworkSpawnTypes networkSpawnType) : base()
70+
{
71+
m_NetworkSpawnType = networkSpawnType;
72+
}
73+
74+
protected override void OnServerAndClientsCreated()
75+
{
76+
m_ParentPrefab = CreateNetworkObjectPrefab("Parent");
77+
m_ChildPrefab = CreateNetworkObjectPrefab("Child");
78+
var parentComponet = m_ParentPrefab.AddComponent<ParentDuringSpawnBehaviour>();
79+
parentComponet.ChildToSpawn = m_ChildPrefab;
80+
var spawnPoint = new GameObject();
81+
parentComponet.ChildSpawnPoint = spawnPoint.transform;
82+
parentComponet.ChildSpawnPoint.position = GetRandomVector3(-5.0f, 5.0f);
83+
var rotation = parentComponet.ChildSpawnPoint.rotation;
84+
rotation.eulerAngles = GetRandomVector3(-180.0f, 180.0f);
85+
parentComponet.ChildSpawnPoint.rotation = rotation;
86+
base.OnServerAndClientsCreated();
87+
}
88+
89+
private bool NonAuthorityInstancesSpawnedParent()
90+
{
91+
foreach (var networkManager in m_NetworkManagers)
92+
{
93+
if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(m_AuthorityInstance.NetworkObjectId))
94+
{
95+
return false;
96+
}
97+
}
98+
return true;
99+
}
100+
101+
private bool NonAuthorityInstancesParentedChild()
102+
{
103+
m_Errors.Clear();
104+
if (m_AuthorityInstance.transform.childCount == 0)
105+
{
106+
return false;
107+
}
108+
var authorityChildObject = m_AuthorityInstance.transform.GetChild(0).GetComponent<NetworkObject>();
109+
110+
foreach (var networkManager in m_NetworkManagers)
111+
{
112+
if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(authorityChildObject.NetworkObjectId))
113+
{
114+
m_Errors.AppendLine($"{networkManager.name} has not spawned the child {authorityChildObject.name}!");
115+
return false;
116+
}
117+
var childObject = networkManager.SpawnManager.SpawnedObjects[authorityChildObject.NetworkObjectId];
118+
119+
if (childObject.transform.parent == null)
120+
{
121+
m_Errors.AppendLine($"{childObject.name} does not have a parent!");
122+
return false;
123+
}
124+
125+
if (!Approximately(authorityChildObject.transform.position, childObject.transform.position))
126+
{
127+
m_Errors.AppendLine($"{childObject.name} position {GetVector3Values(childObject.transform.position)} does " +
128+
$"not match the authority's position {GetVector3Values(authorityChildObject.transform.position)}!");
129+
return false;
130+
}
131+
132+
if (!Approximately(authorityChildObject.transform.rotation, childObject.transform.rotation))
133+
{
134+
m_Errors.AppendLine($"{childObject.name} rotation {GetVector3Values(childObject.transform.rotation.eulerAngles)} does " +
135+
$"not match the authority's position {GetVector3Values(authorityChildObject.transform.rotation.eulerAngles)}!");
136+
return false;
137+
}
138+
}
139+
return true;
140+
}
141+
142+
[UnityTest]
143+
public IEnumerator ParentDuringSpawn()
144+
{
145+
m_NetworkManagers.Clear();
146+
var authorityNetworkManager = m_ServerNetworkManager;
147+
148+
m_NetworkManagers.AddRange(m_ClientNetworkManagers);
149+
m_NetworkManagers.Add(m_ServerNetworkManager);
150+
151+
m_AuthorityInstance = SpawnObject(m_ParentPrefab, authorityNetworkManager).GetComponent<NetworkObject>();
152+
153+
yield return WaitForConditionOrTimeOut(NonAuthorityInstancesSpawnedParent);
154+
AssertOnTimeout($"Not all clients spawned the parent {nameof(NetworkObject)}!");
155+
156+
yield return WaitForConditionOrTimeOut(NonAuthorityInstancesParentedChild);
157+
AssertOnTimeout($"Non-Authority instance had a mismatched value: \n {m_Errors}");
158+
}
159+
}
160+
}

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

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

testproject/Assets/Tests/Runtime/NetworkTransform/NestedNetworkTransformTests.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,10 +181,6 @@ protected override float GetDeltaVarianceThreshold()
181181

182182
private StringBuilder m_ValidationErrors;
183183

184-
private string GetVector3Values(ref Vector3 vector3)
185-
{
186-
return $"({vector3.x:F6},{vector3.y:F6},{vector3.z:F6})";
187-
}
188184

189185
/// <summary>
190186
/// Validates all transform instance values match the authority's

0 commit comments

Comments
 (0)