@@ -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