diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs index d3d6e678b9..710c7be745 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/ComponentController.cs @@ -396,17 +396,21 @@ protected override void OnNetworkPostSpawn() } /// - /// - /// If overriding this method, it is required that you invoke this base method. - /// + // TODO: Not used anymore public override void OnDestroy() + { + base.OnDestroy(); + } + + + internal override void InternalOnDestroy() { if (m_CoroutineObject.IsRunning) { StopCoroutine(m_CoroutineObject.Coroutine); m_CoroutineObject.IsRunning = false; } - base.OnDestroy(); + base.InternalOnDestroy(); } /// diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs index 3a41f7b3a9..e8653687b9 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedBootstrap.cs @@ -75,7 +75,16 @@ public override bool Initialize(string defaultWorldName) LastCreatedWorld = CreateLocalWorld("LocalWorld"); } - OnInitialized?.Invoke(); + // Always wrap events that can invoke user script in a try-catch to assure any + // proceeding script is still executed. + try + { + OnInitialized?.Invoke(); + } + catch (Exception ex) + { + Debug.LogException(ex); + } return true; } diff --git a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedUpdateConnections.cs b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedUpdateConnections.cs index db3988b0fd..76bfc095cd 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedUpdateConnections.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/Helpers/UnifiedUpdateConnections.cs @@ -54,7 +54,14 @@ protected override void OnUpdate() foreach (var con in m_TempConnections) { - NetworkManager.OnNetCodeDisconnect?.Invoke(con); + try + { + NetworkManager.OnNetCodeDisconnect?.Invoke(con); + } + catch (System.Exception ex) + { + Debug.LogException(ex); + } } m_TempConnections.Clear(); @@ -83,7 +90,15 @@ protected override void OnUpdate() // Set the connection in-game commandBuffer.AddComponent(entry.Value.Entity); commandBuffer.AddComponent(entry.Value.Entity, default(ConnectionState)); - NetworkManager.OnNetCodeConnect?.Invoke(entry.Value); + + try + { + NetworkManager.OnNetCodeConnect?.Invoke(entry.Value); + } + catch (System.Exception ex) + { + Debug.LogException(ex); + } m_TempConnections.Add(entry.Value); } } @@ -104,8 +119,16 @@ protected override void OnUpdate() foreach (var (networkId, entity) in SystemAPI.Query().WithEntityAccess()) { commandBuffer.RemoveComponent(entity); - NetworkManager.OnNetCodeDisconnect?.Invoke(new NetcodeConnection - { World = World, Entity = entity, NetworkId = networkId.Value }); + + try + { + NetworkManager.OnNetCodeDisconnect?.Invoke(new NetcodeConnection + { World = World, Entity = entity, NetworkId = networkId.Value }); + } + catch (System.Exception ex) + { + Debug.LogException(ex); + } } } } @@ -121,7 +144,15 @@ protected override void OnDestroy() foreach (var (networkId, entity) in SystemAPI.Query().WithEntityAccess()) { commandBuffer.RemoveComponent(entity); - NetworkManager.OnNetCodeDisconnect?.Invoke(new NetcodeConnection { World = World, Entity = entity, NetworkId = networkId.Value }); + + try + { + NetworkManager.OnNetCodeDisconnect?.Invoke(new NetcodeConnection { World = World, Entity = entity, NetworkId = networkId.Value }); + } + catch (System.Exception ex) + { + Debug.LogException(ex); + } } commandBuffer.Playback(EntityManager); base.OnDestroy(); diff --git a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs index f614843cef..4c20edd258 100644 --- a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs @@ -711,7 +711,15 @@ internal void TransportFailureEventHandler(bool duringStart = false) var clientSeverOrHost = LocalClient.IsServer ? LocalClient.IsHost ? "Host" : "Server" : "Client"; var whenFailed = duringStart ? "start failure" : "failure"; NetworkLog.LogError($"{clientSeverOrHost} is shutting down due to network transport {whenFailed} of {NetworkManager.NetworkConfig.NetworkTransport.GetType().Name}!"); - OnTransportFailure?.Invoke(); + + try + { + OnTransportFailure?.Invoke(); + } + catch (Exception ex) + { + Debug.LogException(ex); + } // If we had a transport failure when trying to start, reset the local client roles and directly invoke the internal shutdown. if (duringStart) @@ -854,12 +862,25 @@ internal void ApproveConnection(ref ConnectionRequestMessage connectionRequestMe // Note: ToArray() also allocates. :( var response = new NetworkManager.ConnectionApprovalResponse(); ClientsToApprove[context.SenderId] = response; - ConnectionApprovalCallback?.Invoke( - new NetworkManager.ConnectionApprovalRequest - { - Payload = connectionRequestMessage.ConnectionData, - ClientNetworkId = context.SenderId - }, response); + try + { + ConnectionApprovalCallback?.Invoke( + new NetworkManager.ConnectionApprovalRequest + { + Payload = connectionRequestMessage.ConnectionData, + ClientNetworkId = context.SenderId + }, response); + } + catch (Exception ex) + { + // A throwing approval handler would otherwise leave a Pending response stranded in + // ClientsToApprove, hanging the connecting client until it times out. Deny instead. + Debug.LogException(ex); + response.Approved = false; + response.Pending = false; + response.CreatePlayerObject = false; + response.Reason = "Connection approval handler threw an exception."; + } } /// @@ -1748,13 +1769,22 @@ internal void Shutdown() { //The Transport is set during initialization, thus it is possible for the Transport to be null var transport = NetworkManager.NetworkConfig?.NetworkTransport; - if (transport != null) + if (transport == null) + { + return; + } + // if the transport throws we need to ensure we finish the shutdown sequence. + try { transport.Shutdown(); - if (NetworkManager.LogLevel <= LogLevel.Developer) - { - NetworkLog.LogInfo($"{nameof(NetworkConnectionManager)}.{nameof(Shutdown)}() -> {nameof(IsListening)} && {nameof(NetworkManager.NetworkConfig.NetworkTransport)} != null -> {nameof(NetworkTransport)}.{nameof(NetworkTransport.Shutdown)}()"); - } + } + catch (Exception ex) + { + Debug.LogException(ex); + } + if (NetworkManager.LogLevel <= LogLevel.Developer) + { + NetworkLog.LogInfo($"{nameof(NetworkConnectionManager)}.{nameof(Shutdown)}() -> {nameof(IsListening)} && {nameof(NetworkManager.NetworkConfig.NetworkTransport)} != null -> {nameof(NetworkTransport)}.{nameof(NetworkTransport.Shutdown)}()"); } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 5d1a04537b..8d79f0f6e4 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -137,7 +137,14 @@ internal void __endSendServerRpc(ref FastBufferWriter bufferWriter, uint rpcMeth MessageSize = 0 }; serverRpcMessage.ReadBuffer = tempBuffer; - serverRpcMessage.Handle(ref context); + try + { + serverRpcMessage.Handle(ref context); + } + catch (Exception e) + { + Debug.LogException(e); + } rpcWriteSize = tempBuffer.Length; } else @@ -267,7 +274,14 @@ internal void __endSendClientRpc(ref FastBufferWriter bufferWriter, uint rpcMeth MessageSize = 0 }; clientRpcMessage.ReadBuffer = tempBuffer; - clientRpcMessage.Handle(ref context); + try + { + clientRpcMessage.Handle(ref context); + } + catch (Exception e) + { + Debug.LogException(e); + } } bufferWriter.Dispose(); @@ -640,8 +654,16 @@ protected internal virtual void OnIsDestroying() /// internal void SetIsDestroying() { - // We intentionally invoke this before setting the IsDestroying flag. - OnIsDestroying(); + try + { + // We intentionally invoke this before setting the IsDestroying flag. + OnIsDestroying(); + } + catch (Exception e) + { + Debug.LogException(e); + } + // Set outside of the try-catch: a throwing override must not leave this flag false. IsDestroying = true; } @@ -931,7 +953,14 @@ internal void InternalOnGainedOwnership() { UpdateNetworkVariableOnOwnershipChanged(); } - OnGainedOwnership(); + try + { + OnGainedOwnership(); + } + catch (Exception e) + { + Debug.LogException(e); + } } /// @@ -948,7 +977,14 @@ protected virtual void OnOwnershipChanged(ulong previous, ulong current) internal void InternalOnOwnershipChanged(ulong previous, ulong current) { - OnOwnershipChanged(previous, current); + try + { + OnOwnershipChanged(previous, current); + } + catch (Exception e) + { + Debug.LogException(e); + } } /// diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 185d682f66..daa3292da9 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -252,7 +252,14 @@ internal void SetSessionOwner(ulong sessionOwner) networkObject.InvokeSessionOwnerPromoted(isSessionOwner); } - OnSessionOwnerPromoted?.Invoke(sessionOwner); + try + { + OnSessionOwnerPromoted?.Invoke(sessionOwner); + } + catch (Exception ex) + { + Log.Exception(ex); + } } #if ENABLE_SESSIONOWNER_PROMOTION_NOTIFICATION @@ -1103,7 +1110,14 @@ private void Awake() EditorApplication.playModeStateChanged += ModeChanged; #endif // Notify we have instantiated a new instance of NetworkManager. - OnInstantiated?.Invoke(this); + try + { + OnInstantiated?.Invoke(this); + } + catch (Exception ex) + { + Log.Exception(ex); + } } private void OnEnable() @@ -1479,8 +1493,15 @@ internal bool InternalStartServer() // Notify the server that everything should be synchronized/spawned at this time. SpawnManager.NotifyNetworkObjectsSynchronized(); - OnServerStarted?.Invoke(); - OnStarted?.Invoke(); + try + { + OnServerStarted?.Invoke(); + OnStarted?.Invoke(); + } + catch (Exception ex) + { + Log.Exception(ex); + } ConnectionManager.LocalClient.IsApproved = true; return true; } @@ -1555,8 +1576,15 @@ internal bool InternalStartClient() } else { - OnClientStarted?.Invoke(); - OnStarted?.Invoke(); + try + { + OnClientStarted?.Invoke(); + OnStarted?.Invoke(); + } + catch (Exception ex) + { + Log.Exception(ex); + } } } catch (Exception ex) @@ -1677,9 +1705,16 @@ private void HostServerInitialize() // Notify the host that everything should be synchronized/spawned at this time. SpawnManager.NotifyNetworkObjectsSynchronized(); - OnServerStarted?.Invoke(); - OnClientStarted?.Invoke(); - OnStarted?.Invoke(); + try + { + OnServerStarted?.Invoke(); + OnClientStarted?.Invoke(); + OnStarted?.Invoke(); + } + catch (Exception ex) + { + Log.Exception(ex); + } // This assures that any in-scene placed NetworkObject is spawned and // any associated NetworkBehaviours' netcode related properties are @@ -1874,21 +1909,28 @@ internal void ShutdownInternal() NetworkTimeSystem?.Shutdown(); NetworkTickSystem = null; - if (localClient.IsClient) + + try { - // If we were a client, we want to know if we were a host - // client or not. (why we pass in "IsServer") - OnClientStopped?.Invoke(localClient.IsServer); - } + if (localClient.IsClient) + { + // If we were a client, we want to know if we were a host + // client or not. (why we pass in "IsServer") + OnClientStopped?.Invoke(localClient.IsServer); + } + if (localClient.IsServer) + { + // If we were a server, we want to know if we were a host + // or not. (why we pass in "IsClient") + OnServerStopped?.Invoke(localClient.IsClient); + } - if (localClient.IsServer) + OnStopped?.Invoke(); + } + catch (Exception ex) { - // If we were a server, we want to know if we were a host - // or not. (why we pass in "IsClient") - OnServerStopped?.Invoke(localClient.IsClient); + Log.Exception(ex); } - - OnStopped?.Invoke(); } // Ensures that the NetworkManager is cleaned up before OnDestroy is run on NetworkObjects and NetworkBehaviours when quitting the application. @@ -1914,9 +1956,17 @@ private void OnApplicationQuit() #endif } + private bool m_IsDestroyed = false; + // Note that this gets also called manually by OnSceneUnloaded and OnApplicationQuit private void OnDestroy() { + if (m_IsDestroyed) + { + return; + } + m_IsDestroyed = true; + try { ShutdownInternal(); diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 0b39c84f6f..49a7088e56 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -523,7 +523,14 @@ public void DeferDespawn(int tickOffset, bool destroy = true) // Notify all NetworkBehaviours that the authority is performing a deferred despawn. // This is when user script would update NetworkVariable states that might be needed // for the deferred despawn sequence on non-authoritative instances. - behaviour.OnDeferringDespawn(DeferredDespawnTick); + try + { + behaviour.OnDeferringDespawn(DeferredDespawnTick); + } + catch (Exception ex) + { + Debug.LogException(ex); + } } // DAHost handles sending updates to all clients @@ -810,6 +817,19 @@ public enum OwnershipPermissionsFailureStatus /// public OnOwnershipPermissionsFailureDelegateHandler OnOwnershipPermissionsFailure; + + internal void InvokeOwnershipPermissionsFailure() + { + try + { + OnOwnershipPermissionsFailure?.Invoke(OwnershipPermissionsFailureStatus.SessionOwnerOnly); + } + catch (Exception ex) + { + Debug.LogException(ex); + } + } + /// /// Returned by to signify w /// : The request for ownership was sent (does not mean it will be granted, but the request was sent). @@ -978,8 +998,16 @@ internal void OwnershipRequest(ulong clientRequestingOwnership) // Finally, check to see if OnOwnershipRequested is registered and if user script is allowing // this transfer of ownership - if (OnOwnershipRequested != null && !OnOwnershipRequested.Invoke(clientRequestingOwnership)) + try { + if (OnOwnershipRequested != null && !OnOwnershipRequested.Invoke(clientRequestingOwnership)) + { + response = OwnershipRequestResponseStatus.Denied; + } + } + catch (Exception ex) + { + Debug.LogException(ex); response = OwnershipRequestResponseStatus.Denied; } @@ -1072,7 +1100,14 @@ public enum OwnershipRequestResponseStatus /// internal void OwnershipRequestResponse(OwnershipRequestResponseStatus ownershipRequestResponse) { - OnOwnershipRequestResponse?.Invoke(ownershipRequestResponse); + try + { + OnOwnershipRequestResponse?.Invoke(ownershipRequestResponse); + } + catch (Exception ex) + { + Debug.LogException(ex); + } } /// @@ -1434,6 +1469,27 @@ public void SetSceneObjectStatus(bool isSceneObject = false) /// public VisibilityDelegate CheckObjectVisibility = null; + /// + /// Returns true if the object should be visible to the specified client, false otherwise + /// Defaults to the object being visible. + /// + internal bool InvokeCheckObjectVisibility(ulong clientId) + { + if (CheckObjectVisibility == null) + { + return true; + } + try + { + return CheckObjectVisibility(clientId); + } + catch (Exception ex) + { + Debug.LogException(ex); + return true; + } + } + /// /// Delegate type for checking spawn options /// @@ -1637,7 +1693,7 @@ public void NetworkShow(ulong clientId) return; } - if (CheckObjectVisibility != null && !CheckObjectVisibility(clientId)) + if (!InvokeCheckObjectVisibility(clientId)) { if (NetworkManagerOwner.LogLevel <= LogLevel.Normal) { @@ -2295,8 +2351,8 @@ internal void SetupObservers() // then add all connected clients as observers foreach (var clientId in NetworkManagerOwner.ConnectedClientsIds) { - // If CheckObjectVisibility has a callback, then allow that method determine who the observers are. - if (CheckObjectVisibility != null && !CheckObjectVisibility(clientId)) + // If CheckObjectVisibility marks this object as not visible to the client, then skip adding it as an observer + if (!InvokeCheckObjectVisibility(clientId)) { continue; } @@ -2386,7 +2442,14 @@ internal void InvokeBehaviourOnOwnershipChanged(ulong originalOwnerClientId, ulo childBehaviour.UpdateNetworkProperties(); if (distributedAuthorityMode || isServer || isPreviousOwner) { - childBehaviour.OnLostOwnership(); + try + { + childBehaviour.OnLostOwnership(); + } + catch (Exception ex) + { + Debug.LogException(ex); + } } } @@ -2457,7 +2520,14 @@ internal void InvokeBehaviourOnNetworkObjectParentChanged(NetworkObject parentNe // Invoke internal notification child.InternalOnNetworkObjectParentChanged(parentNetworkObject); // Invoke public notification - child.OnNetworkObjectParentChanged(parentNetworkObject); + try + { + child.OnNetworkObjectParentChanged(parentNetworkObject); + } + catch (Exception ex) + { + Debug.LogException(ex); + } } } @@ -3904,7 +3974,14 @@ internal void SceneChangedUpdate(Scene scene, bool notify = false) $"client scene mismatch detected! Client-side scene handle ({SceneOriginHandle}) for scene ({gameObject.scene.name})" + $"has no associated server side (network) scene handle!"); } - OnMigratedToNewScene?.Invoke(); + try + { + OnMigratedToNewScene?.Invoke(); + } + catch (Exception ex) + { + Debug.LogException(ex); + } // Only the authority side will notify clients of non-parented NetworkObject scene changes if (m_HasAuthority && notify && !transform.parent) diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs index 62c3391b46..7adcf969db 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs @@ -157,7 +157,7 @@ public void Serialize(FastBufferWriter writer, int targetVersion) // Serialize NetworkVariable data foreach (var sobj in SpawnedObjectsList) { - if (sobj.SpawnWithObservers && (sobj.CheckObjectVisibility == null || sobj.CheckObjectVisibility(OwnerClientId))) + if (sobj.SpawnWithObservers && sobj.InvokeCheckObjectVisibility(OwnerClientId)) { sobj.AddObserver(OwnerClientId); // In distributed authority mode, we send the currently known observers of each NetworkObject to the client being synchronized. diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/AnticipatedNetworkVariable.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/AnticipatedNetworkVariable.cs index 1cfab08e12..2ebb33553b 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/AnticipatedNetworkVariable.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/AnticipatedNetworkVariable.cs @@ -372,7 +372,14 @@ private void OnValueChangedInternal(T previousValue, T newValue) m_SmoothDuration = 0; m_CurrentSmoothTime = 0; - OnAuthoritativeValueChanged?.Invoke(this, previousValue, newValue); + try + { + OnAuthoritativeValueChanged?.Invoke(this, previousValue, newValue); + } + catch (Exception ex) + { + Debug.LogException(ex); + } } /// diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs index 5bcd5cead3..a241e8463b 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs @@ -681,7 +681,15 @@ private void HandleAddListEvent(NetworkListEvent listEvent) { m_DirtyEvents.Add(listEvent); MarkNetworkObjectDirty(); - OnListChanged?.Invoke(listEvent); + + try + { + OnListChanged?.Invoke(listEvent); + } + catch (Exception ex) + { + UnityEngine.Debug.LogException(ex); + } } /// diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs index e8604a66aa..688efa454a 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariable.cs @@ -162,7 +162,14 @@ public virtual T Value NetworkVariableSerialization.Duplicate(m_InternalValue, ref m_LastInternalValue); SetDirty(true); m_IsDisposed = false; - OnValueChanged?.Invoke(previousValue, m_InternalValue); + try + { + OnValueChanged?.Invoke(previousValue, m_InternalValue); + } + catch (Exception ex) + { + Debug.LogException(ex); + } } } } @@ -196,7 +203,14 @@ public bool CheckDirtyState(bool forceCheck = false) if ((!isDirty || forceCheck) && !NetworkVariableSerialization.AreEqual(ref m_LastInternalValue, ref m_InternalValue)) { SetDirty(true); - OnValueChanged?.Invoke(m_LastInternalValue, m_InternalValue); + try + { + OnValueChanged?.Invoke(m_LastInternalValue, m_InternalValue); + } + catch (Exception ex) + { + Debug.LogException(ex); + } m_IsDisposed = false; isDirty = true; NetworkVariableSerialization.Duplicate(m_InternalValue, ref m_LastInternalValue); @@ -345,7 +359,14 @@ public override void ReadDelta(FastBufferReader reader, bool keepDirtyDelta) SetDirty(true); } - OnValueChanged?.Invoke(m_PreviousValue, m_InternalValue); + try + { + OnValueChanged?.Invoke(m_PreviousValue, m_InternalValue); + } + catch (Exception ex) + { + Debug.LogException(ex); + } } /// diff --git a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs index dd52c55782..ba44e8f0e4 100644 --- a/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs +++ b/com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs @@ -125,7 +125,14 @@ public void Initialize(NetworkBehaviour networkBehaviour) // When in distributed authority mode, there is no such thing as server write permissions InternalWritePerm = m_NetworkManager.DistributedAuthorityMode ? NetworkVariableWritePermission.Owner : InternalWritePerm; - OnInitialize(); + try + { + OnInitialize(); + } + catch (Exception ex) + { + Debug.LogException(ex); + } // Some unit tests don't operate with a running NetworkManager. // Only update the last time if there is a NetworkTimeSystem. diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/DefaultSceneManagerHandler.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/DefaultSceneManagerHandler.cs index f7ce87d521..972c3a8ccd 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/DefaultSceneManagerHandler.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/DefaultSceneManagerHandler.cs @@ -215,13 +215,25 @@ public void UnloadUnassignedScenes(NetworkManager networkManager) var scenHandleEntries = SceneNameToSceneHandles[sceneEntry.Key]; foreach (var sceneHandleEntry in scenHandleEntries) { - if (!sceneHandleEntry.Value.IsAssigned) + if (sceneHandleEntry.Value.IsAssigned) + { + continue; + } + + try { - if (sceneManager.VerifySceneBeforeUnloading == null || sceneManager.VerifySceneBeforeUnloading.Invoke(sceneHandleEntry.Value.Scene)) + // Don't unload the scene if the user-configured handler says to keep it loaded + if (sceneManager.VerifySceneBeforeUnloading != null && !sceneManager.VerifySceneBeforeUnloading.Invoke(sceneHandleEntry.Value.Scene)) { - m_ScenesToUnload.Add(sceneHandleEntry.Value.Scene); + continue; } } + catch (Exception ex) + { + Debug.LogException(ex); + } + + m_ScenesToUnload.Add(sceneHandleEntry.Value.Scene); } } foreach (var sceneToUnload in m_ScenesToUnload) @@ -379,18 +391,22 @@ public void SetClientSynchronizationMode(ref NetworkManager networkManager, Load // If using scene verification if (sceneManager.VerifySceneBeforeLoading != null) { - // Determine if we should take this scene into consideration - if (!sceneManager.VerifySceneBeforeLoading.Invoke(scene.buildIndex, scene.name, LoadSceneMode.Additive)) + try { - continue; + // Determine if we should take this scene into consideration + if (!sceneManager.VerifySceneBeforeLoading.Invoke(scene.buildIndex, scene.name, LoadSceneMode.Additive)) + { + continue; + } + } + catch (Exception ex) + { + Debug.LogException(ex); } } // If the scene is not already in the ScenesLoaded list, then add it - if (!sceneManager.ScenesLoaded.ContainsKey(scene.handle)) - { - sceneManager.ScenesLoaded.Add(scene.handle, scene); - } + sceneManager.ScenesLoaded.TryAdd(scene.handle, scene); } } // Set the client synchronization mode diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs index cafce0f7c1..f51d7151e0 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs @@ -598,11 +598,18 @@ internal bool HasSceneAuthority() return (!NetworkManager.DistributedAuthorityMode && NetworkManager.IsServer) || (NetworkManager.DistributedAuthorityMode && NetworkManager.LocalClient.IsSessionOwner); } + private bool m_IsDisposed; + /// /// Handle NetworkSceneManager clean up /// public void Dispose() { + if (m_IsDisposed) + { + return; + } + m_IsDisposed = true; // Always assure we no longer listen to scene changes when disposed. SceneManager.activeSceneChanged -= SceneManager_ActiveSceneChanged; SceneUnloadEventHandler.Shutdown(); @@ -940,7 +947,16 @@ internal bool ValidateSceneBeforeLoading(int sceneIndex, string sceneName, LoadS var validated = true; if (VerifySceneBeforeLoading != null) { - validated = VerifySceneBeforeLoading.Invoke(sceneIndex, sceneName, loadSceneMode); + try + { + validated = VerifySceneBeforeLoading.Invoke(sceneIndex, sceneName, loadSceneMode); + } + catch (Exception ex) + { + Debug.LogException(ex); + // Fallback to the no-handler default + validated = true; + } } if (!validated && !m_DisableValidationWarningMessages) { @@ -1646,16 +1662,23 @@ private void SceneUnloaded(Scene scene) { if (m_NetworkSceneManager != null && m_NetworkSceneManager.NetworkManager != null) { - m_NetworkSceneManager.OnSceneEvent?.Invoke(new SceneEvent() + try + { + m_NetworkSceneManager.OnSceneEvent?.Invoke(new SceneEvent() + { + AsyncOperation = m_AsyncOperation, + SceneEventType = SceneEventType.UnloadComplete, + SceneName = m_Scene.name, + ScenePath = m_Scene.path, + LoadSceneMode = m_LoadSceneMode, + ClientId = m_ClientId + }); + m_NetworkSceneManager.OnUnloadComplete?.Invoke(m_ClientId, m_Scene.name); + } + catch (Exception ex) { - AsyncOperation = m_AsyncOperation, - SceneEventType = SceneEventType.UnloadComplete, - SceneName = m_Scene.name, - ScenePath = m_Scene.path, - LoadSceneMode = m_LoadSceneMode, - ClientId = m_ClientId - }); - m_NetworkSceneManager.OnUnloadComplete?.Invoke(m_ClientId, m_Scene.name); + Debug.LogException(ex); + } } SceneManager.sceneUnloaded -= SceneUnloaded; SceneUnloadComplete(this); @@ -1671,17 +1694,25 @@ private SceneUnloadEventHandler(NetworkSceneManager networkSceneManager, Scene s m_Scene = scene; SceneManager.sceneUnloaded += SceneUnloaded; // Send the initial unload event notification - m_NetworkSceneManager.OnSceneEvent?.Invoke(new SceneEvent() - { - AsyncOperation = m_AsyncOperation, - SceneEventType = SceneEventType.Unload, - SceneName = m_Scene.name, - ScenePath = m_Scene.path, - LoadSceneMode = m_LoadSceneMode, - ClientId = clientId - }); + try + { + m_NetworkSceneManager.OnSceneEvent?.Invoke(new SceneEvent() + { + AsyncOperation = m_AsyncOperation, + SceneEventType = SceneEventType.Unload, + SceneName = m_Scene.name, + ScenePath = m_Scene.path, + LoadSceneMode = m_LoadSceneMode, + ClientId = clientId + }); - m_NetworkSceneManager.OnUnload?.Invoke(networkSceneManager.NetworkManager.LocalClientId, m_Scene.name, null); + m_NetworkSceneManager.OnUnload?.Invoke(networkSceneManager.NetworkManager.LocalClientId, m_Scene.name, null); + + } + catch (Exception ex) + { + Debug.LogException(ex); + } } } @@ -2092,13 +2123,20 @@ internal void SynchronizeNetworkObjects(ulong clientId, bool synchronizingServic // Notify the local server that the client has been sent the synchronize event if (!synchronizingService) { - OnSceneEvent?.Invoke(new SceneEvent() + try { - SceneEventType = SceneEventType.Synchronize, - ClientId = clientId - }); + OnSceneEvent?.Invoke(new SceneEvent() + { + SceneEventType = SceneEventType.Synchronize, + ClientId = clientId + }); + OnSynchronize?.Invoke(clientId); - OnSynchronize?.Invoke(clientId); + } + catch (Exception ex) + { + Debug.LogException(ex); + } } EndSceneEvent(sceneEventData.SceneEventId); @@ -2157,17 +2195,25 @@ private void OnClientBeginSync(uint sceneEventId) sceneLoad = SceneManagerHandler.LoadSceneAsync(sceneName, loadSceneMode, sceneEventProgress); // Notify local client that a scene load has begun - OnSceneEvent?.Invoke(new SceneEvent() + try { - AsyncOperation = sceneLoad, - SceneEventType = SceneEventType.Load, - LoadSceneMode = loadSceneMode, - SceneName = sceneName, - ScenePath = ScenePathFromHash(sceneHash), - ClientId = NetworkManager.LocalClientId, - }); + OnSceneEvent?.Invoke(new SceneEvent() + { + AsyncOperation = sceneLoad, + SceneEventType = SceneEventType.Load, + LoadSceneMode = loadSceneMode, + SceneName = sceneName, + ScenePath = ScenePathFromHash(sceneHash), + ClientId = NetworkManager.LocalClientId, + }); + + OnLoad?.Invoke(NetworkManager.LocalClientId, sceneName, loadSceneMode, sceneLoad); - OnLoad?.Invoke(NetworkManager.LocalClientId, sceneName, loadSceneMode, sceneLoad); + } + catch (Exception ex) + { + Debug.LogException(ex); + } } else { @@ -2323,13 +2369,21 @@ private void HandleClientSceneEvent(uint sceneEventId) { sceneEventData.IsStartingSynchronization = false; - OnSceneEvent?.Invoke(new SceneEvent() + try { - SceneEventType = SceneEventType.Synchronize, - ClientId = NetworkManager.LocalClientId, - }); + OnSceneEvent?.Invoke(new SceneEvent() + { + SceneEventType = SceneEventType.Synchronize, + ClientId = NetworkManager.LocalClientId, + }); + + OnSynchronize?.Invoke(NetworkManager.LocalClientId); - OnSynchronize?.Invoke(NetworkManager.LocalClientId); + } + catch (Exception ex) + { + Debug.LogException(ex); + } } if (!sceneEventData.IsDoneWithSynchronization()) @@ -2406,13 +2460,21 @@ private void HandleClientSceneEvent(uint sceneEventId) NetworkManager.ConnectionManager.InvokeOnClientConnectedCallback(NetworkManager.LocalClientId); // Notify the client that they have finished synchronizing - OnSceneEvent?.Invoke(new SceneEvent() + try { - SceneEventType = sceneEventData.SceneEventType, - ClientId = NetworkManager.LocalClientId, // Client sent this to the server - }); + OnSceneEvent?.Invoke(new SceneEvent() + { + SceneEventType = sceneEventData.SceneEventType, + ClientId = NetworkManager.LocalClientId, // Client sent this to the server + }); + + OnSynchronizeComplete?.Invoke(NetworkManager.LocalClientId); - OnSynchronizeComplete?.Invoke(NetworkManager.LocalClientId); + } + catch (Exception ex) + { + Debug.LogException(ex); + } if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) { @@ -2434,11 +2496,18 @@ private void HandleClientSceneEvent(uint sceneEventId) case SceneEventType.ReSynchronize: { // Notify the local client that they have been re-synchronized after being synchronized with an in progress game session - OnSceneEvent?.Invoke(new SceneEvent() + try + { + OnSceneEvent?.Invoke(new SceneEvent() + { + SceneEventType = sceneEventData.SceneEventType, + ClientId = NetworkManager.ServerClientId, // Server sent this to client + }); + } + catch (Exception ex) { - SceneEventType = sceneEventData.SceneEventType, - ClientId = NetworkManager.ServerClientId, // Server sent this to client - }); + Debug.LogException(ex); + } EndSceneEvent(sceneEventId); break; @@ -2495,12 +2564,19 @@ private void HandleSessionOwnerEvent(uint sceneEventId, ulong clientId) NetworkManager.ConnectedClients[clientId].IsConnected = true; // Notify that a client has finished synchronizing - OnSceneEvent?.Invoke(new SceneEvent() + try + { + OnSceneEvent?.Invoke(new SceneEvent() + { + SceneEventType = sceneEventData.SceneEventType, + ClientId = clientId + }); + OnSynchronizeComplete?.Invoke(clientId); + } + catch (Exception ex) { - SceneEventType = sceneEventData.SceneEventType, - ClientId = clientId - }); - OnSynchronizeComplete?.Invoke(clientId); + Debug.LogException(ex); + } // For non-authority clients in a distributed authority session, we show hidden objects, // we distribute NetworkObjects, and then we end the scene event. @@ -2537,12 +2613,19 @@ private void HandleSessionOwnerEvent(uint sceneEventId, ulong clientId) sceneEventData.SceneEventType = SceneEventType.ReSynchronize; SendSceneEventData(sceneEventId, new ulong[] { clientId }); - OnSceneEvent?.Invoke(new SceneEvent() + try { - SceneEventType = sceneEventData.SceneEventType, - SceneName = string.Empty, - ClientId = clientId - }); + OnSceneEvent?.Invoke(new SceneEvent() + { + SceneEventType = sceneEventData.SceneEventType, + SceneName = string.Empty, + ClientId = clientId + }); + } + catch (Exception ex) + { + Debug.LogException(ex); + } } // DANGO-EXP TODO: Remove this once service distributes objects NetworkManager.SpawnManager.DistributeNetworkObjects(clientId); @@ -3349,39 +3432,46 @@ public List GetSceneMapping(MapTypes mapType) private void InvokeSceneEvents(ulong clientId, SceneEventData eventData, AsyncOperation asyncOperation = null, Scene scene = default) { var sceneName = SceneNameFromHash(eventData.SceneHash); - OnSceneEvent?.Invoke(new SceneEvent() + try { - AsyncOperation = asyncOperation, - SceneEventType = eventData.SceneEventType, - SceneName = sceneName, - ScenePath = ScenePathFromHash(eventData.SceneHash), - ClientId = clientId, - LoadSceneMode = eventData.LoadSceneMode, - ClientsThatCompleted = eventData.ClientsCompleted, - ClientsThatTimedOut = eventData.ClientsTimedOut, - Scene = scene, - }); + OnSceneEvent?.Invoke(new SceneEvent() + { + AsyncOperation = asyncOperation, + SceneEventType = eventData.SceneEventType, + SceneName = sceneName, + ScenePath = ScenePathFromHash(eventData.SceneHash), + ClientId = clientId, + LoadSceneMode = eventData.LoadSceneMode, + ClientsThatCompleted = eventData.ClientsCompleted, + ClientsThatTimedOut = eventData.ClientsTimedOut, + Scene = scene, + }); - switch (eventData.SceneEventType) + switch (eventData.SceneEventType) + { + case SceneEventType.Load: + OnLoad?.Invoke(clientId, sceneName, eventData.LoadSceneMode, asyncOperation); + break; + case SceneEventType.Unload: + OnUnload?.Invoke(clientId, sceneName, asyncOperation); + break; + case SceneEventType.LoadComplete: + OnLoadComplete?.Invoke(clientId, sceneName, eventData.LoadSceneMode); + break; + case SceneEventType.UnloadComplete: + OnUnloadComplete?.Invoke(clientId, sceneName); + break; + case SceneEventType.LoadEventCompleted: + OnLoadEventCompleted?.Invoke(SceneNameFromHash(eventData.SceneHash), eventData.LoadSceneMode, eventData.ClientsCompleted, eventData.ClientsTimedOut); + break; + case SceneEventType.UnloadEventCompleted: + OnUnloadEventCompleted?.Invoke(SceneNameFromHash(eventData.SceneHash), eventData.LoadSceneMode, eventData.ClientsCompleted, eventData.ClientsTimedOut); + break; + } + } + catch (Exception ex) { - case SceneEventType.Load: - OnLoad?.Invoke(clientId, sceneName, eventData.LoadSceneMode, asyncOperation); - break; - case SceneEventType.Unload: - OnUnload?.Invoke(clientId, sceneName, asyncOperation); - break; - case SceneEventType.LoadComplete: - OnLoadComplete?.Invoke(clientId, sceneName, eventData.LoadSceneMode); - break; - case SceneEventType.UnloadComplete: - OnUnloadComplete?.Invoke(clientId, sceneName); - break; - case SceneEventType.LoadEventCompleted: - OnLoadEventCompleted?.Invoke(SceneNameFromHash(eventData.SceneHash), eventData.LoadSceneMode, eventData.ClientsCompleted, eventData.ClientsTimedOut); - break; - case SceneEventType.UnloadEventCompleted: - OnUnloadEventCompleted?.Invoke(SceneNameFromHash(eventData.SceneHash), eventData.LoadSceneMode, eventData.ClientsCompleted, eventData.ClientsTimedOut); - break; + Debug.LogException(ex); } } diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs index daf8d153c0..9ca4ee19da 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs @@ -341,13 +341,27 @@ internal void HandleNetworkPrefabDestroy(NetworkObject networkObjectInstance) { if (m_PrefabAssetToPrefabHandler.TryGetValue(networkPrefabAssetHash, out var prefabInstanceHandler)) { - prefabInstanceHandler.Destroy(networkObjectInstance); + try + { + prefabInstanceHandler.Destroy(networkObjectInstance); + } + catch (Exception ex) + { + Debug.LogException(ex); + } } } // Otherwise the NetworkObject is the source NetworkPrefab else if (m_PrefabAssetToPrefabHandler.TryGetValue(networkObjectInstanceHash, out var prefabInstanceHandler)) { - prefabInstanceHandler.Destroy(networkObjectInstance); + try + { + prefabInstanceHandler.Destroy(networkObjectInstance); + } + catch (Exception ex) + { + Debug.LogException(ex); + } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 28803104f4..e9f138cf84 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -522,7 +522,7 @@ internal void ChangeOwnership(NetworkObject networkObject, ulong clientId, bool { NetworkLog.LogErrorServer($"[{networkObject.name}][Session Owner Only] You cannot change ownership of a {nameof(NetworkObject)} that has the {NetworkObject.OwnershipStatus.SessionOwner} flag set!"); } - networkObject.OnOwnershipPermissionsFailure?.Invoke(NetworkObject.OwnershipPermissionsFailureStatus.SessionOwnerOnly); + networkObject.InvokeOwnershipPermissionsFailure(); return; } @@ -535,7 +535,7 @@ internal void ChangeOwnership(NetworkObject networkObject, ulong clientId, bool { NetworkLog.LogErrorServer($"[{networkObject.name}][Locked] You cannot change ownership while a {nameof(NetworkObject)} is locked!"); } - networkObject.OnOwnershipPermissionsFailure?.Invoke(NetworkObject.OwnershipPermissionsFailureStatus.Locked); + networkObject.InvokeOwnershipPermissionsFailure(); return; } if (networkObject.IsRequestInProgress) @@ -544,7 +544,7 @@ internal void ChangeOwnership(NetworkObject networkObject, ulong clientId, bool { NetworkLog.LogErrorServer($"[{networkObject.name}][Request Pending] You cannot change ownership while a {nameof(NetworkObject)} has a pending ownership request!"); } - networkObject.OnOwnershipPermissionsFailure?.Invoke(NetworkObject.OwnershipPermissionsFailureStatus.RequestInProgress); + networkObject.InvokeOwnershipPermissionsFailure(); return; } if (networkObject.IsOwnershipRequestRequired) @@ -553,7 +553,7 @@ internal void ChangeOwnership(NetworkObject networkObject, ulong clientId, bool { NetworkLog.LogErrorServer($"[{networkObject.name}][Request Required] You cannot change ownership directly if a {nameof(NetworkObject)} has the {NetworkObject.OwnershipStatus.RequestRequired} flag set!"); } - networkObject.OnOwnershipPermissionsFailure?.Invoke(NetworkObject.OwnershipPermissionsFailureStatus.RequestRequired); + networkObject.InvokeOwnershipPermissionsFailure(); return; } if (!networkObject.IsOwnershipTransferable) @@ -562,7 +562,7 @@ internal void ChangeOwnership(NetworkObject networkObject, ulong clientId, bool { NetworkLog.LogErrorServer($"[{networkObject.name}][Not transferrable] You cannot change ownership of a {nameof(NetworkObject)} that does not have the {NetworkObject.OwnershipStatus.Transferable} flag set!"); } - networkObject.OnOwnershipPermissionsFailure?.Invoke(NetworkObject.OwnershipPermissionsFailureStatus.NotTransferrable); + networkObject.InvokeOwnershipPermissionsFailure(); return; } } @@ -1915,7 +1915,7 @@ internal void UpdateObservedNetworkObjects(ulong clientId) else { // CheckObject visibility overrides SpawnWithObservers under this condition - if (sobj.CheckObjectVisibility(clientId)) + if (sobj.InvokeCheckObjectVisibility(clientId)) { sobj.AddObserver(clientId); } @@ -2312,8 +2312,19 @@ internal void DeferredDespawnUpdate(NetworkTime serverTime) // Double check to make sure user did not remove the callback if (networkObject.OnDeferredDespawnComplete != null) { + var despawnThisTick = false; + try + { + despawnThisTick = networkObject.OnDeferredDespawnComplete.Invoke(); + } + catch (Exception ex) + { + Debug.LogException(ex); + // If the user callback throws, despawn immediately to avoid throwing every tick. + despawnThisTick = true; + } // If the user callback returns true, then we despawn it this tick - if (networkObject.OnDeferredDespawnComplete.Invoke()) + if (despawnThisTick) { deferredObjectEntry.TickToDespawn = currentTick; } diff --git a/com.unity.netcode.gameobjects/Runtime/Timing/AnticipationSystem.cs b/com.unity.netcode.gameobjects/Runtime/Timing/AnticipationSystem.cs index 0e86fd24fc..33bbaa3f12 100644 --- a/com.unity.netcode.gameobjects/Runtime/Timing/AnticipationSystem.cs +++ b/com.unity.netcode.gameobjects/Runtime/Timing/AnticipationSystem.cs @@ -69,13 +69,28 @@ public void ProcessReanticipation() { foreach (var behaviour in item.OwnerObject.ChildNetworkBehaviours.Values) { - behaviour.OnReanticipate(lastRoundTripTime); + try + { + behaviour.OnReanticipate(lastRoundTripTime); + } + catch (System.Exception ex) + { + UnityEngine.Debug.LogException(ex); + } } item.ResetAnticipation(); } ObjectsToReanticipate.Clear(); - OnReanticipate?.Invoke(lastRoundTripTime); + + try + { + OnReanticipate?.Invoke(lastRoundTripTime); + } + catch (System.Exception ex) + { + UnityEngine.Debug.LogException(ex); + } } public void Update() diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/NetworkTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/NetworkTransport.cs index 13bf8784b3..1b9a484bdc 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/NetworkTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/NetworkTransport.cs @@ -50,7 +50,14 @@ public abstract class NetworkTransport : MonoBehaviour /// The time the event was received, as reported by Time.realtimeSinceStartup. protected void InvokeOnTransportEvent(NetworkEvent eventType, ulong clientId, ArraySegment payload, float receiveTime) { - OnTransportEvent?.Invoke(eventType, clientId, payload, receiveTime); + try + { + OnTransportEvent?.Invoke(eventType, clientId, payload, receiveTime); + } + catch (Exception ex) + { + Debug.LogException(ex); + } } ///