Skip to content

Commit 83e3c62

Browse files
update
Getting unified working with the initial synchronization when scene management is enabled.
1 parent c15498c commit 83e3c62

4 files changed

Lines changed: 194 additions & 7 deletions

File tree

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

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,67 @@ public void SetNetworkObjectId(ulong value)
1818
}
1919
public override void Awake()
2020
{
21-
base.Awake();
22-
NetworkObjectId.ValueChanged += OnNetworkObjectIdChanged;
23-
}
21+
if (UnifiedBootStrap.Instance != null)
22+
{
23+
Initialize(true);
24+
}
25+
else
26+
{
27+
UnifiedBootStrap.OnInitialized += Initialize;
28+
}
29+
}
30+
31+
private void Initialize(bool initialized)
32+
{
33+
UnifiedBootStrap.OnInitialized -= Initialize;
34+
if (gameObject != null)
35+
{
36+
base.Awake();
37+
NetworkObjectId.ValueChanged += OnNetworkObjectIdChanged;
38+
}
39+
}
2440

2541
private void OnNetworkObjectIdChanged(ulong value)
2642
{
2743
NetworkObjectIdChanged?.Invoke(value);
2844
}
45+
46+
internal void OnDespawn(bool shouldDestroy)
47+
{
48+
if (shouldDestroy)
49+
{
50+
UnifiedBootStrap.OnInitialized -= Initialize;
51+
}
52+
}
53+
54+
public override void OnDestroy()
55+
{
56+
UnifiedBootStrap.OnInitialized -= Initialize;
57+
base.OnDestroy();
58+
}
59+
}
60+
61+
internal class UnifiedBootStrap : ClientServerBootstrap
62+
{
63+
public static UnifiedBootStrap Instance { get; private set; }
64+
public static Action<bool> OnInitialized;
65+
public static ushort Port = 7979;
66+
67+
public override bool Initialize(string defaultWorldName)
68+
{
69+
Instance = this;
70+
AutoConnectPort = Port;
71+
CreateDefaultClientServerWorlds();
72+
73+
var initialized = base.Initialize(defaultWorldName);
74+
OnInitialized?.Invoke(initialized);
75+
return initialized;
76+
}
77+
78+
~UnifiedBootStrap()
79+
{
80+
Instance = null;
81+
}
2982
}
3083
}
3184
#endif

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3549,8 +3549,7 @@ private void Awake()
35493549
private void Start()
35503550
{
35513551
enabled = true;
3552-
}
3553-
internal GhostInstance GhostInstance;
3552+
}
35543553
[SerializeField]
35553554
[HideInInspector]
35563555
internal NetworkObjectBridge NetworkObjectBridge;

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Unity.Collections;
66
using UnityEngine.SceneManagement;
77

8+
89
namespace Unity.Netcode
910
{
1011
/// <summary>
@@ -1132,13 +1133,40 @@ internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager)
11321133
{
11331134
builder.AppendLine($"[Read][Synchronize Objects][WPos: {InternalBuffer.Position}][NO-Count: {newObjectsCount}] Begin:");
11341135
}
1136+
#if UNIFIED_NETCODE
1137+
// TODO-UNIFIED: This is a temporary POC fix to handle hybrid spawning where the Ghost instance might not yet exist.
1138+
var spawnManager = m_NetworkManager.SpawnManager;
1139+
#endif
11351140

11361141
for (int i = 0; i < newObjectsCount; i++)
11371142
{
11381143
var noStart = InternalBuffer.Position;
11391144
var sceneObject = new NetworkObject.SceneObject();
11401145
sceneObject.Deserialize(InternalBuffer);
11411146

1147+
#if UNIFIED_NETCODE
1148+
// TODO-UNIFIED: This is a temporary POC fix to handle synchronizing hybrid spawned objects where the Ghost instance might not yet exist.
1149+
if (sceneObject.HasGhost && !networkManager.SpawnManager.GhostsPendingSpawn.ContainsKey(sceneObject.NetworkObjectId))
1150+
{
1151+
if (networkManager.LogLevel == LogLevel.Developer)
1152+
{
1153+
UnityEngine.Debug.Log($"[{nameof(SceneEventData)}][{nameof(SynchronizeSceneNetworkObjects)}] Deferring creation of NetworkObjectId-{sceneObject.NetworkObjectId} to wait for Ghost.");
1154+
}
1155+
1156+
var newEntry = new PendingGhostSpawnEntry()
1157+
{
1158+
RegistrationTime = UnityEngine.Time.realtimeSinceStartup,
1159+
SceneObject = sceneObject,
1160+
Buffer = new FastBufferReader(InternalBuffer, Allocator.Persistent, sceneObject.SynchronizationDataSize)
1161+
};
1162+
1163+
spawnManager.RegisterGhostPendingSynchronization(newEntry);
1164+
1165+
InternalBuffer.Seek(InternalBuffer.Position + sceneObject.SynchronizationDataSize);
1166+
continue;
1167+
}
1168+
#endif
1169+
11421170
// If the sceneObject is in-scene placed, then set the scene being synchronized
11431171
if (sceneObject.IsSceneObject)
11441172
{
@@ -1425,4 +1453,14 @@ internal SceneEventData(NetworkManager networkManager)
14251453
SceneEventId = XXHash.Hash32(Guid.NewGuid().ToString());
14261454
}
14271455
}
1456+
1457+
#if UNIFIED_NETCODE
1458+
internal struct PendingGhostSpawnEntry
1459+
{
1460+
public float RegistrationTime;
1461+
public FastBufferReader Buffer;
1462+
public NetworkObject.SceneObject SceneObject;
1463+
1464+
}
1465+
#endif
14281466
}

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

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,12 @@ public void RegisterGhostPendingSpawn(NetworkObject networkObject, ulong network
4141
Debug.Log($"[{nameof(RegisterGhostPendingSpawn)}] Registering {networkObject.name} with a {nameof(NetworkObject.NetworkObjectId)} of {networkObjectId}.");
4242
}
4343
GhostsPendingSpawn.TryAdd(networkObjectId, networkObject);
44-
NetworkManager.DeferredMessageManager.ProcessTriggers(IDeferredNetworkMessageManager.TriggerType.OnGhostSpawned, (ulong)networkObject.GhostInstance.ghostId);
44+
NetworkManager.DeferredMessageManager.ProcessTriggers(IDeferredNetworkMessageManager.TriggerType.OnGhostSpawned, networkObjectId);
45+
if (GhostsArePendingSynchronization && GhostsPendingSynchronization.ContainsKey(networkObjectId))
46+
{
47+
// When the object is spawned, it will invoke GetGhostNetworkObjectForSpawn below which removes the entry from GhostsPendingSpawn
48+
ProcessGhostPendingSynchronization(networkObjectId);
49+
}
4550
}
4651

4752
internal NetworkObject GetGhostNetworkObjectForSpawn(ulong networkObjectId)
@@ -55,6 +60,90 @@ internal NetworkObject GetGhostNetworkObjectForSpawn(ulong networkObjectId)
5560
GhostsPendingSpawn.Remove(networkObjectId);
5661
return networkObject;
5762
}
63+
64+
internal bool GhostsArePendingSynchronization;
65+
internal readonly Dictionary<ulong, PendingGhostSpawnEntry> GhostsPendingSynchronization = new Dictionary<ulong, PendingGhostSpawnEntry>();
66+
internal void RegisterGhostPendingSynchronization(PendingGhostSpawnEntry pendingGhostSpawnEntry)
67+
{
68+
var networkObjectId = pendingGhostSpawnEntry.SceneObject.NetworkObjectId;
69+
if (NetworkManager.LogLevel == LogLevel.Developer)
70+
{
71+
Debug.Log($"[{nameof(RegisterGhostPendingSpawn)}] Registering {nameof(NetworkObject)}-{networkObjectId} for pending synchronization.");
72+
}
73+
GhostsPendingSynchronization.TryAdd(networkObjectId, pendingGhostSpawnEntry);
74+
GhostsArePendingSynchronization = true;
75+
}
76+
77+
internal void ProcessGhostPendingSynchronization(ulong networkObjectId, bool removeUponSpawn = true)
78+
{
79+
var ghostPendingSynch = GhostsPendingSynchronization[networkObjectId];
80+
var sceneObject = ghostPendingSynch.SceneObject;
81+
var reader = ghostPendingSynch.Buffer;
82+
if (removeUponSpawn)
83+
{
84+
GhostsPendingSynchronization.Remove(networkObjectId);
85+
}
86+
87+
if (sceneObject.IsSceneObject)
88+
{
89+
NetworkManager.SceneManager.SetTheSceneBeingSynchronized(sceneObject.NetworkSceneHandle);
90+
}
91+
var networkObject = NetworkObject.AddSceneObject(sceneObject, reader, NetworkManager);
92+
// TODO-UNIFIED: How do we handle the "all in-scene placed objects are spawned notification"?
93+
//if (sceneObject.IsSceneObject)
94+
//{
95+
// networkObject.InternalInSceneNetworkObjectsSpawned();
96+
//}
97+
98+
if (removeUponSpawn)
99+
{
100+
GhostsArePendingSynchronization = GhostsPendingSynchronization.Count > 0;
101+
ghostPendingSynch.Buffer.Dispose();
102+
}
103+
}
104+
105+
106+
private HashSet<ulong> m_GhostSynchronizationPendingRemoval = new HashSet<ulong>();
107+
108+
internal void ProcessAllGhostsPendingSynchronization()
109+
{
110+
var spawnTimeout = NetworkManager.NetworkConfig.SpawnTimeout;
111+
var logLevel = NetworkManager.LogLevel;
112+
if (!GhostsArePendingSynchronization)
113+
{
114+
return;
115+
}
116+
foreach (var ghost in GhostsPendingSynchronization)
117+
{
118+
var networkObjectId = ghost.Value.SceneObject.NetworkObjectId;
119+
if (GhostsPendingSpawn.ContainsKey(networkObjectId))
120+
{
121+
// Process it, but don't remove it as we handle that a little later
122+
ProcessGhostPendingSynchronization(ghost.Value.SceneObject.NetworkObjectId, false);
123+
m_GhostSynchronizationPendingRemoval.Add(networkObjectId);
124+
}
125+
else
126+
if ((ghost.Value.RegistrationTime + spawnTimeout) < Time.realtimeSinceStartup)
127+
{
128+
if (logLevel == LogLevel.Developer)
129+
{
130+
Debug.LogWarning($"[{nameof(NetworkSpawnManager)}][{nameof(ProcessAllGhostsPendingSynchronization)}] NetworkObject-{networkObjectId} pending Ghost spawn timed out wiating for the Ghost instance to spawn!");
131+
}
132+
// Timed out entries are removed too
133+
m_GhostSynchronizationPendingRemoval.Add(ghost.Key);
134+
}
135+
}
136+
137+
foreach(var networkObjectId in m_GhostSynchronizationPendingRemoval)
138+
{
139+
var entry = GhostsPendingSynchronization[networkObjectId];
140+
GhostsPendingSynchronization.Remove(networkObjectId);
141+
entry.Buffer.Dispose();
142+
}
143+
m_GhostSynchronizationPendingRemoval.Clear();
144+
GhostsArePendingSynchronization = GhostsPendingSynchronization.Count > 0;
145+
}
146+
58147
#endif
59148

60149
/// <summary>
@@ -1757,7 +1846,15 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec
17571846
{
17581847
RemovePlayerObject(networkObject, destroyGameObject);
17591848
}
1760-
1849+
#if UNIFIED_NETCODE
1850+
// Let unified netcode handle destroying
1851+
if (destroyGameObject && networkObject.HasGhost && !NetworkManager.IsServer)
1852+
{
1853+
networkObject.NetworkObjectBridge.OnDespawn(destroyGameObject);
1854+
// exit early
1855+
return;
1856+
}
1857+
#endif
17611858
var gobj = networkObject.gameObject;
17621859
if (destroyGameObject && gobj != null)
17631860
{

0 commit comments

Comments
 (0)