Skip to content

Commit 88eaa08

Browse files
fix: distributed authority spawning prefab overrides (#3160)
* fix All clients in distributed authority should invoke CheckForGlobalObjectIdHashOverride when spawning a NetworkPrefab since each client has the authority to spawn (i.e. each should be treated like a host/server in regards to spawning and prefab overrides). * update adding changelog entry. * update adding changelog PR number. * test updating existing test to run through with distributed authority and client-server network topologies. (still need one more integration test to validate the final spawned object on the non-authority sides) * style removing whitespace * fix Fixed issue with server only NetworkManager instance spawning the network prefab itself as opposed to creating an instance. Extracted the instantiation portion of a network prefab into an internal method that can be used for integration testing prefab instantiation and potentially other edge case scenarios. * 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`. * style correcting some typos in comments
1 parent f170341 commit 88eaa08

11 files changed

+351
-19
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Additional documentation and release notes are available at [Multiplayer Documen
1515

1616
### Fixed
1717

18+
- Fixed issue where a sever only `NetworkManager` instance would spawn the actual `NetworkPrefab`'s `GameObject` as opposed to creating an instance of it. (#3160)
19+
- Fixed issue where only the session owner (as opposed to all clients) would handle spawning prefab overrides properly when using a distributed authority network topology. (#3160)
1820
- Fixed issue where an exception was thrown when calling `NetworkManager.Shutdown` after calling `UnityTransport.Shutdown`. (#3118)
1921
- Fixed issue where `NetworkList` properties on in-scene placed `NetworkObject`s could cause small memory leaks when entering playmode. (#3147)
2022
- Fixed in-scene `NertworkObject` synchronization issue when loading a scene with currently connected clients connected to a session created by a `NetworkManager` started as a server (i.e. not as a host). (#3133)

com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3246,14 +3246,15 @@ internal void UpdateForSceneChanges()
32463246
}
32473247

32483248
/// <summary>
3249-
/// Only applies to Hosts or session owners (for now)
3249+
/// Client-Server: Only applies to spawn authority (i.e. Server)
3250+
/// Distributed Authority: Applies to all clients since they all have spawn authority.
32503251
/// Will return the registered source NetworkPrefab's GlobalObjectIdHash if one exists.
32513252
/// Server and Clients will always return the NetworkObject's GlobalObjectIdHash.
32523253
/// </summary>
32533254
/// <returns>appropriate hash value</returns>
32543255
internal uint CheckForGlobalObjectIdHashOverride()
32553256
{
3256-
if (NetworkManager.IsServer || (NetworkManager.DistributedAuthorityMode && NetworkManager.LocalClient.IsSessionOwner))
3257+
if (NetworkManager.IsServer || NetworkManager.DistributedAuthorityMode)
32573258
{
32583259
if (NetworkManager.PrefabHandler.ContainsHandler(this))
32593260
{

com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -736,12 +736,20 @@ public NetworkObject InstantiateAndSpawn(NetworkObject networkPrefab, ulong owne
736736
internal NetworkObject InstantiateAndSpawnNoParameterChecks(NetworkObject networkPrefab, ulong ownerClientId = NetworkManager.ServerClientId, bool destroyWithScene = false, bool isPlayerObject = false, bool forceOverride = false, Vector3 position = default, Quaternion rotation = default)
737737
{
738738
var networkObject = networkPrefab;
739-
// Host spawns the ovveride and server spawns the original prefab unless forceOverride is set to true where both server or host will spawn the override.
740-
// In distributed authority mode, we alaways get the override
741-
if (forceOverride || NetworkManager.IsHost || NetworkManager.DistributedAuthorityMode)
739+
// - Host and clients always instantiate the override if one exists.
740+
// - Server instantiates the original prefab unless:
741+
// -- forceOverride is set to true =or=
742+
// -- The prefab has a registered prefab handler, then we let user code determine what to spawn.
743+
// - Distributed authority mode always spawns the override if one exists.
744+
if (forceOverride || NetworkManager.IsClient || NetworkManager.DistributedAuthorityMode || NetworkManager.PrefabHandler.ContainsHandler(networkPrefab.GlobalObjectIdHash))
742745
{
743746
networkObject = GetNetworkObjectToSpawn(networkPrefab.GlobalObjectIdHash, ownerClientId, position, rotation);
744747
}
748+
else // Under this case, server instantiate the prefab passed in.
749+
{
750+
networkObject = InstantiateNetworkPrefab(networkPrefab.gameObject, networkPrefab.GlobalObjectIdHash, position, rotation);
751+
}
752+
745753
if (networkObject == null)
746754
{
747755
Debug.LogError($"Failed to instantiate and spawn {networkPrefab.name}!");
@@ -824,16 +832,37 @@ internal NetworkObject GetNetworkObjectToSpawn(uint globalObjectIdHash, ulong ow
824832
else
825833
{
826834
// Create prefab instance while applying any pre-assigned position and rotation values
827-
networkObject = UnityEngine.Object.Instantiate(networkPrefabReference).GetComponent<NetworkObject>();
828-
networkObject.transform.position = position ?? networkObject.transform.position;
829-
networkObject.transform.rotation = rotation ?? networkObject.transform.rotation;
830-
networkObject.NetworkManagerOwner = NetworkManager;
831-
networkObject.PrefabGlobalObjectIdHash = globalObjectIdHash;
835+
networkObject = InstantiateNetworkPrefab(networkPrefabReference, globalObjectIdHash, position, rotation);
832836
}
833837
}
834838
return networkObject;
835839
}
836840

841+
/// <summary>
842+
/// Instantiates a network prefab instance, assigns the base prefab <see cref="NetworkObject.GlobalObjectIdHash"/>, positions, and orients
843+
/// the instance.
844+
/// !!! Should only be invoked by <see cref="GetNetworkObjectToSpawn"/> unless used by an integration test !!!
845+
/// </summary>
846+
/// <remarks>
847+
/// <param name="prefabGlobalObjectIdHash"> should be the base prefab <see cref="NetworkObject.GlobalObjectIdHash"/> value and not the
848+
/// overrided value.
849+
/// (Can be used for integration testing)
850+
/// </remarks>
851+
/// <param name="networkPrefab">prefab to instantiate</param>
852+
/// <param name="prefabGlobalObjectIdHash"><see cref="NetworkObject.GlobalObjectIdHash"/> of the base prefab instance</param>
853+
/// <param name="position">conditional position in place of the network prefab's default position</param>
854+
/// <param name="rotation">conditional rotation in place of the network prefab's default rotation</param>
855+
/// <returns>the instance of the <see cref="NetworkObject"/></returns>
856+
internal NetworkObject InstantiateNetworkPrefab(GameObject networkPrefab, uint prefabGlobalObjectIdHash, Vector3? position, Quaternion? rotation)
857+
{
858+
var networkObject = UnityEngine.Object.Instantiate(networkPrefab).GetComponent<NetworkObject>();
859+
networkObject.transform.position = position ?? networkObject.transform.position;
860+
networkObject.transform.rotation = rotation ?? networkObject.transform.rotation;
861+
networkObject.NetworkManagerOwner = NetworkManager;
862+
networkObject.PrefabGlobalObjectIdHash = prefabGlobalObjectIdHash;
863+
return networkObject;
864+
}
865+
837866
/// <summary>
838867
/// Creates a local NetowrkObject to be spawned.
839868
/// </summary>

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.

com.unity.netcode.gameobjects/Tests/Runtime/NetworkPrefabHandlerTests.cs renamed to com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerTests.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,14 @@ public void NetworkConfigInvalidNetworkPrefabTest()
8585
private const string k_PrefabObjectName = "NetworkPrefabHandlerTestObject";
8686

8787
[Test]
88-
public void NetworkPrefabHandlerClass()
88+
public void NetworkPrefabHandlerClass([Values] bool distributedAuthority)
8989
{
90-
Assert.IsTrue(NetworkManagerHelper.StartNetworkManager(out _));
90+
var networkConfig = new NetworkConfig()
91+
{
92+
NetworkTopology = distributedAuthority ? NetworkTopologyTypes.DistributedAuthority : NetworkTopologyTypes.ClientServer,
93+
};
94+
95+
Assert.IsTrue(NetworkManagerHelper.StartNetworkManager(out _, networkConfig: networkConfig));
9196
var testPrefabObjectName = k_PrefabObjectName;
9297

9398
Guid baseObjectID = NetworkManagerHelper.AddGameNetworkObject(testPrefabObjectName);

0 commit comments

Comments
 (0)