Skip to content

Commit a9318a3

Browse files
committed
Ensure objects are cleaned up on a failed spawn
1 parent 296b9f5 commit a9318a3

File tree

6 files changed

+355
-210
lines changed

6 files changed

+355
-210
lines changed

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

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -972,27 +972,39 @@ internal void HandleConnectionApproval(ulong ownerClientId, bool createPlayerObj
972972
}
973973

974974
// Server-side spawning (only if there is a prefab hash or player prefab provided)
975-
if (!NetworkManager.DistributedAuthorityMode && createPlayerObject && (playerPrefabHash.HasValue || NetworkManager.NetworkConfig.PlayerPrefab != null))
975+
var idHashToSpawn = playerPrefabHash ?? NetworkManager.NetworkConfig.PlayerPrefab?.GetComponent<NetworkObject>()?.GlobalObjectIdHash;
976+
if (!NetworkManager.DistributedAuthorityMode && createPlayerObject && idHashToSpawn.HasValue)
976977
{
977-
var playerObject = playerPrefabHash.HasValue ? NetworkManager.SpawnManager.GetNetworkObjectToSpawn(playerPrefabHash.Value, ownerClientId, playerPosition, playerRotation)
978-
: NetworkManager.SpawnManager.GetNetworkObjectToSpawn(NetworkManager.NetworkConfig.PlayerPrefab.GetComponent<NetworkObject>().GlobalObjectIdHash, ownerClientId, playerPosition, playerRotation);
978+
var playerObject = NetworkManager.SpawnManager.GetNetworkObjectToSpawn(idHashToSpawn.Value, ownerClientId, playerPosition, playerRotation);
979979

980980
if (playerObject == null)
981981
{
982-
Debug.LogError($"[{nameof(NetworkObject)}] Player prefab is null! Cannot spawn player object!");
982+
if (NetworkManager.LogLevel <= LogLevel.Error)
983+
{
984+
NetworkLog.LogError($"[{nameof(NetworkObject)}] Player prefab is null! Cannot spawn player object!");
985+
}
983986
}
984987
else
985988
{
986989
// Spawn the player NetworkObject locally
987-
NetworkManager.SpawnManager.AuthorityLocalSpawn(
990+
if (NetworkManager.SpawnManager.AuthorityLocalSpawn(
988991
playerObject,
989992
NetworkManager.SpawnManager.GetNetworkObjectId(),
990993
sceneObject: false,
991994
playerObject: true,
992995
ownerClientId,
993-
destroyWithScene: false);
996+
destroyWithScene: false))
997+
{
998+
client.AssignPlayerObject(ref playerObject);
999+
}
1000+
else
1001+
{
1002+
if (NetworkManager.LogLevel <= LogLevel.Developer)
1003+
{
1004+
NetworkLog.LogError($"[{nameof(NetworkObject)}] Player prefab failed to spawn!");
1005+
}
1006+
}
9941007

995-
client.AssignPlayerObject(ref playerObject);
9961008
}
9971009
}
9981010

@@ -1122,8 +1134,25 @@ internal void CreateAndSpawnPlayer(ulong ownerId)
11221134
}
11231135
return;
11241136
}
1125-
var globalObjectIdHash = playerPrefab.GetComponent<NetworkObject>().GlobalObjectIdHash;
1126-
var networkObject = NetworkManager.SpawnManager.GetNetworkObjectToSpawn(globalObjectIdHash, ownerId, playerPrefab.transform.position, playerPrefab.transform.rotation);
1137+
var prefabObject = playerPrefab.GetComponent<NetworkObject>();
1138+
if (prefabObject == null)
1139+
{
1140+
if (NetworkManager.LogLevel <= LogLevel.Normal)
1141+
{
1142+
NetworkLog.LogError("Failed to fetch valid player prefab. Ensure PlayerPrefab that is set in NetcodeConfig contains a NetworkObject component.");
1143+
}
1144+
return;
1145+
}
1146+
var networkObject = NetworkManager.SpawnManager.GetNetworkObjectToSpawn(prefabObject.GlobalObjectIdHash, ownerId, playerPrefab.transform.position, playerPrefab.transform.rotation);
1147+
if (networkObject == null)
1148+
{
1149+
if (NetworkManager.LogLevel <= LogLevel.Normal)
1150+
{
1151+
NetworkLog.LogError("Failed to spawn player prefab!");
1152+
}
1153+
return;
1154+
}
1155+
11271156
networkObject.IsSceneObject = false;
11281157
networkObject.NetworkManagerOwner = NetworkManager;
11291158
networkObject.SpawnAsPlayerObject(ownerId, networkObject.DestroyWithScene);

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,19 @@ internal static class FindObjects
2121
/// <typeparam name="T"></typeparam>
2222
/// <param name="includeInactive">When true, inactive objects will be included.</param>
2323
/// <param name="orderByIdentifier">When true, the array returned will be sorted by identifier.</param>
24-
/// <returns>Resulst as an <see cref="Array"/> of type T</returns>
24+
/// <returns>Results as an <see cref="Array"/> of type T</returns>
2525
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2626
public static T[] ByType<T>(bool includeInactive = false, bool orderByIdentifier = false) where T : Object
2727
{
2828
var inactive = includeInactive ? UnityEngine.FindObjectsInactive.Include : UnityEngine.FindObjectsInactive.Exclude;
2929
#if NGO_FINDOBJECTS_NOSORTING
3030
var results = Object.FindObjectsByType<T>(inactive);
31+
#if !NGO_FINDOBJECTS_UNORDERED_IDS
3132
if (orderByIdentifier)
3233
{
3334
Array.Sort(results, (a, b) => a.GetEntityId().CompareTo(b.GetEntityId()));
3435
}
36+
#endif
3537
#else
3638
var results = Object.FindObjectsByType<T>(inactive, orderByIdentifier ? UnityEngine.FindObjectsSortMode.InstanceID : UnityEngine.FindObjectsSortMode.None);
3739
#endif

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

Lines changed: 30 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1807,7 +1807,15 @@ internal void SpawnInternal(bool destroyWithScene, ulong ownerClientId, bool pla
18071807
}
18081808
}
18091809

1810-
NetworkManagerOwner.SpawnManager.AuthorityLocalSpawn(this, NetworkManagerOwner.SpawnManager.GetNetworkObjectId(), IsSceneObject.HasValue && IsSceneObject.Value, playerObject, ownerClientId, destroyWithScene);
1810+
if (!NetworkManagerOwner.SpawnManager.AuthorityLocalSpawn(this, NetworkManagerOwner.SpawnManager.GetNetworkObjectId(), IsSceneObject.HasValue && IsSceneObject.Value, playerObject, ownerClientId, destroyWithScene))
1811+
{
1812+
if (NetworkManagerOwner.LogLevel <= LogLevel.Normal)
1813+
{
1814+
NetworkLog.LogWarning($"[{name}] Failed to finish spawning!");
1815+
}
1816+
ResetOnDespawn();
1817+
return;
1818+
}
18111819

18121820
if ((NetworkManagerOwner.DistributedAuthorityMode && NetworkManagerOwner.DAHost) || (!NetworkManagerOwner.DistributedAuthorityMode && NetworkManagerOwner.IsServer))
18131821
{
@@ -1865,7 +1873,7 @@ public static NetworkObject InstantiateAndSpawn(GameObject networkPrefab, Networ
18651873
/// <summary>
18661874
/// This invokes <see cref="NetworkSpawnManager.InstantiateAndSpawn(NetworkObject, ulong, bool, bool, bool, Vector3, Quaternion)"/>.
18671875
/// </summary>
1868-
/// <param name="networkManager">The local instance of the NetworkManager connected to an session in progress.</param>
1876+
/// <param name="networkManager">The local instance of the NetworkManager connected to a session in progress.</param>
18691877
/// <param name="ownerClientId">The owner of the <see cref="NetworkObject"/> instance (defaults to server).</param>
18701878
/// <param name="destroyWithScene">Whether the <see cref="NetworkObject"/> instance will be destroyed when the scene it is located within is unloaded (default is false).</param>
18711879
/// <param name="isPlayerObject">Whether the <see cref="NetworkObject"/> instance is a player object or not (default is false).</param>
@@ -2161,6 +2169,11 @@ internal void SetNetworkParenting(ulong? latestParent, bool worldPositionStays)
21612169
m_CachedWorldPositionStays = worldPositionStays;
21622170
}
21632171

2172+
internal void ClearNetworkParenting()
2173+
{
2174+
m_LatestParent = null;
2175+
}
2176+
21642177
/// <summary>
21652178
/// Set the parent of the NetworkObject transform.
21662179
/// </summary>
@@ -3304,90 +3317,35 @@ internal static NetworkObject Deserialize(in SerializedObject serializedObject,
33043317
{
33053318
var endOfSynchronizationData = reader.Position + serializedObject.SynchronizationDataSize;
33063319

3307-
byte[] instantiationData = null;
3308-
if (serializedObject.HasInstantiationData)
3309-
{
3310-
reader.ReadValueSafe(out instantiationData);
3311-
}
3312-
3313-
3314-
// Attempt to create a local NetworkObject
3315-
var networkObject = networkManager.SpawnManager.CreateLocalNetworkObject(serializedObject, instantiationData);
3316-
3317-
3318-
if (networkObject == null)
3319-
{
3320-
// Log the error that the NetworkObject failed to construct
3321-
if (networkManager.LogLevel <= LogLevel.Normal)
3322-
{
3323-
NetworkLog.LogError($"Failed to spawn {nameof(NetworkObject)} for Hash {serializedObject.Hash}.");
3324-
}
3325-
3326-
try
3327-
{
3328-
// If we failed to load this NetworkObject, then skip past the Network Variable and (if any) synchronization data
3329-
reader.Seek(endOfSynchronizationData);
3330-
}
3331-
catch (Exception ex)
3332-
{
3333-
Debug.LogException(ex);
3334-
}
3320+
// Do the SpawnManager parts of the object spawn
3321+
var succeeded = networkManager.SpawnManager.NonAuthorityLocalSpawn(in serializedObject, out var networkObject, reader, serializedObject.DestroyWithScene);
33353322

3336-
// We have nothing left to do here.
3337-
return null;
3338-
}
3339-
3340-
networkObject.NetworkManagerOwner = networkManager;
3341-
3342-
// This will get set again when the NetworkObject is spawned locally, but we set it here ahead of spawning
3343-
// in order to be able to determine which NetworkVariables the client will be allowed to read.
3344-
networkObject.OwnerClientId = serializedObject.OwnerClientId;
3323+
// Process any deferred messages once the object is 100% finished spawning
3324+
// Ensure this is done whether the spawn succeeds or fails
3325+
networkManager.DeferredMessageManager.ProcessTriggers(IDeferredNetworkMessageManager.TriggerType.OnSpawn, networkObject.NetworkObjectId);
33453326

3346-
// Special Case: Invoke NetworkBehaviour.OnPreSpawn methods here before SynchronizeNetworkBehaviours
3347-
networkObject.InvokeBehaviourNetworkPreSpawn();
3348-
3349-
// Process the remaining synchronization data from the buffer
3350-
try
3327+
// If the SpawnManager spawn doesn't succeed, be sure to clean up
3328+
if (!succeeded)
33513329
{
3352-
// Synchronize NetworkBehaviours
3353-
var bufferSerializer = new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
3354-
networkObject.SynchronizeNetworkBehaviours(ref bufferSerializer, networkManager.LocalClientId);
3355-
33563330
// Ensure that the buffer is completely reset
33573331
if (reader.Position != endOfSynchronizationData)
33583332
{
3359-
Debug.LogWarning($"[Size mismatch] Expected: {endOfSynchronizationData} Currently At: {reader.Position}!");
3333+
if (networkManager.LogLevel <= LogLevel.Normal)
3334+
{
3335+
NetworkLog.LogWarning($"[{networkObject.name}][Deserialize][Size mismatch] Expected: {endOfSynchronizationData} Currently At: {reader.Position}!");
3336+
}
33603337
reader.Seek(endOfSynchronizationData);
33613338
}
3362-
}
3363-
catch
3364-
{
3365-
reader.Seek(endOfSynchronizationData);
3366-
}
33673339

3368-
// If we are an in-scene placed NetworkObject and we originally had a parent but when synchronized we are
3369-
// being told we do not have a parent, then we want to clear the latest parent so it is not automatically
3370-
// "re-parented" to the original parent. This can happen if not unloading the scene and the parenting of
3371-
// the in-scene placed Networkobject changes several times over different sessions.
3372-
if (serializedObject.IsSceneObject && !serializedObject.HasParent && networkObject.m_LatestParent.HasValue)
3373-
{
3374-
networkObject.m_LatestParent = null;
3375-
}
3376-
3377-
// Spawn the NetworkObject
3378-
if (networkObject.IsSpawned)
3379-
{
3380-
if (NetworkManager.Singleton.LogLevel <= LogLevel.Error)
3340+
// If the networkObject was created but the spawn failed, the created object needs to be destroyed
3341+
if (networkObject != null)
33813342
{
3382-
NetworkLog.LogErrorServer($"[{networkObject.name}] Object-{networkObject.NetworkObjectId} is already spawned!");
3343+
Destroy(networkObject.gameObject);
33833344
}
3345+
33843346
return null;
33853347
}
33863348

3387-
// Invoke the non-authority local spawn method
3388-
// (It also invokes post spawn and handles processing derferred messages)
3389-
networkManager.SpawnManager.NonAuthorityLocalSpawn(networkObject, serializedObject, serializedObject.DestroyWithScene);
3390-
33913349
if (serializedObject.SyncObservers)
33923350
{
33933351
foreach (var observer in serializedObject.Observers)

0 commit comments

Comments
 (0)