Skip to content

Commit f723e09

Browse files
test
Migrated all runtime network prefab related tests into a Prefabs subfolder. Added new NetworkPrefabOverrideTests to validate the actual spawned instances on all clients using both client-server and distributed authority network topologies. Added helper method `NettcodeIntegrationTestHelpers.CreateNetworkObject`.
1 parent 8a3ff45 commit f723e09

File tree

8 files changed

+302
-7
lines changed

8 files changed

+302
-7
lines changed

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

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,29 @@ public static void MakeNetworkObjectTestPrefab(NetworkObject networkObject, uint
559559
}
560560
}
561561

562+
/// <summary>
563+
/// Creates a <see cref="NetworkObject"/> to be used with integration testing
564+
/// </summary>
565+
/// <param name="baseName">namr of the object</param>
566+
/// <param name="owner">owner of the object</param>
567+
/// <param name="moveToDDOL">when true, the instance is automatically migrated into the DDOL</param>
568+
/// <returns></returns>
569+
internal static GameObject CreateNetworkObject(string baseName, NetworkManager owner, bool moveToDDOL = false)
570+
{
571+
var gameObject = new GameObject
572+
{
573+
name = baseName
574+
};
575+
var networkObject = gameObject.AddComponent<NetworkObject>();
576+
networkObject.NetworkManagerOwner = owner;
577+
MakeNetworkObjectTestPrefab(networkObject);
578+
if (moveToDDOL)
579+
{
580+
Object.DontDestroyOnLoad(gameObject);
581+
}
582+
return gameObject;
583+
}
584+
562585
public static GameObject CreateNetworkObjectPrefab(string baseName, NetworkManager server, params NetworkManager[] clients)
563586
{
564587
void AddNetworkPrefab(NetworkConfig config, NetworkPrefab prefab)
@@ -570,13 +593,7 @@ void AddNetworkPrefab(NetworkConfig config, NetworkPrefab prefab)
570593
Assert.IsNotNull(server, prefabCreateAssertError);
571594
Assert.IsFalse(server.IsListening, prefabCreateAssertError);
572595

573-
var gameObject = new GameObject
574-
{
575-
name = baseName
576-
};
577-
var networkObject = gameObject.AddComponent<NetworkObject>();
578-
networkObject.NetworkManagerOwner = server;
579-
MakeNetworkObjectTestPrefab(networkObject);
596+
var gameObject = CreateNetworkObject(baseName, server);
580597
var networkPrefab = new NetworkPrefab() { Prefab = gameObject };
581598

582599
// We could refactor this test framework to share a NetworkPrefabList instance, but at this point it's

com.unity.netcode.gameobjects/Tests/Runtime/Prefabs.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
using System.Collections;
2+
using System.Linq;
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+
/// <summary>
12+
/// Integration test that validates spawning instances of <see cref="NetworkPrefab"/>s with overrides and
13+
/// <see cref="NetworkPrefabHandler"/> registered overrides.
14+
/// </summary>
15+
[TestFixture(NetworkTopologyTypes.ClientServer, HostOrServer.Server)]
16+
[TestFixture(NetworkTopologyTypes.ClientServer, HostOrServer.Host)]
17+
[TestFixture(NetworkTopologyTypes.DistributedAuthority, HostOrServer.DAHost)]
18+
internal class NetworkPrefabOverrideTests : NetcodeIntegrationTest
19+
{
20+
private const string k_PrefabRootName = "PrefabObj";
21+
protected override int NumberOfClients => 2;
22+
23+
private NetworkPrefab m_ClientSidePlayerPrefab;
24+
private NetworkPrefab m_PrefabOverride;
25+
26+
public NetworkPrefabOverrideTests(NetworkTopologyTypes networkTopologyType, HostOrServer hostOrServer) : base(networkTopologyType, hostOrServer) { }
27+
28+
/// <summary>
29+
/// Prefab override handler that will instantiate the ServerSideInstance (m_PlayerPrefab) only on server instances
30+
/// and will spawn the ClientSideInstance (m_ClientSidePlayerPrefab.Prefab) only on clients and/or a host.
31+
/// </summary>
32+
public class TestPrefabOverrideHandler : MonoBehaviour, INetworkPrefabInstanceHandler
33+
{
34+
public GameObject ServerSideInstance;
35+
public GameObject ClientSideInstance;
36+
private NetworkManager m_NetworkManager;
37+
38+
private void Start()
39+
{
40+
m_NetworkManager = GetComponent<NetworkManager>();
41+
m_NetworkManager.PrefabHandler.AddHandler(ServerSideInstance, this);
42+
}
43+
44+
private void OnDestroy()
45+
{
46+
if (m_NetworkManager != null && m_NetworkManager.PrefabHandler != null)
47+
{
48+
m_NetworkManager.PrefabHandler.RemoveHandler(ServerSideInstance);
49+
}
50+
}
51+
52+
public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation)
53+
{
54+
var instance = m_NetworkManager.IsClient ? Instantiate(ClientSideInstance) : Instantiate(ServerSideInstance);
55+
return instance.GetComponent<NetworkObject>();
56+
}
57+
58+
public void Destroy(NetworkObject networkObject)
59+
{
60+
Object.Destroy(networkObject);
61+
}
62+
}
63+
64+
/// <summary>
65+
/// Mock component for testing that the client-side player is using the right
66+
/// network prefab.
67+
/// </summary>
68+
public class ClientSideOnlyComponent : MonoBehaviour
69+
{
70+
71+
}
72+
73+
/// <summary>
74+
/// When we create the player prefab, make a modified instance that will be used
75+
/// with the <see cref="TestPrefabOverrideHandler"/>.
76+
/// </summary>
77+
protected override void OnCreatePlayerPrefab()
78+
{
79+
var clientPlayer = Object.Instantiate(m_PlayerPrefab);
80+
clientPlayer.AddComponent<ClientSideOnlyComponent>();
81+
Object.DontDestroyOnLoad(clientPlayer);
82+
m_ClientSidePlayerPrefab = new NetworkPrefab()
83+
{
84+
Prefab = clientPlayer,
85+
};
86+
87+
base.OnCreatePlayerPrefab();
88+
}
89+
90+
/// <summary>
91+
/// Add the additional <see cref="NetworkPrefab"/>s and <see cref="TestPrefabOverrideHandler"/>s to
92+
/// all <see cref="NetworkManager"/> instances.
93+
/// </summary>
94+
protected override void OnServerAndClientsCreated()
95+
{
96+
// Create a NetworkPrefab with an override
97+
var basePrefab = NetcodeIntegrationTestHelpers.CreateNetworkObject($"{k_PrefabRootName}-base", m_ServerNetworkManager, true);
98+
var targetPrefab = NetcodeIntegrationTestHelpers.CreateNetworkObject($"{k_PrefabRootName}-over", m_ServerNetworkManager, true);
99+
m_PrefabOverride = new NetworkPrefab()
100+
{
101+
Prefab = basePrefab,
102+
Override = NetworkPrefabOverride.Prefab,
103+
SourcePrefabToOverride = basePrefab,
104+
OverridingTargetPrefab = targetPrefab,
105+
};
106+
107+
// Add the prefab override handler for instance specific player prefabs to the server side
108+
var playerPrefabOverrideHandler = m_ServerNetworkManager.gameObject.AddComponent<TestPrefabOverrideHandler>();
109+
playerPrefabOverrideHandler.ServerSideInstance = m_PlayerPrefab;
110+
playerPrefabOverrideHandler.ClientSideInstance = m_ClientSidePlayerPrefab.Prefab;
111+
112+
// Add the NetworkPrefab with override
113+
m_ServerNetworkManager.NetworkConfig.Prefabs.Add(m_PrefabOverride);
114+
// Add the client player prefab that will be used on clients (and the host)
115+
m_ServerNetworkManager.NetworkConfig.Prefabs.Add(m_ClientSidePlayerPrefab);
116+
117+
foreach (var networkManager in m_ClientNetworkManagers)
118+
{
119+
// Add the prefab override handler for instance specific player prefabs to the client side
120+
playerPrefabOverrideHandler = networkManager.gameObject.AddComponent<TestPrefabOverrideHandler>();
121+
playerPrefabOverrideHandler.ServerSideInstance = m_PlayerPrefab;
122+
playerPrefabOverrideHandler.ClientSideInstance = m_ClientSidePlayerPrefab.Prefab;
123+
124+
// Add the NetworkPrefab with override
125+
networkManager.NetworkConfig.Prefabs.Add(m_PrefabOverride);
126+
// Add the client player prefab that will be used on clients (and the host)
127+
networkManager.NetworkConfig.Prefabs.Add(m_ClientSidePlayerPrefab);
128+
}
129+
130+
m_PrefabOverride.Prefab.GetComponent<NetworkObject>().IsSceneObject = false;
131+
m_PrefabOverride.SourcePrefabToOverride.GetComponent<NetworkObject>().IsSceneObject = false;
132+
m_PrefabOverride.OverridingTargetPrefab.GetComponent<NetworkObject>().IsSceneObject = false;
133+
m_ClientSidePlayerPrefab.Prefab.GetComponent<NetworkObject>().IsSceneObject = false;
134+
135+
base.OnServerAndClientsCreated();
136+
}
137+
138+
protected override IEnumerator OnTearDown()
139+
{
140+
if (m_PrefabOverride != null)
141+
{
142+
if (m_PrefabOverride.SourcePrefabToOverride)
143+
{
144+
Object.Destroy(m_PrefabOverride.SourcePrefabToOverride);
145+
}
146+
147+
if (m_PrefabOverride.OverridingTargetPrefab)
148+
{
149+
Object.Destroy(m_PrefabOverride.OverridingTargetPrefab);
150+
}
151+
}
152+
153+
if (m_ClientSidePlayerPrefab != null)
154+
{
155+
if (m_ClientSidePlayerPrefab.Prefab)
156+
{
157+
Object.Destroy(m_ClientSidePlayerPrefab.Prefab);
158+
}
159+
}
160+
m_ClientSidePlayerPrefab = null;
161+
m_PrefabOverride = null;
162+
163+
yield return base.OnTearDown();
164+
}
165+
166+
167+
private GameObject GetPlayerNetworkPrefabObject(NetworkManager networkManager)
168+
{
169+
return networkManager.IsClient ? m_ClientSidePlayerPrefab.Prefab : m_PlayerPrefab;
170+
}
171+
172+
[UnityTest]
173+
public IEnumerator PrefabOverrideTests()
174+
{
175+
var prefabNetworkObject = (NetworkObject)null;
176+
var spawnedGlobalObjectId = (uint)0;
177+
178+
var networkManagers = m_ClientNetworkManagers.ToList();
179+
if (m_UseHost)
180+
{
181+
networkManagers.Insert(0, m_ServerNetworkManager);
182+
}
183+
else
184+
{
185+
// If running as just a server, validate that all player prefab clone instances are the server side version
186+
prefabNetworkObject = GetPlayerNetworkPrefabObject(m_ServerNetworkManager).GetComponent<NetworkObject>();
187+
foreach (var playerEntry in m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId])
188+
{
189+
spawnedGlobalObjectId = playerEntry.Value.GlobalObjectIdHash;
190+
Assert.IsTrue(prefabNetworkObject.GlobalObjectIdHash == spawnedGlobalObjectId, $"Server-Side {playerEntry.Value.name} was spawned as prefab ({spawnedGlobalObjectId}) but we expected ({prefabNetworkObject.GlobalObjectIdHash})!");
191+
}
192+
}
193+
194+
// Validates prefab overrides via the NetworkPrefabHandler.
195+
// Validate the player prefab instance clones relative to all NetworkManagers.
196+
foreach (var networkManager in networkManagers)
197+
{
198+
// Get the expected player prefab to be spawned based on the NetworkManager
199+
prefabNetworkObject = GetPlayerNetworkPrefabObject(networkManager).GetComponent<NetworkObject>();
200+
if (networkManager.IsClient)
201+
{
202+
spawnedGlobalObjectId = networkManager.LocalClient.PlayerObject.GlobalObjectIdHash;
203+
Assert.IsTrue(prefabNetworkObject.GlobalObjectIdHash == spawnedGlobalObjectId, $"{networkManager.name} spawned player prefab ({spawnedGlobalObjectId}) did not match the expected one ({prefabNetworkObject.GlobalObjectIdHash})!");
204+
}
205+
206+
foreach (var playerEntry in m_PlayerNetworkObjects[networkManager.LocalClientId])
207+
{
208+
// We already checked our locally spawned player prefab above
209+
if (playerEntry.Key == networkManager.LocalClientId)
210+
{
211+
continue;
212+
}
213+
spawnedGlobalObjectId = playerEntry.Value.GlobalObjectIdHash;
214+
Assert.IsTrue(prefabNetworkObject.GlobalObjectIdHash == spawnedGlobalObjectId, $"Client-{networkManager.LocalClientId} clone of {playerEntry.Value.name} was spawned as prefab ({spawnedGlobalObjectId}) but we expected ({prefabNetworkObject.GlobalObjectIdHash})!");
215+
}
216+
}
217+
218+
// Validates prefab overrides via NetworkPrefab configuration.
219+
var spawnedInstance = (NetworkObject)null;
220+
var networkManagerOwner = m_ServerNetworkManager;
221+
222+
if (m_DistributedAuthority)
223+
{
224+
networkManagerOwner = m_ClientNetworkManagers[0];
225+
}
226+
// Clients and Host will spawn the OverridingTargetPrefab while a dedicated server will spawn the SourcePrefabToOverride
227+
var expectedServerGlobalObjectIdHash = networkManagerOwner.IsClient ? m_PrefabOverride.OverridingTargetPrefab.GetComponent<NetworkObject>().GlobalObjectIdHash : m_PrefabOverride.SourcePrefabToOverride.GetComponent<NetworkObject>().GlobalObjectIdHash;
228+
var expectedClientGlobalObjectIdHash = m_PrefabOverride.OverridingTargetPrefab.GetComponent<NetworkObject>().GlobalObjectIdHash;
229+
230+
spawnedInstance = NetworkObject.InstantiateAndSpawn(m_PrefabOverride.SourcePrefabToOverride, networkManagerOwner, networkManagerOwner.LocalClientId);
231+
var builder = new StringBuilder();
232+
bool ObjectSpawnedOnAllNetworkMangers()
233+
{
234+
builder.Clear();
235+
if (!m_ServerNetworkManager.SpawnManager.SpawnedObjects.ContainsKey(spawnedInstance.NetworkObjectId))
236+
{
237+
builder.AppendLine($"Client-{m_ServerNetworkManager.LocalClientId} failed to spawn {spawnedInstance.name}-{spawnedInstance.NetworkObjectId}!");
238+
return false;
239+
}
240+
var instanceGID = m_ServerNetworkManager.SpawnManager.SpawnedObjects[spawnedInstance.NetworkObjectId].GlobalObjectIdHash;
241+
if (instanceGID != expectedServerGlobalObjectIdHash)
242+
{
243+
builder.AppendLine($"Client-{m_ServerNetworkManager.LocalClientId} instance {spawnedInstance.name}-{spawnedInstance.NetworkObjectId} GID is {instanceGID} but was expected to be {expectedServerGlobalObjectIdHash}!");
244+
return false;
245+
}
246+
247+
foreach (var networkManger in m_ClientNetworkManagers)
248+
{
249+
if (!networkManger.SpawnManager.SpawnedObjects.ContainsKey(spawnedInstance.NetworkObjectId))
250+
{
251+
builder.AppendLine($"Client-{networkManger.LocalClientId} failed to spawn {spawnedInstance.name}-{spawnedInstance.NetworkObjectId}!");
252+
return false;
253+
}
254+
instanceGID = networkManger.SpawnManager.SpawnedObjects[spawnedInstance.NetworkObjectId].GlobalObjectIdHash;
255+
if (instanceGID != expectedClientGlobalObjectIdHash)
256+
{
257+
builder.AppendLine($"Client-{networkManger.LocalClientId} instance {spawnedInstance.name}-{spawnedInstance.NetworkObjectId} GID is {instanceGID} but was expected to be {expectedClientGlobalObjectIdHash}!");
258+
return false;
259+
}
260+
}
261+
return true;
262+
}
263+
264+
yield return WaitForConditionOrTimeOut(ObjectSpawnedOnAllNetworkMangers);
265+
AssertOnTimeout($"The spawned prefab override validation failed!\n {builder}");
266+
}
267+
}
268+
}

com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabOverrideTests.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)