Skip to content

Commit b0863ca

Browse files
chore: [1.x] Add NetworkTransform parenting test (#3367)
Adds a test replicating the bug fixed in #3361 Move the code to sync prefabs for late joining clients into the `NetcodeIntegrationTest` ## Changelog - Added: Test for #3361 ## Testing and Documentation - Adds Unit test <!-- Uncomment and mark items off with a * if this PR deprecates any API: ### Deprecated API - [ ] An `[Obsolete]` attribute was added along with a `(RemovedAfter yyyy-mm-dd)` entry. - [ ] An [api updater] was added. - [ ] Deprecation of the API is explained in the CHANGELOG. - [ ] The users can understand why this API was removed and what they should use instead. --> --------- Co-authored-by: Noel Stephens <[email protected]>
1 parent d17d98b commit b0863ca

16 files changed

+216
-102
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,14 @@ private void AddRemoveNetworkManager(NetworkManager networkManager, bool addNetw
524524
/// <param name="networkManager">The NetworkManager instance of the client.</param>
525525
protected virtual void OnNewClientCreated(NetworkManager networkManager)
526526
{
527+
// Ensure any late joining client has all NetworkPrefabs required to connect.
528+
foreach (var networkPrefab in m_ServerNetworkManager.NetworkConfig.Prefabs.Prefabs)
529+
{
530+
if (!networkManager.NetworkConfig.Prefabs.Contains(networkPrefab.Prefab))
531+
{
532+
networkManager.NetworkConfig.Prefabs.Add(networkPrefab);
533+
}
534+
}
527535
}
528536

529537
/// <summary>

com.unity.netcode.gameobjects/Tests/Runtime/DeferredMessagingTests.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,7 @@ protected override void OnNewClientCreated(NetworkManager networkManager)
256256
{
257257
AddPrefabsToClient(networkManager);
258258
}
259-
260-
base.OnNewClientCreated(networkManager);
259+
// Don't call base as this will synchronize the prefabs
261260
}
262261

263262
private void SpawnClients(bool clearTestDeferredMessageManagerCallFlags = true)

com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourPrePostSpawnTests.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,6 @@ protected override IEnumerator OnSetup()
8888
return base.OnSetup();
8989
}
9090

91-
protected override void OnNewClientCreated(NetworkManager networkManager)
92-
{
93-
networkManager.NetworkConfig.Prefabs = m_ServerNetworkManager.NetworkConfig.Prefabs;
94-
base.OnNewClientCreated(networkManager);
95-
}
96-
9791
/// <summary>
9892
/// This validates that pre spawn can be used to instantiate and assign a NetworkVariable (or other prespawn tasks)
9993
/// which can be useful for assigning a NetworkVariable value on the server side when the NetworkVariable has owner write permissions.

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

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,10 @@ private bool CheckClientsSideObserverTestObj()
6262
}
6363

6464
/// <summary>
65-
/// Assures the <see cref="ObserverSpawnTests"/> late joining client has all
66-
/// NetworkPrefabs required to connect.
65+
/// Set up late joining client
6766
/// </summary>
6867
protected override void OnNewClientCreated(NetworkManager networkManager)
6968
{
70-
foreach (var networkPrefab in m_ServerNetworkManager.NetworkConfig.Prefabs.Prefabs)
71-
{
72-
if (!networkManager.NetworkConfig.Prefabs.Contains(networkPrefab.Prefab))
73-
{
74-
networkManager.NetworkConfig.Prefabs.Add(networkPrefab);
75-
}
76-
}
7769
networkManager.NetworkConfig.EnableSceneManagement = m_ServerNetworkManager.NetworkConfig.EnableSceneManagement;
7870
base.OnNewClientCreated(networkManager);
7971
}

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

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,21 +76,17 @@ protected override void OnServerAndClientsCreated()
7676

7777
protected override void OnNewClientCreated(NetworkManager networkManager)
7878
{
79+
// Setup late joining client prefabs first
80+
base.OnNewClientCreated(networkManager);
81+
7982
networkManager.NetworkConfig.PlayerPrefab = m_PlayerPrefab;
8083
networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety = m_VariableLengthSafety == VariableLengthSafety.EnabledNetVarSafety;
81-
foreach (var networkPrefab in m_ServerNetworkManager.NetworkConfig.Prefabs.Prefabs)
82-
{
83-
// To simulate a failure, we exclude the m_InValidNetworkPrefab from the connecting
84-
// client's side.
85-
if (networkPrefab.Prefab.name != m_InValidNetworkPrefab.name)
86-
{
87-
networkManager.NetworkConfig.Prefabs.Add(networkPrefab);
88-
}
89-
}
84+
9085
// Disable forcing the same prefabs to avoid failed connections
9186
networkManager.NetworkConfig.ForceSamePrefabs = false;
9287
networkManager.LogLevel = m_CurrentLogLevel;
93-
base.OnNewClientCreated(networkManager);
88+
// To simulate a failure, exclude the m_InValidNetworkPrefab from the connecting client's side.
89+
networkManager.NetworkConfig.Prefabs.Remove(m_InValidNetworkPrefab);
9490
}
9591

9692
[UnityTest]

com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformBase.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,6 @@ protected override IEnumerator OnServerAndClientsConnected()
339339
/// </summary>
340340
protected override void OnNewClientCreated(NetworkManager networkManager)
341341
{
342-
networkManager.NetworkConfig.Prefabs = m_ServerNetworkManager.NetworkConfig.Prefabs;
343342
networkManager.NetworkConfig.TickRate = GetTickRate();
344343
if (m_EnableVerboseDebug)
345344
{

com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformOwnershipTests.cs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,6 @@ protected override void OnServerAndClientsCreated()
4646
base.OnServerAndClientsCreated();
4747
}
4848

49-
/// <summary>
50-
/// Clients created during a test need to have their prefabs list updated to
51-
/// match the server's prefab list.
52-
/// </summary>
53-
protected override void OnNewClientCreated(NetworkManager networkManager)
54-
{
55-
foreach (var networkPrefab in m_ServerNetworkManager.NetworkConfig.Prefabs.Prefabs)
56-
{
57-
networkManager.NetworkConfig.Prefabs.Add(networkPrefab);
58-
}
59-
60-
base.OnNewClientCreated(networkManager);
61-
}
62-
6349
private bool ClientIsOwner()
6450
{
6551
var clientId = m_ClientNetworkManagers[0].LocalClientId;
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
using System.Collections;
2+
using Unity.Netcode.Components;
3+
using Unity.Netcode.TestHelpers.Runtime;
4+
using UnityEngine;
5+
using UnityEngine.TestTools;
6+
7+
namespace Unity.Netcode.RuntimeTests
8+
{
9+
internal class NetworkTransformParentingTests : IntegrationTestWithApproximation
10+
{
11+
/// <summary>
12+
/// A NetworkBehaviour that moves in space.
13+
/// When spawned on the client, an RPC is sent to the server to spawn a player object for that client.
14+
/// The server parents the player object to the spawner object. This gives a moving parent object and a non-moving child object.
15+
/// The child object should always be at {0,0,0} local space, while the parent object moves around.
16+
/// This NetworkBehaviour tests that parenting to a moving object works as expected.
17+
/// </summary>
18+
internal class PlayerSpawner : NetworkBehaviour
19+
{
20+
/// <summary>
21+
/// Prefab for the player
22+
/// </summary>
23+
public NetworkObject PlayerPrefab;
24+
25+
/// <summary>
26+
/// The server side NetworkObject that was spawned when the client connected.
27+
/// </summary>
28+
public NetworkObject SpawnedPlayer;
29+
30+
/// <summary>
31+
/// Represents the different movement states of the PlayerSpawner during the test lifecycle.
32+
/// </summary>
33+
public enum MoveState
34+
{
35+
// Initial state, PlayerSpawner will move without counting frames
36+
NotStarted,
37+
// The player object has been spawned, start counting frames
38+
PlayerSpawned,
39+
// We have moved far enough to test location
40+
ReachedPeak,
41+
}
42+
public MoveState State = MoveState.NotStarted;
43+
44+
// A count of the number of updates since the player object was spawned.
45+
private int m_Count;
46+
47+
// Movement offsets and targets.
48+
private const float k_PositionOffset = 5.0f;
49+
private const float k_RotationOffset = 25.0f;
50+
private readonly Vector3 m_PositionTarget = Vector3.one * k_PositionOffset * 10;
51+
private readonly Vector3 m_RotationTarget = Vector3.one * k_RotationOffset * 10;
52+
53+
private void Update()
54+
{
55+
if (!IsServer)
56+
{
57+
return;
58+
}
59+
60+
transform.position = Vector3.Lerp(transform.position, m_PositionTarget, Time.deltaTime * 2);
61+
var rotation = transform.rotation;
62+
rotation.eulerAngles = Vector3.Slerp(rotation.eulerAngles, m_RotationTarget, Time.deltaTime * 2);
63+
transform.rotation = rotation;
64+
65+
if (State != MoveState.PlayerSpawned)
66+
{
67+
return;
68+
}
69+
70+
// Move self for some time after player object is spawned
71+
// This ensures the parent object is moving throughout the spawn process.
72+
m_Count++;
73+
if (m_Count > 10)
74+
{
75+
// Mark PlayerSpawner as having moved far enough to test.
76+
State = MoveState.ReachedPeak;
77+
}
78+
}
79+
80+
public override void OnNetworkSpawn()
81+
{
82+
if (IsOwner)
83+
{
84+
// Owner initialises PlayerSpawner movement on spawn
85+
transform.position = Vector3.one * k_PositionOffset;
86+
var rotation = transform.rotation;
87+
rotation.eulerAngles = Vector3.one * k_RotationOffset;
88+
transform.rotation = rotation;
89+
}
90+
else
91+
{
92+
// When spawned on a client, send the RPC to spawn the player object
93+
// Using an RPC ensures the PlayerSpawner is moving for the entire spawning of the player object.
94+
RequestPlayerObjectSpawnServerRpc();
95+
}
96+
}
97+
98+
/// <summary>
99+
/// A ServerRpc that requests the server to spawn a player object for the client that invoked this RPC.
100+
/// </summary>
101+
/// <param name="rpcParams">Parameters for the ServerRpc, including the sender's client ID.</param>
102+
[ServerRpc(RequireOwnership = false)]
103+
private void RequestPlayerObjectSpawnServerRpc(ServerRpcParams rpcParams = default)
104+
{
105+
SpawnedPlayer = Instantiate(PlayerPrefab);
106+
SpawnedPlayer.SpawnAsPlayerObject(rpcParams.Receive.SenderClientId);
107+
SpawnedPlayer.TrySetParent(NetworkObject, false);
108+
State = MoveState.PlayerSpawned;
109+
}
110+
}
111+
112+
// Client Authoritative NetworkTransform
113+
internal class ClientNetworkTransform : NetworkTransform
114+
{
115+
protected override bool OnIsServerAuthoritative()
116+
{
117+
return false;
118+
}
119+
}
120+
121+
// Don't start with any clients, we will manually spawn a client inside the test
122+
protected override int NumberOfClients => 0;
123+
124+
// Parent prefab with moving PlayerSpawner which will spawn the childPrefab
125+
private GameObject m_PlayerSpawnerPrefab;
126+
127+
// Client and server instances
128+
private PlayerSpawner m_ServerPlayerSpawner;
129+
private NetworkObject m_NewClientPlayer;
130+
131+
protected override void OnServerAndClientsCreated()
132+
{
133+
m_PlayerSpawnerPrefab = CreateNetworkObjectPrefab("Parent");
134+
var parentPlayerSpawner = m_PlayerSpawnerPrefab.AddComponent<PlayerSpawner>();
135+
m_PlayerSpawnerPrefab.AddComponent<NetworkTransform>();
136+
137+
var playerPrefab = CreateNetworkObjectPrefab("Child");
138+
var childNetworkTransform = playerPrefab.AddComponent<ClientNetworkTransform>();
139+
childNetworkTransform.InLocalSpace = true;
140+
141+
parentPlayerSpawner.PlayerPrefab = playerPrefab.GetComponent<NetworkObject>();
142+
143+
base.OnServerAndClientsCreated();
144+
}
145+
146+
private bool NewPlayerObjectSpawned()
147+
{
148+
return m_ServerPlayerSpawner.SpawnedPlayer &&
149+
m_ClientNetworkManagers[0].SpawnManager.SpawnedObjects.ContainsKey(m_ServerPlayerSpawner.SpawnedPlayer.NetworkObjectId);
150+
}
151+
152+
private bool HasServerInstanceReachedPeakPoint()
153+
{
154+
VerboseDebug($"Client Local: {m_NewClientPlayer.transform.localPosition} Server Local: {m_ServerPlayerSpawner.SpawnedPlayer.transform.localPosition}");
155+
return m_ServerPlayerSpawner.State == PlayerSpawner.MoveState.ReachedPeak;
156+
}
157+
158+
private bool ServerClientPositionMatches()
159+
{
160+
return Approximately(m_NewClientPlayer.transform.localPosition, m_ServerPlayerSpawner.SpawnedPlayer.transform.localPosition) &&
161+
Approximately(m_NewClientPlayer.transform.position, m_ServerPlayerSpawner.SpawnedPlayer.transform.position);
162+
}
163+
164+
[UnityTest]
165+
public IEnumerator TestParentedPlayerUsingLocalSpace()
166+
{
167+
// Spawn the PlayerSpawner object and save the instantiated component
168+
// The PlayerSpawner object will start moving.
169+
m_ServerPlayerSpawner = SpawnObject(m_PlayerSpawnerPrefab, m_ServerNetworkManager).GetComponent<PlayerSpawner>();
170+
171+
// Create a new client and connect to the server
172+
// The client will prompt the server to spawn a player object and parent it to the PlayerSpawner object.
173+
yield return CreateAndStartNewClient();
174+
175+
yield return WaitForConditionOrTimeOut(NewPlayerObjectSpawned);
176+
AssertOnTimeout($"Client did not spawn new player object!");
177+
178+
// Save the spawned player object
179+
m_NewClientPlayer = m_ClientNetworkManagers[0].SpawnManager.SpawnedObjects[m_ServerPlayerSpawner.SpawnedPlayer.NetworkObjectId];
180+
181+
// Let the parent PlayerSpawner move for several ticks to get an offset
182+
yield return WaitForConditionOrTimeOut(HasServerInstanceReachedPeakPoint);
183+
AssertOnTimeout($"Server instance never reached peak point!");
184+
185+
// Check that the client and server local positions match (they should both be at {0,0,0} local space)
186+
yield return WaitForConditionOrTimeOut(ServerClientPositionMatches);
187+
AssertOnTimeout($"Client local position {m_NewClientPlayer.transform.localPosition} does not match" +
188+
$" server local position {m_ServerPlayerSpawner.SpawnedPlayer.transform.localPosition}");
189+
}
190+
}
191+
}

com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformParentingTests.cs.meta

Lines changed: 3 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/Animation/NetworkAnimatorTests.cs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -626,19 +626,6 @@ public IEnumerator TriggerUpdateTests([Values] OwnerShipMode ownerShipMode, [Val
626626
VerboseDebug($" ------------------ Trigger Test [{TriggerTest.Iteration}][{ownerShipMode}] Stopping ------------------ ");
627627
}
628628

629-
protected override void OnNewClientCreated(NetworkManager networkManager)
630-
{
631-
var networkPrefab = new NetworkPrefab() { Prefab = m_AnimationTestPrefab };
632-
networkManager.NetworkConfig.Prefabs.Add(networkPrefab);
633-
networkPrefab = new NetworkPrefab() { Prefab = m_AnimationOwnerTestPrefab };
634-
networkManager.NetworkConfig.Prefabs.Add(networkPrefab);
635-
636-
networkPrefab = new NetworkPrefab() { Prefab = m_AnimationCheerTestPrefab };
637-
networkManager.NetworkConfig.Prefabs.Add(networkPrefab);
638-
networkPrefab = new NetworkPrefab() { Prefab = m_AnimationCheerOwnerTestPrefab };
639-
networkManager.NetworkConfig.Prefabs.Add(networkPrefab);
640-
}
641-
642629
/// <summary>
643630
/// Verifies that triggers are synchronized with currently connected clients
644631
/// </summary>

testproject/Assets/Tests/Runtime/DontDestroyOnLoadTests.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@ protected override void OnServerAndClientsCreated()
2222
base.OnServerAndClientsCreated();
2323
}
2424

25-
protected override void OnNewClientCreated(NetworkManager networkManager)
26-
{
27-
networkManager.NetworkConfig.Prefabs = m_ServerNetworkManager.NetworkConfig.Prefabs;
28-
base.OnNewClientCreated(networkManager);
29-
}
30-
3125
private ulong m_SpawnedNetworkObjectId;
3226
private StringBuilder m_ErrorLog = new StringBuilder();
3327

testproject/Assets/Tests/Runtime/NetworkManagerTests.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,16 +108,12 @@ public enum ShutdownChecks
108108
protected override void OnNewClientCreated(NetworkManager networkManager)
109109
{
110110
networkManager.NetworkConfig.EnableSceneManagement = m_UseSceneManagement;
111-
foreach (var prefab in m_ServerNetworkManager.NetworkConfig.Prefabs.Prefabs)
112-
{
113-
networkManager.NetworkConfig.Prefabs.Add(prefab);
114-
}
115111
base.OnNewClientCreated(networkManager);
116112
}
117113

118114
/// <summary>
119115
/// Validate shutting down a second time does not cause an exception.
120-
/// </summary>
116+
/// </summary>
121117
[UnityTest]
122118
public IEnumerator ValidateShutdown([Values] ShutdownChecks shutdownCheck)
123119
{
@@ -132,7 +128,7 @@ public IEnumerator ValidateShutdown([Values] ShutdownChecks shutdownCheck)
132128
}
133129
else
134130
{
135-
// For this test (simplify the complexity) with a late joining client, just remove the
131+
// For this test (simplify the complexity) with a late joining client, just remove the
136132
// in-scene placed NetworkObject prior to the client connecting
137133
// (We are testing the shutdown sequence)
138134
var spawnedObjects = m_ServerNetworkManager.SpawnManager.SpawnedObjectsList.ToList();

testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkObjectSceneMigrationTests.cs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,19 +59,6 @@ protected override void OnServerAndClientsCreated()
5959
base.OnServerAndClientsCreated();
6060
}
6161

62-
protected override void OnNewClientCreated(NetworkManager networkManager)
63-
{
64-
foreach (var networkPrfab in m_ServerNetworkManager.NetworkConfig.Prefabs.Prefabs)
65-
{
66-
if (networkPrfab.Prefab == null)
67-
{
68-
continue;
69-
}
70-
networkManager.NetworkConfig.Prefabs.Add(networkPrfab);
71-
}
72-
base.OnNewClientCreated(networkManager);
73-
}
74-
7562
private bool DidClientsSpawnInstance(NetworkObject serverObject, bool checkDestroyWithScene = false)
7663
{
7764
foreach (var networkManager in m_ClientNetworkManagers)

0 commit comments

Comments
 (0)