Skip to content

Commit 4308345

Browse files
update
Some potential fixes (etc) for hybrid spawn integration testing.
1 parent 0cc6391 commit 4308345

8 files changed

Lines changed: 217 additions & 84 deletions

File tree

com.unity.netcode.gameobjects/Runtime/Components/Helpers/NetworkObjectBridge.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ private void OnValidate()
4848
/// N4E-spawned hybrid prefab instances.
4949
/// </summary>
5050
internal GhostField<ulong> NetworkObjectId = new GhostField<ulong>();
51-
52-
public void SetNetworkObjectId(ulong value)
51+
public void SetNetworkObjectId(ulong networkObjectId)
5352
{
54-
NetworkObjectId.Value = value;
53+
NetworkObjectId.PresetValue(networkObjectId);
54+
NetworkObjectId.Value = networkObjectId;
5555
}
5656
}
5757
#endif
@@ -70,7 +70,7 @@ internal class UnifiedBootStrap : ClientServerBootstrap
7070

7171
public static World LastCreatedWorld { get; private set; }
7272

73-
private static int WorldCounter = 0;
73+
private static int s_WorldCounter = 0;
7474

7575
public override bool Initialize(string defaultWorldName)
7676
{
@@ -83,26 +83,28 @@ public override bool Initialize(string defaultWorldName)
8383
AutoConnectPort = Port;
8484
if (base.Initialize(defaultWorldName))
8585
{
86-
UnityEngine.Debug.LogError($"[{nameof(UnifiedBootStrap)}] Auto-bootstrap is enabled!!! This will break the POC!");
86+
Debug.LogError($"[{nameof(UnifiedBootStrap)}] Auto-bootstrap is enabled!!! This will break the POC!");
8787
return true;
8888
}
8989

9090
if (networkManager != null)
9191
{
9292
Debug.Log($"Starting a world for {(networkManager.IsServer ? "Host" : "Client")}");
93-
LastCreatedWorld = networkManager.IsServer
94-
? CreateSingleWorldHost($"ClientAndServerWorld {WorldCounter++}")
95-
: CreateClientWorld($"ClientWorld {WorldCounter++}");
93+
s_WorldCounter++;
94+
LastCreatedWorld = networkManager.IsServer ? CreateSingleWorldHost($"HostSingleWorld-{s_WorldCounter}")
95+
: CreateClientWorld($"ClientWorld-{s_WorldCounter}");
9696

9797
if (LastCreatedWorld == null)
9898
{
99-
UnityEngine.Debug.LogError($"[{nameof(UnifiedBootStrap)}] World is null!");
99+
s_WorldCounter--;
100+
Debug.LogError($"[{nameof(UnifiedBootStrap)}] World is null!");
100101
return false;
101102
}
102103

103104
if (!LastCreatedWorld.IsCreated)
104105
{
105-
UnityEngine.Debug.LogError($"[{nameof(UnifiedBootStrap)}] World was not created!");
106+
s_WorldCounter--;
107+
Debug.LogError($"[{nameof(UnifiedBootStrap)}] World was not created!");
106108
return false;
107109
}
108110

com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66
using System.Runtime.CompilerServices;
77
using Unity.Collections;
88
using Unity.Collections.LowLevel.Unsafe;
9-
#if UNIFIED_NETCODE
10-
using Unity.Netcode.Unified;
11-
#endif
129
using Unity.Profiling;
1310
using UnityEngine;
1411
using Debug = UnityEngine.Debug;

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

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3852,9 +3852,13 @@ private void OnEnable()
38523852
private void OnDisable()
38533853
{
38543854
Debug.Log("Disabled!");
3855-
if (IsSpawned)
3855+
if (IsSpawned || HasGhost)
38563856
{
3857-
enabled = true;
3857+
if (HasGhost && GhostAdapter.IsPrefab())
3858+
{
3859+
return;
3860+
}
3861+
gameObject.SetActive(true);
38583862
}
38593863

38603864
try
@@ -3870,13 +3874,6 @@ private void OnDisable()
38703874

38713875
private void Start()
38723876
{
3873-
// TODO-UNIFIED: Remove once the prefab registration is in place.
3874-
if (!enabled)
3875-
{
3876-
Debug.LogWarning($"[{nameof(NetworkObject)}][{name}] Was not enabled on start! Enabling.");
3877-
enabled = true;
3878-
}
3879-
38803877
InitGhost();
38813878
}
38823879
[SerializeField]
@@ -3892,7 +3889,7 @@ private void InitGhost()
38923889
{
38933890
Debug.Log($"[{nameof(NetworkObject)}] GhostBridge {name} detected and instantiated.");
38943891
}
3895-
if (NetworkObjectBridge.NetworkObjectId.Value != 0)
3892+
if (GhostAdapter.WasInitialized && NetworkObjectBridge.NetworkObjectId.Value != 0)
38963893
{
38973894
RegisterGhostBridge();
38983895
}
@@ -3904,9 +3901,23 @@ internal void RegisterGhostBridge()
39043901
if (NetworkManager.LogLevel == LogLevel.Developer)
39053902
{
39063903
Debug.Log($"[{nameof(NetworkObject)}][{nameof(NetworkObjectId)}] NetworkObjectBridge notified instance exists with assigned ID of: {NetworkObjectBridge.NetworkObjectId.Value}");
3904+
if (!NetworkManager.IsListening)
3905+
{
3906+
Debug.LogWarning($"[{nameof(NetworkObject)}] Did not register because there is no session in progress!");
3907+
return;
3908+
}
39073909
}
39083910

3909-
if (!NetworkManager.IsServer)
3911+
// Set when running through integration tests in order to initially bypass the
3912+
// normal registration. This is because at this point in the instantiation process,
3913+
// NetworkObject's NetworkManager is pointing to the singleton which means all instances
3914+
// (even if intended to be for a specific client) will end up registering with whichever
3915+
// NetworkManager instance is being pointed to by the singleton.
3916+
if (NetworkSpawnManager.RegisterPendingGhost != null)
3917+
{
3918+
NetworkSpawnManager.RegisterPendingGhost(this, NetworkObjectBridge.NetworkObjectId.Value);
3919+
}
3920+
else if (!NetworkManager.IsServer)
39103921
{
39113922
NetworkManager.SpawnManager.RegisterGhostPendingSpawn(this, NetworkObjectBridge.NetworkObjectId.Value);
39123923
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int
131131
{
132132
if (networkManager.LogLevel == LogLevel.Developer)
133133
{
134-
UnityEngine.Debug.Log($"Deferring {nameof(CreateObjectMessage)} to wait for Ghost.");
134+
UnityEngine.Debug.Log($"[{nameof(NetworkObject)}-{ObjectInfo.NetworkObjectId}] Deferring {nameof(CreateObjectMessage)} to wait for Ghost.");
135135
}
136136
networkManager.DeferredMessageManager.DeferMessage(IDeferredNetworkMessageManager.TriggerType.OnGhostSpawned, ObjectInfo.NetworkObjectId, reader, ref context, k_Name);
137137
return false;

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
using System.Linq;
55
using System.Runtime.CompilerServices;
66
using System.Text;
7+
using Unity.Entities;
8+
using Unity.NetCode;
79
using UnityEngine;
810

911
namespace Unity.Netcode
@@ -35,6 +37,13 @@ public class NetworkSpawnManager
3537

3638
internal readonly Dictionary<ulong, NetworkObject> GhostsPendingSpawn = new Dictionary<ulong, NetworkObject>();
3739

40+
// TODO: We might want to make this a mock interfacebut temporary solution to validate
41+
// the need to assure we are registering with the right NetworkManager instance when testing (everything
42+
// will use the singleton during Awake and Start when we need to register).
43+
internal delegate void RegisterPendingGhostDelegateHandler(NetworkObject networkObject, ulong networkObjectId);
44+
45+
internal static RegisterPendingGhostDelegateHandler RegisterPendingGhost;
46+
3847
internal void RegisterGhostPendingSpawn(NetworkObject networkObject, ulong networkObjectId)
3948
{
4049
if (NetworkManager.LogLevel == LogLevel.Developer)
@@ -43,6 +52,7 @@ internal void RegisterGhostPendingSpawn(NetworkObject networkObject, ulong netwo
4352
}
4453
if (GhostsPendingSpawn.TryAdd(networkObjectId, networkObject))
4554
{
55+
// TODO-REVIEW-BELOW: *** This is very likely no longer an issue with the new connection sequence ***
4656
// TODO-UNIFIED: We need a better way to preserve any hybrid instances pending NGO spawn.
4757
// Edge-Case scenario: During initial client synchronization (i.e. !NetworkManager.IsConnectedClient).
4858
//

com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,8 +427,29 @@ public override void DisconnectRemoteClient(ulong clientId)
427427

428428
public override void DisconnectLocalClient()
429429
{
430-
m_NetworkManager.NetcodeWorld.RequestDisconnectFromServer();
430+
// Remove the connection 1st (the world might not be available)
431431
m_Connections.Remove((int)ServerClientId);
432+
433+
// TODO-FIX-REVIEW-ME:
434+
// This was causing errors to occur upon shutdown during an integration test.
435+
// The cases being trapped for below yield no errors, but there might be some
436+
// form of other underlying issue here:
437+
438+
if (m_NetworkManager.NetcodeWorld == null || !m_NetworkManager.NetcodeWorld.IsCreated)
439+
{
440+
return;
441+
}
442+
443+
if (m_NetworkManager.IsServer || m_NetworkManager.NetcodeWorld.IsHost())
444+
{
445+
if (m_NetworkManager.LogLevel <= LogLevel.Developer)
446+
{
447+
Debug.LogWarning("Host is attempting to shutdown the local client which is not required with a single world host.");
448+
}
449+
return;
450+
}
451+
m_NetworkManager.NetcodeWorld.RequestDisconnectFromServer();
452+
432453
}
433454

434455
public override ulong GetCurrentRtt(ulong clientId)
Lines changed: 69 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
using System.Collections;
2-
using System.Collections.Generic;
32
using NUnit.Framework;
4-
using Unity.NetCode;
53
using Unity.Netcode.Components;
64
using Unity.Netcode.TestHelpers.Runtime;
5+
using Unity.NetCode;
76
using UnityEngine;
87
using UnityEngine.TestTools;
98

9+
1010
namespace Unity.Netcode.RuntimeTests
1111
{
1212
/// <summary>
1313
/// Test class that deliberately removes some functionality from NetworkTransform that is conditionally disabled
1414
/// by the presence of ghost objects in the base class. This is to help be certain that the network transform
1515
/// is not doing the work, but that the work is being done by N4E's snapshots.
1616
/// </summary>
17-
public class DoNothingNetworkTransform : NetworkTransform
17+
internal class DoNothingNetworkTransform : NetworkTransform
1818
{
1919
public override void OnNetworkSpawn()
2020
{
@@ -26,53 +26,93 @@ internal override void InternalInitialization(bool isOwnershipChange = false)
2626
// Deliberately left empty
2727
}
2828
}
29-
30-
public class UnifiedNetworkTransformTest : IntegrationTestWithApproximation
29+
30+
internal class UnifiedNetworkTransformTest : IntegrationTestWithApproximation
3131
{
3232
protected override int NumberOfClients => 2;
3333

3434
private GameObject m_Prefab;
3535
private NetworkObject m_Instance;
3636

37-
protected override void OnServerAndClientsCreated()
37+
protected override IEnumerator OnSetup()
3838
{
39-
m_Prefab = CreateNetworkObjectPrefab("Test prefab");
40-
SetupGhostAdapterForNetworkObjectPrefab(ref m_Prefab);
41-
39+
// Creates the hybrid prefab
40+
m_Prefab = CreateHybridPrefab("HybridPrefab", true);
4241
m_Prefab.AddComponent<DoNothingNetworkTransform>();
42+
NetworkSpawnManager.RegisterPendingGhost = RegisterPendingGhost;
43+
return base.OnSetup();
44+
}
4345

44-
/*NetCode.Netcode.RunOnServerStarted(() =>
45-
{
46-
NetCode.Netcode.RegisterPrefabSingleWorld(m_PlayerPrefab, true);
47-
});*/
46+
protected override IEnumerator OnTearDown()
47+
{
48+
NetworkSpawnManager.RegisterPendingGhost = null;
49+
m_EnableVerboseDebug = false;
50+
return base.OnTearDown();
4851
}
4952

50-
protected override IEnumerator OnServerAndClientsConnected()
53+
private void RegisterPendingGhost(NetworkObject networkObject, ulong networkObjectId)
5154
{
52-
m_Instance = SpawnObject(m_Prefab, m_ServerNetworkManager).GetComponent<NetworkObject>();
53-
yield return WaitForConditionOrTimeOut(() =>
55+
var ghost = networkObject.GetComponent<GhostAdapter>();
56+
Assert.IsNotNull(ghost, $"[RegisterPendingGhost][NetworkObject-{networkObjectId}] Has no {nameof(GhostAdapter)}!");
57+
foreach (var networkManager in m_NetworkManagers)
5458
{
55-
foreach (var client in m_ClientNetworkManagers)
59+
// If the world matches, then register the instance with this NetworkManager's spawn manager.
60+
if (networkManager.NetcodeWorld == ghost.World)
5661
{
57-
if (!s_GlobalNetworkObjects.ContainsKey(client.LocalClientId) || !s_GlobalNetworkObjects[client.LocalClientId].ContainsKey(m_Instance.NetworkObjectId))
58-
{
59-
return false;
60-
}
62+
networkManager.SpawnManager.RegisterGhostPendingSpawn(networkObject, networkObjectId);
63+
return;
6164
}
65+
}
66+
Debug.LogError($"Did not find a world for NetworkObject-{networkObjectId}!!");
67+
}
68+
69+
protected override void OnServerAndClientsCreated()
70+
{
6271

63-
return true;
64-
});
65-
AssertOnTimeout($"Timed out waiting for objects to spawn!");
66-
yield return null;
72+
// Add the hybrid prefab to the prefabs list for
73+
// all NetworkManager instances.
74+
// TODO: Emma and I discussed actually not making it
75+
// a requirement to have NetworkManager instances.
76+
// We can get that PR landed and merged back into the
77+
// unified branch so this is no longer needed.
78+
// (We can modify CreateHybridPrefab to use whatever list
79+
// is used to handle this when using the normal prefab creation
80+
// methods).
81+
var networkPrefab = new NetworkPrefab()
82+
{
83+
Prefab = m_Prefab,
84+
};
85+
foreach (var networkManager in m_NetworkManagers)
86+
{
87+
networkManager.LogLevel = LogLevel.Developer;
88+
networkManager.NetworkConfig.Prefabs.Add(networkPrefab);
89+
// Set the deferred message timeout to be 5 seconds for this test.
90+
// (To see if the messages for the instances ever get processed.)
91+
// Enable this to debug deferred
92+
//networkManager.NetworkConfig.SpawnTimeout = 5;
93+
}
6794
}
6895

6996
[UnityTest]
7097
public IEnumerator BasicMovementTest()
7198
{
99+
m_EnableVerboseDebug = true;
72100
var authority = GetAuthorityNetworkManager();
101+
m_Instance = SpawnObject(m_Prefab, m_ServerNetworkManager).GetComponent<NetworkObject>();
102+
103+
// Wait 5 seconds so we will dump any deferred messages if it failed on clients
104+
// when checking to see if it spawned or not on the clients next.
105+
// Enable this to debug deferred
106+
//yield return new WaitForSeconds(5);
107+
108+
yield return WaitForSpawnedOnAllOrTimeOut(m_Instance);
109+
AssertOnTimeout($"Failed to spawn {m_Instance.name} on all clients!");
110+
111+
VerboseDebug("All clients spawned instance!");
112+
73113
var originalPos = authority.LocalClient.PlayerObject.transform.position;
74114
var newPos = originalPos + new Vector3(1, 1, 1);
75-
115+
76116
m_Instance.transform.position = newPos;
77117

78118
foreach (var client in m_ClientNetworkManagers)
@@ -81,11 +121,12 @@ public IEnumerator BasicMovementTest()
81121
}
82122

83123
yield return new WaitForSeconds(1);
84-
124+
85125
foreach (var client in m_ClientNetworkManagers)
86126
{
87127
Assert.IsTrue(Approximately(newPos, s_GlobalNetworkObjects[client.LocalClientId][m_Instance.NetworkObjectId].transform.position));
88128
}
129+
VerboseDebug("Test Passed!");
89130
}
90131
}
91-
}
132+
}

0 commit comments

Comments
 (0)