Skip to content

Commit c31e7c2

Browse files
Merge branch 'experimental/v2-x-x/ngo-and-n4e' into test-merge/v2-x-x/ngo-and-n4e-with-poc-transport
2 parents 8bc8800 + ac625b2 commit c31e7c2

File tree

5 files changed

+70
-40
lines changed

5 files changed

+70
-40
lines changed

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,17 @@ namespace Unity.Netcode
77
{
88

99
/// <summary>
10-
/// TODO-UNIFIED: Would need to be reviewed for alternate ways of handling this.
10+
/// TODO-UNIFIED: Needs further peer review and exploring alternate ways of handling this.
1111
/// </summary>
1212
/// <remarks>
1313
/// If used, we most likely would make this internal
1414
/// </remarks>
1515
public partial class NetworkObjectBridge : GhostBehaviour
1616
{
17-
public Action<ulong> NetworkObjectIdChanged;
18-
17+
/// <summary>
18+
/// This is used to link <see cref="NetworkObject.SerializedObject"/> data to
19+
/// N4E-spawned hybrid prefab instances.
20+
/// </summary>
1921
internal GhostField<ulong> NetworkObjectId = new GhostField<ulong>();
2022

2123
public void SetNetworkObjectId(ulong value)

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

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ public struct NetcodeConnection
1313
internal Entity Entity;
1414
public int NetworkId;
1515

16-
internal float ConnectedTime;
17-
internal bool IsSynced;
18-
1916
public bool IsServer => World.IsServer();
2017
public void GoInGame()
2118
{
@@ -48,6 +45,8 @@ protected override void OnUpdate()
4845
{
4946
var isServer = World.IsServer();
5047
var commandBuffer = new EntityCommandBuffer(Allocator.Temp);
48+
var networkManager = NetworkManager.Singleton;
49+
5150
foreach (var (networkId, connectionState, entity) in SystemAPI.Query<NetworkId, ConnectionState>().WithNone<NetworkStreamConnection>().WithEntityAccess())
5251
{
5352
commandBuffer.RemoveComponent<ConnectionState>(entity);
@@ -60,16 +59,12 @@ protected override void OnUpdate()
6059

6160
m_TempConnections.Clear();
6261

63-
62+
// TODO: We should figure out how to associate the N4E NetworkId with the NGO ClientId
6463
foreach (var (networkId, entity) in SystemAPI.Query<NetworkId>().WithAll<NetworkStreamConnection>().WithNone<NetworkStreamInGame>().WithEntityAccess())
6564
{
66-
// TODO-Unified: For new connections, we have a delay before the N4E in-game state for the client to provide time for the NGO side of the client to synchronize.
67-
// Note: Once both are using the same transport we should be able to get the transport id and determine the NGO assigned client-id and at that point once the
68-
// client has signaled that it has synchronized (or has been sent the synchronization data) we finalize the in-game connection state (or something along those lines).
6965
if (!m_NewConnections.ContainsKey(networkId.Value))
7066
{
71-
var delayTime = 0.0f;// isServer ? 0.2f : 0.1f;
72-
var newConnection = new NetcodeConnection { World = World, Entity = entity, NetworkId = networkId.Value, ConnectedTime = UnityEngine.Time.realtimeSinceStartup + delayTime, IsSynced = isServer};
67+
var newConnection = new NetcodeConnection { World = World, Entity = entity, NetworkId = networkId.Value };
7368
m_NewConnections.Add(networkId.Value, newConnection);
7469
}
7570
}
@@ -79,8 +74,9 @@ protected override void OnUpdate()
7974
{
8075
foreach (var entry in m_NewConnections)
8176
{
82-
// Check if the delay time has passed.
83-
if (entry.Value.IsSynced && entry.Value.ConnectedTime < UnityEngine.Time.realtimeSinceStartup)
77+
// Server: always connect
78+
// Client: wait until we have synchronized before announcing we are ready to receive snapshots
79+
if (networkManager.IsServer || (!networkManager.IsServer && networkManager.IsConnectedClient))
8480
{
8581
// Set the connection in-game
8682
commandBuffer.AddComponent<NetworkStreamInGame>(entry.Value.Entity);
@@ -97,21 +93,29 @@ protected override void OnUpdate()
9793
}
9894
m_TempConnections.Clear();
9995

96+
// If the local NetworkManager is shutting down or no longer connected, then
97+
// make sure we have disconnected all known connections.
98+
if (networkManager.ShutdownInProgress || !networkManager.IsListening)
99+
{
100+
foreach (var (networkId, entity) in SystemAPI.Query<NetworkId>().WithEntityAccess())
101+
{
102+
commandBuffer.RemoveComponent<ConnectionState>(entity);
103+
NetworkManager.OnNetCodeDisconnect?.Invoke(new NetcodeConnection { World = World, Entity = entity, NetworkId = networkId.Value });
104+
}
105+
}
100106
commandBuffer.Playback(EntityManager);
101107
}
102108

109+
/// <summary>
110+
/// Always disconnect all known connections when being destroyed.
111+
/// </summary>
103112
protected override void OnDestroy()
104113
{
105114
var commandBuffer = new EntityCommandBuffer(Allocator.Temp);
106115
foreach (var (networkId, entity) in SystemAPI.Query<NetworkId>().WithEntityAccess())
107116
{
108117
commandBuffer.RemoveComponent<ConnectionState>(entity);
109-
// TODO: maybe disconnect reason?
110-
m_TempConnections.Add(new NetcodeConnection { World = World, Entity = entity, NetworkId = networkId.Value });
111-
}
112-
foreach (var con in m_TempConnections)
113-
{
114-
NetworkManager.OnNetCodeDisconnect?.Invoke(con);
118+
NetworkManager.OnNetCodeDisconnect?.Invoke(new NetcodeConnection { World = World, Entity = entity, NetworkId = networkId.Value });
115119
}
116120
commandBuffer.Playback(EntityManager);
117121
base.OnDestroy();

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

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1732,8 +1732,15 @@ private void OnDestroy()
17321732
return;
17331733
}
17341734

1735+
var spawnManager = NetworkManager.SpawnManager;
1736+
17351737
// Always attempt to remove from scene changed updates
1736-
networkManager.SpawnManager?.RemoveNetworkObjectFromSceneChangedUpdates(this);
1738+
spawnManager?.RemoveNetworkObjectFromSceneChangedUpdates(this);
1739+
1740+
#if UNIFIED_NETCODE
1741+
spawnManager?.GhostsPendingSpawn.Remove(NetworkObjectId);
1742+
spawnManager?.GhostsPendingSynchronization.Remove(NetworkObjectId);
1743+
#endif
17371744

17381745
if (IsSpawned && !networkManager.ShutdownInProgress)
17391746
{
@@ -1763,11 +1770,11 @@ private void OnDestroy()
17631770
}
17641771
}
17651772

1766-
if (networkManager.SpawnManager != null && networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject))
1773+
if (spawnManager != null && spawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject))
17671774
{
17681775
if (this == networkObject)
17691776
{
1770-
networkManager.SpawnManager.OnDespawnObject(networkObject, false);
1777+
spawnManager.OnDespawnObject(networkObject, false);
17711778
}
17721779
}
17731780
}
@@ -3846,7 +3853,6 @@ private void InitGhost()
38463853
{
38473854
Debug.Log($"[{nameof(NetworkObject)}] GhostBridge {name} detected and instantiated.");
38483855
}
3849-
NetworkObjectBridge.NetworkObjectIdChanged += OnNetworkObjectIdChanged;
38503856
if (NetworkObjectBridge.NetworkObjectId.Value != 0)
38513857
{
38523858
RegisterGhostBridge();
@@ -3866,11 +3872,6 @@ internal void RegisterGhostBridge()
38663872
NetworkManager.SpawnManager.RegisterGhostPendingSpawn(this, NetworkObjectBridge.NetworkObjectId.Value);
38673873
}
38683874
}
3869-
3870-
private void OnNetworkObjectIdChanged(ulong networkObjectId)
3871-
{
3872-
RegisterGhostBridge();
3873-
}
38743875
#endif
38753876

38763877
/// <summary>

com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2740,8 +2740,14 @@ internal void PopulateScenePlacedObjects(Scene sceneToFilterBy, bool clearSceneP
27402740
var globalObjectIdHash = networkObjectInstance.GlobalObjectIdHash;
27412741
var sceneHandle = networkObjectInstance.gameObject.scene.handle;
27422742
// We check to make sure the NetworkManager instance is the same one to be "NetcodeIntegrationTestHelpers" compatible and filter the list on a per scene basis (for additive scenes)
2743+
#if UNIFIED_NETCODE
2744+
if (!networkObjectInstance.HasGhost && networkObjectInstance.IsSceneObject != false && (networkObjectInstance.NetworkManager == NetworkManager ||
2745+
networkObjectInstance.NetworkManagerOwner == null) && sceneHandle == sceneToFilterBy.handle)
2746+
#else
27432747
if (networkObjectInstance.IsSceneObject != false && (networkObjectInstance.NetworkManager == NetworkManager ||
27442748
networkObjectInstance.NetworkManagerOwner == null) && sceneHandle == sceneToFilterBy.handle)
2749+
2750+
#endif
27452751
{
27462752
if (!ScenePlacedObjects.ContainsKey(globalObjectIdHash))
27472753
{

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

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,35 @@ internal void RegisterGhostPendingSpawn(NetworkObject networkObject, ulong netwo
4343
if (GhostsPendingSpawn.TryAdd(networkObjectId, networkObject))
4444
{
4545
// TODO-UNIFIED: We need a better way to preserve any hybrid instances pending NGO spawn.
46-
// For now, move any pending object into the DDOL.
47-
UnityEngine.Object.DontDestroyOnLoad(networkObject.gameObject);
46+
// Edge-Case scenario: During initial client synchronization (i.e. !NetworkManager.IsConnectedClient).
47+
//
48+
// Description: A client can receive snapshots before finishing the NGO synchronization process.
49+
// This is when an edge case scenario can happen where the initial NGO synchronization information
50+
// can include new scenes to load. If one of those scenes is configured to load in SingleMode, then
51+
// any instantiated ghosts pending synchronization would be instantiated in whatever the currently
52+
// active scene was when the client was processing the synchronization data. If the ghosts pending
53+
// synchrpnization are in the currently active scene when the new scene is loaded in SingleMode, then
54+
// they would be destroyed.
55+
//
56+
// Current Fix:
57+
// If the client is not yet synchronized, then any ghost pending spawn get migrated into the DDOL.
58+
//
59+
// Further review:
60+
// We need to make sure that we are migrating NetworkObjects into their assigned scene (if scene
61+
// management is enabled). Currently, we assume all instances were in the DDOL and just migrate
62+
// them into the currently active scene upon spawn.
63+
if (!NetworkManager.IsConnectedClient && !GhostsPendingSynchronization.ContainsKey(networkObjectId))
64+
{
65+
UnityEngine.Object.DontDestroyOnLoad(networkObject.gameObject);
66+
}
67+
else // There is matching spawn data for this pending Ghost, process the pending spawn for this hybrid instance.
68+
{
69+
NetworkManager.DeferredMessageManager.ProcessTriggers(IDeferredNetworkMessageManager.TriggerType.OnGhostSpawned, networkObjectId);
70+
}
4871
}
49-
50-
NetworkManager.DeferredMessageManager.ProcessTriggers(IDeferredNetworkMessageManager.TriggerType.OnGhostSpawned, networkObjectId);
51-
if (GhostsArePendingSynchronization && GhostsPendingSynchronization.ContainsKey(networkObjectId))
72+
else
5273
{
53-
// TODO-UNIFIED: We need a better way to preserve any hybrid instances pending NGO spawn.
54-
// NOTE: We might be able to use the NetworkSceneHandle to get the associated local scene handle to which we can use to get the targeted scene.
55-
UnityEngine.SceneManagement.SceneManager.MoveGameObjectToScene(networkObject.gameObject, UnityEngine.SceneManagement.SceneManager.GetActiveScene());
56-
57-
// When the object is spawned, it will invoke GetGhostNetworkObjectForSpawn below which removes the entry from GhostsPendingSpawn
58-
ProcessGhostPendingSynchronization(networkObjectId);
74+
Debug.LogError($"[{networkObject.name}-{networkObjectId}] Has already been registered as a pending ghost!");
5975
}
6076
}
6177

@@ -109,6 +125,7 @@ internal void ProcessGhostPendingSynchronization(ulong networkObjectId, bool rem
109125
//}
110126
if (removeUponSpawn)
111127
{
128+
GhostsPendingSynchronization.Remove(networkObjectId);
112129
GhostsArePendingSynchronization = GhostsPendingSynchronization.Count > 0;
113130
ghostPendingSynch.Buffer.Dispose();
114131
}

0 commit comments

Comments
 (0)