Skip to content

Commit 087a50a

Browse files
Merge branch 'develop-2.0.0' into fix/attachablebehaviours-despawn-errors-scene-changes
2 parents 9004f12 + e02c3e7 commit 087a50a

14 files changed

Lines changed: 411 additions & 258 deletions

File tree

.yamato/_triggers.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,20 +75,20 @@ pr_code_changes_checks:
7575
# Run API validation to early-detect all new APIs that would force us to release new minor version of the package. Note that for this to work the package version in package.json must correspond to "actual package state" which means that it should be higher than last released version
7676
- .yamato/vetting-test.yml#vetting_test
7777

78-
# Run package EditMode and Playmode package tests on 6000.5 and an older supported editor (6000.0)
79-
- .yamato/package-tests.yml#package_test_-_ngo_6000.5_mac
78+
# Run package EditMode and Playmode package tests on trunk and an older supported editor (6000.0)
79+
- .yamato/package-tests.yml#package_test_-_ngo_trunk_mac
8080
- .yamato/package-tests.yml#package_test_-_ngo_6000.0_win
8181

82-
# Run testproject EditMode and Playmode project tests on 6000.5 and an older supported editor (6000.0)
83-
- .yamato/project-tests.yml#test_testproject_win_6000.5
82+
# Run testproject EditMode and Playmode project tests on trunk and an older supported editor (6000.0)
83+
- .yamato/project-tests.yml#test_testproject_win_trunk
8484
- .yamato/project-tests.yml#test_testproject_mac_6000.0
8585

8686
# Run standalone test. We run it only on Ubuntu since it's the fastest machine, and it was noted that for example distribution on macOS is taking 40m since we switched to Apple Silicon
8787
# Coverage on other standalone machines is present in Nightly job so it's enough to not run all of them for PRs
8888
# desktop_standalone_test and cmb_service_standalone_test are both reusing desktop_standalone_build dependency so we run those in the same configuration on PRs to reduce waiting time.
89-
# Note that our daily tests will anyway run both test configurations in "minimal supported" and "6000.5" configurations
90-
- .yamato/desktop-standalone-tests.yml#desktop_standalone_test_testproject_ubuntu_il2cpp_6000.5
91-
- .yamato/cmb-service-standalone-tests.yml#cmb_service_standalone_test_testproject_ubuntu_il2cpp_6000.5
89+
# Note that our daily tests will anyway run both test configurations in "minimal supported" and "trunk" configurations
90+
- .yamato/desktop-standalone-tests.yml#desktop_standalone_test_testproject_ubuntu_il2cpp_trunk
91+
- .yamato/cmb-service-standalone-tests.yml#cmb_service_standalone_test_testproject_ubuntu_il2cpp_trunk
9292
triggers:
9393
expression: |-
9494
(pull_request.comment eq "ngo" OR

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
1313

1414
### Changed
1515

16+
- Hardened error handling and recovery during `NetworkObject` spawn. (#3941)
1617
- Replaced Debug usage by NetcodeLog on `NetworkSpawnManager` and `NetworkObject`. (#3933)
1718
- Improved performance of `NetworkBehaviour`. (#3915)
1819
- Improved performance of `NetworkTransform`. (#3907)

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: 35 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1853,7 +1853,15 @@ internal void SpawnInternal(bool destroyWithScene, ulong ownerClientId, bool pla
18531853
}
18541854
}
18551855

1856-
NetworkManagerOwner.SpawnManager.AuthorityLocalSpawn(this, NetworkManagerOwner.SpawnManager.GetNetworkObjectId(), IsSceneObject.HasValue && IsSceneObject.Value, playerObject, ownerClientId, destroyWithScene);
1856+
if (!NetworkManagerOwner.SpawnManager.AuthorityLocalSpawn(this, NetworkManagerOwner.SpawnManager.GetNetworkObjectId(), IsSceneObject.HasValue && IsSceneObject.Value, playerObject, ownerClientId, destroyWithScene))
1857+
{
1858+
if (NetworkManagerOwner.LogLevel <= LogLevel.Normal)
1859+
{
1860+
NetworkLog.LogWarning($"[{name}] Failed to finish spawning!");
1861+
}
1862+
ResetOnDespawn();
1863+
return;
1864+
}
18571865

18581866
if ((NetworkManagerOwner.DistributedAuthorityMode && NetworkManagerOwner.DAHost) || (!NetworkManagerOwner.DistributedAuthorityMode && NetworkManagerOwner.IsServer))
18591867
{
@@ -1917,7 +1925,7 @@ public static NetworkObject InstantiateAndSpawn(GameObject networkPrefab, Networ
19171925
/// <summary>
19181926
/// This invokes <see cref="NetworkSpawnManager.InstantiateAndSpawn(NetworkObject, ulong, bool, bool, bool, Vector3, Quaternion)"/>.
19191927
/// </summary>
1920-
/// <param name="networkManager">The local instance of the NetworkManager connected to an session in progress.</param>
1928+
/// <param name="networkManager">The local instance of the NetworkManager connected to a session in progress.</param>
19211929
/// <param name="ownerClientId">The owner of the <see cref="NetworkObject"/> instance (defaults to server).</param>
19221930
/// <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>
19231931
/// <param name="isPlayerObject">Whether the <see cref="NetworkObject"/> instance is a player object or not (default is false).</param>
@@ -2229,6 +2237,11 @@ internal void SetNetworkParenting(ulong? latestParent, bool worldPositionStays)
22292237
m_CachedWorldPositionStays = worldPositionStays;
22302238
}
22312239

2240+
internal void ClearNetworkParenting()
2241+
{
2242+
m_LatestParent = null;
2243+
}
2244+
22322245
/// <summary>
22332246
/// Set the parent of the NetworkObject transform.
22342247
/// </summary>
@@ -3263,7 +3276,7 @@ internal void SynchronizeNetworkBehaviours<T>(ref BufferSerializer<T> serializer
32633276
{
32643277
reader.ReadValueSafe(out ushort networkBehaviourId);
32653278
var networkBehaviour = GetNetworkBehaviourAtOrderIndex(networkBehaviourId);
3266-
networkBehaviour.Synchronize(ref serializer, targetClientId);
3279+
networkBehaviour?.Synchronize(ref serializer, targetClientId);
32673280
}
32683281
}
32693282
}
@@ -3355,86 +3368,46 @@ internal static NetworkObject Deserialize(in SerializedObject serializedObject,
33553368
{
33563369
var endOfSynchronizationData = reader.Position + serializedObject.SynchronizationDataSize;
33573370

3358-
byte[] instantiationData = null;
3359-
if (serializedObject.HasInstantiationData)
3360-
{
3361-
reader.ReadValueSafe(out instantiationData);
3362-
}
3363-
3364-
// Attempt to create a local NetworkObject
3365-
var networkObject = networkManager.SpawnManager.CreateLocalNetworkObject(serializedObject, instantiationData);
3366-
if (networkObject == null)
3371+
if (serializedObject.NetworkObjectId == default)
33673372
{
3368-
// Log the error that the NetworkObject failed to construct
3369-
if (networkManager.LogLevel <= LogLevel.Normal)
3370-
{
3371-
NetworkLog.LogError($"Failed to spawn {nameof(NetworkObject)} for Hash {serializedObject.Hash}.");
3372-
}
3373-
3374-
try
3375-
{
3376-
// If we failed to load this NetworkObject, then skip past the Network Variable and (if any) synchronization data
3377-
reader.Seek(endOfSynchronizationData);
3378-
}
3379-
catch (Exception ex)
3373+
if (networkManager.LogLevel <= LogLevel.Error)
33803374
{
3381-
Debug.LogException(ex);
3375+
NetworkLog.LogErrorServer($"[{nameof(GlobalObjectIdHash)}={serializedObject.Hash}] Received spawn request with invalid {nameof(NetworkObjectId)} {serializedObject.NetworkObjectId}. This should not happen!");
33823376
}
33833377

3384-
// We have nothing left to do here.
3378+
reader.Seek(endOfSynchronizationData);
33853379
return null;
33863380
}
3387-
networkObject.NetworkManagerOwner = networkManager;
33883381

3389-
// This will get set again when the NetworkObject is spawned locally, but we set it here ahead of spawning
3390-
// in order to be able to determine which NetworkVariables the client will be allowed to read.
3391-
networkObject.OwnerClientId = serializedObject.OwnerClientId;
3382+
// Do the SpawnManager parts of the object spawn
3383+
var succeeded = networkManager.SpawnManager.NonAuthorityLocalSpawn(in serializedObject, out var networkObject, reader, serializedObject.DestroyWithScene);
33923384

3393-
// Special Case: Invoke NetworkBehaviour.OnPreSpawn methods here before SynchronizeNetworkBehaviours
3394-
networkObject.InvokeBehaviourNetworkPreSpawn();
3385+
// Process any deferred messages once the object is 100% finished spawning
3386+
// Ensure this is done whether the spawn succeeds or fails
3387+
networkManager.DeferredMessageManager.ProcessTriggers(IDeferredNetworkMessageManager.TriggerType.OnSpawn, serializedObject.NetworkObjectId);
33953388

3396-
// Process the remaining synchronization data from the buffer
3397-
try
3389+
// Ensure that the buffer is completely reset
3390+
if (reader.Position != endOfSynchronizationData)
33983391
{
3399-
// Synchronize NetworkBehaviours
3400-
var bufferSerializer = new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
3401-
networkObject.SynchronizeNetworkBehaviours(ref bufferSerializer, networkManager.LocalClientId);
3402-
3403-
// Ensure that the buffer is completely reset
3404-
if (reader.Position != endOfSynchronizationData)
3392+
if (networkManager.LogLevel <= LogLevel.Normal)
34053393
{
3406-
Debug.LogWarning($"[Size mismatch] Expected: {endOfSynchronizationData} Currently At: {reader.Position}!");
3407-
reader.Seek(endOfSynchronizationData);
3394+
NetworkLog.LogWarning($"[{networkObject.name}][Deserialize][{nameof(NetworkBehaviour)}Synchronization][Size mismatch] Expected: {endOfSynchronizationData} Currently At: {reader.Position}!");
34083395
}
3409-
}
3410-
catch
3411-
{
34123396
reader.Seek(endOfSynchronizationData);
34133397
}
34143398

3415-
// If we are an in-scene placed NetworkObject and we originally had a parent but when synchronized we are
3416-
// being told we do not have a parent, then we want to clear the latest parent so it is not automatically
3417-
// "re-parented" to the original parent. This can happen if not unloading the scene and the parenting of
3418-
// the in-scene placed Networkobject changes several times over different sessions.
3419-
if (serializedObject.IsSceneObject && !serializedObject.HasParent && networkObject.m_LatestParent.HasValue)
3420-
{
3421-
networkObject.m_LatestParent = null;
3422-
}
3423-
3424-
// Spawn the NetworkObject
3425-
if (networkObject.IsSpawned)
3399+
// If the SpawnManager spawn doesn't succeed, be sure to clean up
3400+
if (!succeeded)
34263401
{
3427-
if (NetworkManager.Singleton.LogLevel <= LogLevel.Error)
3402+
// If the networkObject was created but the spawn failed, the created object needs to be destroyed
3403+
if (networkObject != null)
34283404
{
3429-
NetworkLog.LogErrorServer($"[{networkObject.name}] Object-{networkObject.NetworkObjectId} is already spawned!");
3405+
Destroy(networkObject.gameObject);
34303406
}
3407+
34313408
return null;
34323409
}
34333410

3434-
// Invoke the non-authority local spawn method
3435-
// (It also invokes post spawn and handles processing derferred messages)
3436-
networkManager.SpawnManager.NonAuthorityLocalSpawn(networkObject, serializedObject, serializedObject.DestroyWithScene);
3437-
34383411
if (serializedObject.SyncObservers)
34393412
{
34403413
foreach (var observer in serializedObject.Observers)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int
112112
}
113113

114114
// Client-Server mode we always defer where in distributed authority mode we only defer if it is not a targeted destroy
115-
if (!networkManager.DistributedAuthorityMode || (networkManager.DistributedAuthorityMode && !IsTargetedDestroy))
115+
if (!networkManager.DistributedAuthorityMode || !networkManager.IsConnectedClient || (networkManager.DistributedAuthorityMode && !IsTargetedDestroy))
116116
{
117117
networkManager.DeferredMessageManager.DeferMessage(IDeferredNetworkMessageManager.TriggerType.OnSpawn, NetworkObjectId, reader, ref context, k_Name);
118118
}

0 commit comments

Comments
 (0)