diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 6585a7dc93..57365ab991 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). -## [Unreleased] +## [2.7.0] - 2025-10-27 ### Added @@ -33,6 +33,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Added +- Adding this as a mock change. - `RpcInvokePermission` to control who has permission to invoke specific RPC methods. (#3731) - Added NetworkRigidbody documentation section. (#3664) - Added new fields to the `SceneMap` struct when using Unity 6.3 or higher. These fields allow referencing scene handles via the new `SceneHandle` struct. (#3734) @@ -60,6 +61,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed +- Initialization errors with NetworkAnimator. (#3767) - Multiple disconnect events from the same transport will no longer disconnect the host. (#3707) - Fixed NetworkTransform state synchronization issue when `NetworkTransform.SwitchTransformSpaceWhenParented` is enabled and the associated NetworkObject is parented multiple times in a single frame or within a couple of frames. (#3664) - Fixed issue when spawning, parenting, and immediately re-parenting when `NetworkTransform.SwitchTransformSpaceWhenParented` is enabled. (#3664) diff --git a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs index e6e70d69f6..4749012fd3 100644 --- a/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs +++ b/com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs @@ -1654,18 +1654,14 @@ private CustomAttribute CheckAndGetRpcAttribute(MethodDefinition methodDefinitio return null; } - var typeSystem = methodDefinition.Module.TypeSystem; - var hasInvokePermission = false; + bool hasInvokePermission = false, hasRequireOwnership = false; - CustomAttributeNamedArgument? invokePermissionAttribute = null; foreach (var argument in rpcAttribute.Fields) { switch (argument.Name) { case k_ServerRpcAttribute_RequireOwnership: - var requireOwnership = argument.Argument.Type == typeSystem.Boolean && (bool)argument.Argument.Value; - var invokePermissionArg = new CustomAttributeArgument(m_RpcInvokePermissions_TypeRef, requireOwnership ? RpcInvokePermission.Owner : RpcInvokePermission.Everyone); - invokePermissionAttribute = new CustomAttributeNamedArgument(k_RpcAttribute_InvokePermission, invokePermissionArg); + hasRequireOwnership = true; break; case k_RpcAttribute_InvokePermission: hasInvokePermission = true; @@ -1673,15 +1669,10 @@ private CustomAttribute CheckAndGetRpcAttribute(MethodDefinition methodDefinitio } } - if (invokePermissionAttribute != null) + if (hasInvokePermission && hasRequireOwnership) { - if (hasInvokePermission) - { - m_Diagnostics.AddError($"{methodDefinition.Name} cannot declare both RequireOwnership and InvokePermission!"); - return null; - } - - rpcAttribute.Fields.Add(invokePermissionAttribute.Value); + m_Diagnostics.AddError($"{methodDefinition.Name} cannot declare both RequireOwnership and InvokePermission!"); + return null; } diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs index b4ca574471..e5f8d7f9f1 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkAnimator.cs @@ -720,13 +720,16 @@ internal AnimationMessage GetAnimationMessage() return m_AnimationMessage; } - /// - public override void OnNetworkSpawn() + internal override void InternalOnNetworkPreSpawn(ref NetworkManager networkManager) { // Save internal state references - m_LocalNetworkManager = NetworkManager; + m_LocalNetworkManager = networkManager; DistributedAuthorityMode = m_LocalNetworkManager.DistributedAuthorityMode; + } + /// + public override void OnNetworkSpawn() + { // If there is no assigned Animator then generate a server network warning (logged locally and if applicable on the server-host side as well). if (m_Animator == null) { diff --git a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs index 1c2ed5dadb..6162ad91e2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Runtime/Components/NetworkTransform.cs @@ -4230,8 +4230,14 @@ internal BufferedLinearInterpolatorQuaternion GetRotationInterpolator() // Non-Authority private void UpdateInterpolation() { - // Select the time system relative to the type of NetworkManager instance. - var timeSystem = m_CachedNetworkManager.IsServer ? m_CachedNetworkManager.LocalTime : m_CachedNetworkManager.ServerTime; + // Use the local time because: + // Client-Server: + // Local time is server time on a host or server. + // Local time on clients takes latency into consideration. + // Distributed authority: + // Local time is used by the authority. + // Local time on non-authority takes latency into consid]eration. + var timeSystem = m_CachedNetworkManager.LocalTime; var currentTime = timeSystem.Time; #if COM_UNITY_MODULES_PHYSICS || COM_UNITY_MODULES_PHYSICS2D var cachedDeltaTime = m_UseRigidbodyForMotion ? m_CachedNetworkManager.RealTimeProvider.FixedDeltaTime : m_CachedNetworkManager.RealTimeProvider.DeltaTime; diff --git a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs index dc21b8b711..51f5dc2aeb 100644 --- a/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs @@ -103,11 +103,37 @@ public sealed class NetworkConnectionManager private static ProfilerMarker s_TransportDisconnect = new ProfilerMarker($"{nameof(NetworkManager)}.TransportDisconnect"); #endif + private string m_DisconnectReason; /// /// When disconnected from the server, the server may send a reason. If a reason was sent, this property will - /// tell client code what the reason was. It should be queried after the OnClientDisconnectCallback is called + /// provide disconnect information that will be followed by the server's disconnect reason. /// - public string DisconnectReason { get; internal set; } + /// + /// On a server or host, this value could no longer exist after all subscribed callbacks are invoked for the + /// client that disconnected. It is recommended to copy the message to some other property or field when + /// is invoked. + /// + public string DisconnectReason => GetDisconnectReason(); // fine as function because this call is infrequent + + /// + /// Gets the reason for why this client was disconnected if exists. + /// + /// disconnect reason if it exists, otherwise . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal string GetDisconnectReason() + { + // TODO: fix this properly + if (!string.IsNullOrEmpty(ServerDisconnectReason)) + { + return ServerDisconnectReason; + } + return m_DisconnectReason; + } + + /// + /// Updated by . + /// + internal string ServerDisconnectReason; /// /// The callback to invoke once a client connects. This callback is only ran on the server and on the local client that connects. @@ -537,21 +563,20 @@ internal void DataEventHandler(ulong transportClientId, ref ArraySegment p private void GenerateDisconnectInformation(ulong clientId, ulong transportClientId, string reason = null) { var header = $"[Disconnect Event][Client-{clientId}][TransportClientId-{transportClientId}]"; - var existingDisconnectReason = DisconnectReason; - var defaultMessage = Transport.DisconnectEventMessage; if (reason != null) { defaultMessage = $"{reason} {defaultMessage}"; } + // Just go ahead and set this whether client or server so any subscriptions to a disconnect event can check the DisconnectReason // to determine why the client disconnected - DisconnectReason = $"{header}[{Transport.DisconnectEvent}] {defaultMessage}"; - DisconnectReason = $"{DisconnectReason}\n{existingDisconnectReason}"; + m_DisconnectReason = $"{header}[{Transport.DisconnectEvent}] {defaultMessage}"; if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) { - NetworkLog.LogInfo($"{DisconnectReason}"); + var serverDisconnectReason = string.IsNullOrEmpty(ServerDisconnectReason) ? string.Empty : $"\n{ServerDisconnectReason}"; + NetworkLog.LogInfo($"{m_DisconnectReason}{serverDisconnectReason}"); } } @@ -1475,7 +1500,8 @@ internal void Initialize(NetworkManager networkManager) TransportIdToClientIdMap.Clear(); ClientsToApprove.Clear(); NetworkObject.OrphanChildren.Clear(); - DisconnectReason = string.Empty; + m_DisconnectReason = string.Empty; + ServerDisconnectReason = string.Empty; NetworkManager = networkManager; MessageManager = networkManager.MessageManager; diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index 92b4679fdf..a76066c237 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -335,7 +335,11 @@ internal FastBufferWriter __beginSendRpc(uint rpcMethodId, RpcParams rpcParams, throw new RpcException("This RPC can only be sent by the server."); } - if (attributeParams.InvokePermission == RpcInvokePermission.Owner && !IsOwner) +#pragma warning disable CS0618 // Type or member is obsolete + var requireOwnership = attributeParams.RequireOwnership; +#pragma warning restore CS0618 // Type or member is obsolete + + if ((requireOwnership || attributeParams.InvokePermission == RpcInvokePermission.Owner) && !IsOwner) { throw new RpcException("This RPC can only be sent by its owner."); } @@ -749,6 +753,8 @@ public virtual void OnNetworkDespawn() { } /// public virtual void OnNetworkPreDespawn() { } + internal virtual void InternalOnNetworkPreSpawn(ref NetworkManager networkManager) { } + internal void NetworkPreSpawn(ref NetworkManager networkManager, NetworkObject networkObject) { m_NetworkObject = networkObject; @@ -757,6 +763,15 @@ internal void NetworkPreSpawn(ref NetworkManager networkManager, NetworkObject n UpdateNetworkProperties(); + InternalOnNetworkPreSpawn(ref networkManager); + + // Exit early for disabled NetworkBehaviours. + // We still want the above values to be set. + if (!gameObject.activeInHierarchy) + { + return; + } + try { OnNetworkPreSpawn(ref networkManager); diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 74d1a412f0..e29097426d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -2082,6 +2082,12 @@ internal void InvokeBehaviourOnNetworkObjectParentChanged(NetworkObject parentNe { for (int i = 0; i < ChildNetworkBehaviours.Count; i++) { + // Any NetworkBehaviour that is not spawned and the associated GameObject is disabled should be + // skipped over (i.e. not supported). + if (!ChildNetworkBehaviours[i].IsSpawned && !ChildNetworkBehaviours[i].gameObject.activeInHierarchy) + { + continue; + } // Invoke internal notification ChildNetworkBehaviours[i].InternalOnNetworkObjectParentChanged(parentNetworkObject); // Invoke public notification @@ -2217,7 +2223,6 @@ public bool TrySetParent(NetworkObject parent, bool worldPositionStays = true) // DANGO-TODO: Do we want to worry about ownership permissions here? // It wouldn't make sense to not allow parenting, but keeping this note here as a reminder. var isAuthority = HasAuthority || (AllowOwnerToParent && IsOwner); - Debug.Log($"something is broken! isAuthority={isAuthority} | HasAuthority={HasAuthority} | (AllowOwnerToParent && IsOwner)={(AllowOwnerToParent && IsOwner)}"); // If we don't have authority and we are not shutting down, then don't allow any parenting. // If we are shutting down and don't have authority then allow it. @@ -2543,10 +2548,7 @@ internal void InvokeBehaviourNetworkPreSpawn() var networkManager = NetworkManager; for (int i = 0; i < ChildNetworkBehaviours.Count; i++) { - if (ChildNetworkBehaviours[i].gameObject.activeInHierarchy) - { - ChildNetworkBehaviours[i].NetworkPreSpawn(ref networkManager, this); - } + ChildNetworkBehaviours[i].NetworkPreSpawn(ref networkManager, this); } } @@ -2558,10 +2560,9 @@ internal void InvokeBehaviourNetworkSpawn() { if (!childBehaviour.gameObject.activeInHierarchy) { - Debug.LogWarning($"{childBehaviour.gameObject.name} is disabled! Netcode for GameObjects does not support spawning disabled NetworkBehaviours! The {childBehaviour.GetType().Name} component was skipped during spawn!"); + Debug.LogWarning($"{GenerateDisabledNetworkBehaviourWarning(childBehaviour)}"); continue; } - childBehaviour.InternalOnNetworkSpawn(); } } @@ -2619,6 +2620,11 @@ internal void InvokeBehaviourNetworkDespawn() private List m_ChildNetworkBehaviours; + internal string GenerateDisabledNetworkBehaviourWarning(NetworkBehaviour networkBehaviour) + { + return $"[{name}][{networkBehaviour.GetType().Name}][{nameof(isActiveAndEnabled)}: {networkBehaviour.isActiveAndEnabled}] Disabled {nameof(NetworkBehaviour)}s will be excluded from spawning and synchronization!"; + } + internal List ChildNetworkBehaviours { get diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/DisconnectReasonMessage.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DisconnectReasonMessage.cs similarity index 82% rename from com.unity.netcode.gameobjects/Runtime/Messaging/DisconnectReasonMessage.cs rename to com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DisconnectReasonMessage.cs index b26982ea1f..1a8bf88dcc 100644 --- a/com.unity.netcode.gameobjects/Runtime/Messaging/DisconnectReasonMessage.cs +++ b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DisconnectReasonMessage.cs @@ -10,7 +10,7 @@ public void Serialize(FastBufferWriter writer, int targetVersion) { string reasonSent = Reason ?? string.Empty; - // Since we don't send a ConnectionApprovedMessage, the version for this message is encded with the message itself. + // Since we don't send a ConnectionApprovedMessage, the version for this message is encoded with the message itself. // However, note that we HAVE received a ConnectionRequestMessage, so we DO have a valid targetVersion on this side of things. // We just have to make sure the receiving side knows what version we sent it, since whoever has the higher version number is responsible for versioning and they may be the one with the higher version number. BytePacker.WriteValueBitPacked(writer, Version); @@ -37,7 +37,10 @@ public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int public void Handle(ref NetworkContext context) { - ((NetworkManager)context.SystemOwner).ConnectionManager.DisconnectReason = Reason; + // Always apply the server-side generated disconnect reason to the server specific disconnect reason. + // This is combined with the additional disconnect information when getting NetworkManager.DisconnectReason + // (NetworkConnectionManager.DisconnectReason). + ((NetworkManager)context.SystemOwner).ConnectionManager.ServerDisconnectReason = Reason; } }; } diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/DisconnectReasonMessage.cs.meta b/com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DisconnectReasonMessage.cs.meta similarity index 100% rename from com.unity.netcode.gameobjects/Runtime/Messaging/DisconnectReasonMessage.cs.meta rename to com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DisconnectReasonMessage.cs.meta diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Build/BuildTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Build/BuildTests.cs index 8bf2663d66..7ee23412bf 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Build/BuildTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Build/BuildTests.cs @@ -1,4 +1,3 @@ -#if !MULTIPLAYER_TOOLS using System.IO; using System.Reflection; using NUnit.Framework; @@ -41,4 +40,3 @@ public void BasicBuildTest() } } } -#endif diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/ConnectionApproval.cs b/com.unity.netcode.gameobjects/Tests/Runtime/ConnectionApproval.cs index fdf8cc3836..b630136c61 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/ConnectionApproval.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/ConnectionApproval.cs @@ -92,7 +92,7 @@ protected override void OnServerAndClientsCreated() private void Client_OnClientDisconnectCallback(ulong clientId) { m_ClientNetworkManagers[0].OnClientDisconnectCallback -= Client_OnClientDisconnectCallback; - m_ClientDisconnectReasonValidated = m_ClientNetworkManagers[0].LocalClientId == clientId && m_ClientNetworkManagers[0].DisconnectReason == k_InvalidToken; + m_ClientDisconnectReasonValidated = m_ClientNetworkManagers[0].LocalClientId == clientId && m_ClientNetworkManagers[0].DisconnectReason.Contains(k_InvalidToken); } private bool ClientAndHostValidated() diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/DisconnectTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/DisconnectTests.cs index e7ad3595e2..82669d7187 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/DisconnectTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/DisconnectTests.cs @@ -35,7 +35,7 @@ public enum ClientDisconnectType ClientDisconnectsFromServer } - protected override int NumberOfClients => 1; + protected override int NumberOfClients => 2; private OwnerPersistence m_OwnerPersistence; private ClientDisconnectType m_ClientDisconnectType; @@ -99,8 +99,14 @@ private void OnConnectionEvent(NetworkManager networkManager, ConnectionEventDat { return; } - - m_DisconnectedEvent.Add(networkManager, connectionEventData); + if (!m_DisconnectedEvent.ContainsKey(networkManager)) + { + m_DisconnectedEvent.Add(networkManager, connectionEventData); + } + else + { + m_DisconnectedEvent[networkManager] = connectionEventData; + } } /// @@ -144,10 +150,29 @@ private bool DoesServerStillHaveSpawnedPlayerObject() return !m_ServerNetworkManager.SpawnManager.SpawnedObjects.Any(x => x.Value.IsPlayerObject && x.Value.OwnerClientId == m_ClientId); } + /// + /// Used to compare against when the client-side disconnects + /// + private int m_ExpectedConnectedClientCount; + [UnityTest] public IEnumerator ClientPlayerDisconnected([Values] ClientDisconnectType clientDisconnectType) { - var clientNetworkManager = m_ClientNetworkManagers[0]; + // Cycling through 2 (or more) clients disconnecting + for (int i = m_ClientNetworkManagers.Length - 1; i >= 0; i--) + { + var client = m_ClientNetworkManagers[i]; + if (client.LocalClientId == m_ServerNetworkManager.LocalClientId) + { + continue; + } + m_ExpectedConnectedClientCount = m_ServerNetworkManager.ConnectedClients.Count; + yield return DisconnectClient(m_ClientNetworkManagers[i], clientDisconnectType); + } + } + + private IEnumerator DisconnectClient(NetworkManager clientNetworkManager, ClientDisconnectType clientDisconnectType) + { m_ClientId = clientNetworkManager.LocalClientId; m_ClientDisconnectType = clientDisconnectType; @@ -163,6 +188,9 @@ public IEnumerator ClientPlayerDisconnected([Values] ClientDisconnectType client clientNetworkManager.OnConnectionEvent += OnConnectionEvent; m_ServerNetworkManager.OnConnectionEvent += OnConnectionEvent; m_ServerNetworkManager.DisconnectClient(m_ClientId); + Assert.True(!string.IsNullOrEmpty(m_ServerNetworkManager.DisconnectReason), "Server-side disconnect notification should have been generated but was not!"); + var splitByDisconnectEvent = m_ServerNetworkManager.DisconnectReason.Split("[Disconnect Event]"); + Assert.IsTrue(splitByDisconnectEvent.Length <= 2, $"Multiple disconnect events found in the server-side disconnect reason:\n {m_ServerNetworkManager.DisconnectReason}"); } else { @@ -187,13 +215,14 @@ public IEnumerator ClientPlayerDisconnected([Values] ClientDisconnectType client } else { + m_ExpectedConnectedClientCount -= 1; Assert.IsTrue(m_DisconnectedEvent.ContainsKey(m_ServerNetworkManager), $"Could not find the server {nameof(NetworkManager)} disconnect event entry!"); Assert.IsTrue(m_DisconnectedEvent[m_ServerNetworkManager].ClientId == m_ClientId, $"Expected ClientID {m_ClientId} but found ClientID {m_DisconnectedEvent[m_ServerNetworkManager].ClientId} for the server {nameof(NetworkManager)} disconnect event entry!"); Assert.IsTrue(m_DisconnectedEvent.ContainsKey(clientNetworkManager), $"Could not find the client {nameof(NetworkManager)} disconnect event entry!"); Assert.IsTrue(m_DisconnectedEvent[clientNetworkManager].ClientId == m_ClientId, $"Expected ClientID {m_ClientId} but found ClientID {m_DisconnectedEvent[m_ServerNetworkManager].ClientId} for the client {nameof(NetworkManager)} disconnect event entry!"); - Assert.IsTrue(m_ServerNetworkManager.ConnectedClientsIds.Count == 1, $"Expected connected client identifiers count to be 1 but it was {m_ServerNetworkManager.ConnectedClientsIds.Count}!"); - Assert.IsTrue(m_ServerNetworkManager.ConnectedClients.Count == 1, $"Expected connected client identifiers count to be 1 but it was {m_ServerNetworkManager.ConnectedClients.Count}!"); - Assert.IsTrue(m_ServerNetworkManager.ConnectedClientsList.Count == 1, $"Expected connected client identifiers count to be 1 but it was {m_ServerNetworkManager.ConnectedClientsList.Count}!"); + Assert.IsTrue(m_ServerNetworkManager.ConnectedClientsIds.Count == m_ExpectedConnectedClientCount, $"Expected connected client identifiers count to be {m_ExpectedConnectedClientCount} but it was {m_ServerNetworkManager.ConnectedClientsIds.Count}!"); + Assert.IsTrue(m_ServerNetworkManager.ConnectedClients.Count == m_ExpectedConnectedClientCount, $"Expected connected client identifiers count to be {m_ExpectedConnectedClientCount} but it was {m_ServerNetworkManager.ConnectedClients.Count}!"); + Assert.IsTrue(m_ServerNetworkManager.ConnectedClientsList.Count == m_ExpectedConnectedClientCount, $"Expected connected client identifiers count to be {m_ExpectedConnectedClientCount} but it was {m_ServerNetworkManager.ConnectedClientsList.Count}!"); } if (m_OwnerPersistence == OwnerPersistence.DestroyWithOwner) @@ -244,6 +273,8 @@ public IEnumerator ClientPlayerDisconnected([Values] ClientDisconnectType client Assert.IsNull(m_ServerNetworkManager.ConnectionManager.LocalClient.PlayerObject, $"{m_ServerNetworkManager.name} still has Player assigned!"); } } + m_DisconnectedEvent.Clear(); + m_ClientDisconnected = false; } } } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/DisconnectReasonTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/DisconnectReasonTests.cs index e293be3e64..3d65334c86 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/DisconnectReasonTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Messaging/DisconnectReasonTests.cs @@ -57,9 +57,8 @@ public IEnumerator DisconnectReasonTest() yield return null; } - Assert.AreEqual(m_ClientNetworkManagers[0].DisconnectReason, "Bogus reason 1"); - Assert.AreEqual(m_ClientNetworkManagers[1].DisconnectReason, "Bogus reason 2"); - + Assert.True(m_ClientNetworkManagers[0].DisconnectReason.Contains("Bogus reason 1"), $"[Client-{m_ClientNetworkManagers[0].LocalClientId}] Disconnect reason should contain \"Bogus reason 1\" but is: {m_ClientNetworkManagers[0].DisconnectReason}"); + Assert.True(m_ClientNetworkManagers[1].DisconnectReason.Contains("Bogus reason 2"), $"[Client-{m_ClientNetworkManagers[0].LocalClientId}] Disconnect reason should contain \"Bogus reason 2\" but is: {m_ClientNetworkManagers[1].DisconnectReason}"); Debug.Assert(m_DisconnectCount == 2); } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourGenericTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourGenericTests.cs index 91e3124932..5569278aa0 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourGenericTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkBehaviourGenericTests.cs @@ -1,4 +1,6 @@ using System.Collections; +using System.Linq; +using System.Text.RegularExpressions; using NUnit.Framework; using Unity.Netcode.Components; using Unity.Netcode.TestHelpers.Runtime; @@ -16,6 +18,8 @@ internal class NetworkBehaviourGenericTests : NetcodeIntegrationTest private bool m_AllowServerToStart; + private GameObject m_PrefabToSpawn; + protected override bool CanStartServerAndClients() { return m_AllowServerToStart; @@ -32,45 +36,95 @@ public override void OnNetworkDespawn() } } + protected override void OnServerAndClientsCreated() + { + m_PrefabToSpawn = CreateNetworkObjectPrefab("TestPrefab"); + + var childObject = new GameObject + { + name = "ChildObject" + }; + childObject.transform.parent = m_PrefabToSpawn.transform; + childObject.AddComponent(); + base.OnServerAndClientsCreated(); + } + protected override IEnumerator OnSetup() { m_AllowServerToStart = false; return base.OnSetup(); } + + protected override void OnNewClientCreated(NetworkManager networkManager) + { + networkManager.NetworkConfig.Prefabs.Add(new NetworkPrefab() + { + Prefab = m_PrefabToSpawn, + }); + base.OnNewClientCreated(networkManager); + } + + /// - /// This validates the fix for when a child GameObject with a NetworkBehaviour + /// This validates: + /// - The fix for when a child GameObject with a NetworkBehaviour /// is deleted while the parent GameObject with a NetworkObject is spawned and /// is not deleted until a later time would cause an exception due to the /// NetworkBehaviour not being removed from the NetworkObject.ChildNetworkBehaviours /// list. + /// - When a NetworkBehaviour is disabled but the associated GameObject is enabled, + /// the object spawns without any issues. /// [UnityTest] - public IEnumerator ValidatedDisableddNetworkBehaviourWarning() + public IEnumerator ValidatedDisableddNetworkBehaviourWarning([Values] bool disableGameObject) { m_AllowServerToStart = true; - yield return s_DefaultWaitForTick; - // Now just start the Host yield return StartServerAndClients(); - var parentObject = new GameObject(); - var childObject = new GameObject + // Now join a new client to make sure a connected client spawns the instance. + yield return CreateAndStartNewClient(); + + // Adjust the prefab to either have the child GameObject completely disabled or the NetworkBehaviour + // disabled. + var childBehaviour = m_PrefabToSpawn.GetComponentInChildren(true); + if (disableGameObject) { - name = "ChildObject" - }; - childObject.transform.parent = parentObject.transform; - var parentNetworkObject = parentObject.AddComponent(); - var childBehaviour = childObject.AddComponent(); + childBehaviour.enabled = true; + childBehaviour.gameObject.SetActive(false); + } + else + { + childBehaviour.enabled = false; + childBehaviour.gameObject.SetActive(true); + } + // Now create an instance of the prefab + var instance = Object.Instantiate(m_PrefabToSpawn); + var instanceNetworkObject = instance.GetComponent(); + // When the GameObject is disabled, check for the warning. + if (disableGameObject) + { + // Generate the expected warning message + var expectedWarning = instanceNetworkObject.GenerateDisabledNetworkBehaviourWarning(instanceNetworkObject.GetComponentInChildren(true)); + var expectedSplit = expectedWarning.Split(']'); + var expectedWarningBody = expectedSplit.Last(); + LogAssert.Expect(LogType.Warning, new Regex($".*{expectedWarningBody}*.")); + } - // Set the child object to be inactive in the hierarchy - childObject.SetActive(false); + // Spawn the instance + SpawnObjectInstance(instanceNetworkObject, m_ServerNetworkManager); + // Asure the connected client spawned the object first + yield return WaitForSpawnedOnAllOrTimeOut(instanceNetworkObject); + AssertOnTimeout($"Not all clients spawned {instanceNetworkObject.name}!"); - LogAssert.Expect(LogType.Warning, $"{childObject.name} is disabled! Netcode for GameObjects does not support spawning disabled NetworkBehaviours! The {childBehaviour.GetType().Name} component was skipped during spawn!"); + // Now join a new client to make sure the client synchronizes with the disabled GameObject or NetworkBehaviour component. + yield return CreateAndStartNewClient(); - parentNetworkObject.Spawn(); - yield return s_DefaultWaitForTick; + // Asure the newly connected client synchronizes the spawned object correctly + yield return WaitForSpawnedOnAllOrTimeOut(instanceNetworkObject); + AssertOnTimeout($"Not all clients spawned {instanceNetworkObject.name}!"); } /// diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformBase.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformBase.cs index 7d9635dd76..ba03c78fb4 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformBase.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformBase.cs @@ -230,6 +230,11 @@ protected override void OnCreatePlayerPrefab() { var networkTransformTestComponent = m_PlayerPrefab.AddComponent(); networkTransformTestComponent.ServerAuthority = m_Authority == Authority.ServerAuthority; + // Handle setting up additional transform settings for the current test here. + networkTransformTestComponent.UseUnreliableDeltas = UseUnreliableDeltas(); + networkTransformTestComponent.UseHalfFloatPrecision = m_Precision == Precision.Half; + networkTransformTestComponent.UseQuaternionSynchronization = m_Rotation == Rotation.Quaternion; + networkTransformTestComponent.UseQuaternionCompression = m_RotationCompression == RotationCompression.QuaternionCompress; } protected override void OnServerAndClientsCreated() @@ -291,19 +296,6 @@ protected virtual void OnClientsAndServerConnectedSetup() // Get the NetworkTransformTestComponent to make sure the client side is ready before starting test m_AuthoritativeTransform = m_AuthoritativePlayer.GetComponent(); m_NonAuthoritativeTransform = m_NonAuthoritativePlayer.GetComponent(); - - // Setup whether we are or are not using unreliable deltas - m_AuthoritativeTransform.UseUnreliableDeltas = UseUnreliableDeltas(); - m_NonAuthoritativeTransform.UseUnreliableDeltas = UseUnreliableDeltas(); - - m_AuthoritativeTransform.UseHalfFloatPrecision = m_Precision == Precision.Half; - m_AuthoritativeTransform.UseQuaternionSynchronization = m_Rotation == Rotation.Quaternion; - m_AuthoritativeTransform.UseQuaternionCompression = m_RotationCompression == RotationCompression.QuaternionCompress; - m_NonAuthoritativeTransform.UseHalfFloatPrecision = m_Precision == Precision.Half; - m_NonAuthoritativeTransform.UseQuaternionSynchronization = m_Rotation == Rotation.Quaternion; - m_NonAuthoritativeTransform.UseQuaternionCompression = m_RotationCompression == RotationCompression.QuaternionCompress; - - m_OwnerTransform = m_AuthoritativeTransform.IsOwner ? m_AuthoritativeTransform : m_NonAuthoritativeTransform; } @@ -781,7 +773,6 @@ internal class NetworkTransformTestComponent : NetworkTransform protected override void OnAuthorityPushTransformState(ref NetworkTransformState networkTransformState) { - Debug.Log($"[Auth]{name} State Pushed."); StatePushed = true; AuthorityLastSentState = networkTransformState; AuthorityPushedTransformState?.Invoke(ref networkTransformState); @@ -792,7 +783,6 @@ protected override void OnAuthorityPushTransformState(ref NetworkTransformState public bool StateUpdated { get; internal set; } protected override void OnNetworkTransformStateUpdated(ref NetworkTransformState oldState, ref NetworkTransformState newState) { - Debug.Log($"[Non-Auth]{name} State Updated."); StateUpdated = true; base.OnNetworkTransformStateUpdated(ref oldState, ref newState); } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs index 6ab913866b..67e66b61b3 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs @@ -1,4 +1,3 @@ -#if !MULTIPLAYER_TOOLS using NUnit.Framework; using Unity.Netcode.Components; using Unity.Netcode.TestHelpers.Runtime; @@ -916,4 +915,3 @@ public void TestThresholds( } } } -#endif diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformTests.cs index 43e03491a2..e76729f9c5 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformTests.cs @@ -13,70 +13,57 @@ namespace Unity.Netcode.RuntimeTests [TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.None, Rotation.Euler, Precision.Full, NetworkTransform.InterpolationTypes.LegacyLerp)] [TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.None, Rotation.Euler, Precision.Full, NetworkTransform.InterpolationTypes.Lerp)] [TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.None, Rotation.Euler, Precision.Full, NetworkTransform.InterpolationTypes.SmoothDampening)] -#if !MULTIPLAYER_TOOLS [TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.None, Rotation.Euler, Precision.Half, NetworkTransform.InterpolationTypes.LegacyLerp)] [TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Full, NetworkTransform.InterpolationTypes.LegacyLerp)] [TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Half, NetworkTransform.InterpolationTypes.LegacyLerp)] [TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Full, NetworkTransform.InterpolationTypes.LegacyLerp)] [TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Half, NetworkTransform.InterpolationTypes.LegacyLerp)] - [TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.None, Rotation.Euler, Precision.Half, NetworkTransform.InterpolationTypes.Lerp)] [TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Full, NetworkTransform.InterpolationTypes.Lerp)] [TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Half, NetworkTransform.InterpolationTypes.Lerp)] [TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Full, NetworkTransform.InterpolationTypes.Lerp)] [TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Half, NetworkTransform.InterpolationTypes.Lerp)] - [TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.None, Rotation.Euler, Precision.Half, NetworkTransform.InterpolationTypes.SmoothDampening)] [TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Full, NetworkTransform.InterpolationTypes.SmoothDampening)] [TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Half, NetworkTransform.InterpolationTypes.SmoothDampening)] [TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Full, NetworkTransform.InterpolationTypes.SmoothDampening)] [TestFixture(HostOrServer.DAHost, Authority.OwnerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Half, NetworkTransform.InterpolationTypes.SmoothDampening)] - -#endif [TestFixture(HostOrServer.Server, Authority.ServerAuthority, RotationCompression.None, Rotation.Euler, Precision.Full, NetworkTransform.InterpolationTypes.LegacyLerp)] [TestFixture(HostOrServer.Server, Authority.ServerAuthority, RotationCompression.None, Rotation.Euler, Precision.Full, NetworkTransform.InterpolationTypes.Lerp)] [TestFixture(HostOrServer.Server, Authority.ServerAuthority, RotationCompression.None, Rotation.Euler, Precision.Full, NetworkTransform.InterpolationTypes.SmoothDampening)] -#if !MULTIPLAYER_TOOLS [TestFixture(HostOrServer.Server, Authority.ServerAuthority, RotationCompression.None, Rotation.Euler, Precision.Half, NetworkTransform.InterpolationTypes.LegacyLerp)] [TestFixture(HostOrServer.Server, Authority.ServerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Full, NetworkTransform.InterpolationTypes.LegacyLerp)] [TestFixture(HostOrServer.Server, Authority.ServerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Half, NetworkTransform.InterpolationTypes.LegacyLerp)] [TestFixture(HostOrServer.Server, Authority.ServerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Full, NetworkTransform.InterpolationTypes.LegacyLerp)] [TestFixture(HostOrServer.Server, Authority.ServerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Half, NetworkTransform.InterpolationTypes.LegacyLerp)] - [TestFixture(HostOrServer.Server, Authority.ServerAuthority, RotationCompression.None, Rotation.Euler, Precision.Half, NetworkTransform.InterpolationTypes.Lerp)] [TestFixture(HostOrServer.Server, Authority.ServerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Full, NetworkTransform.InterpolationTypes.Lerp)] [TestFixture(HostOrServer.Server, Authority.ServerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Half, NetworkTransform.InterpolationTypes.Lerp)] [TestFixture(HostOrServer.Server, Authority.ServerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Full, NetworkTransform.InterpolationTypes.Lerp)] [TestFixture(HostOrServer.Server, Authority.ServerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Half, NetworkTransform.InterpolationTypes.Lerp)] - [TestFixture(HostOrServer.Server, Authority.ServerAuthority, RotationCompression.None, Rotation.Euler, Precision.Half, NetworkTransform.InterpolationTypes.SmoothDampening)] [TestFixture(HostOrServer.Server, Authority.ServerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Full, NetworkTransform.InterpolationTypes.SmoothDampening)] [TestFixture(HostOrServer.Server, Authority.ServerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Half, NetworkTransform.InterpolationTypes.SmoothDampening)] [TestFixture(HostOrServer.Server, Authority.ServerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Full, NetworkTransform.InterpolationTypes.SmoothDampening)] [TestFixture(HostOrServer.Server, Authority.ServerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Half, NetworkTransform.InterpolationTypes.SmoothDampening)] -#endif [TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.None, Rotation.Euler, Precision.Full, NetworkTransform.InterpolationTypes.LegacyLerp)] [TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.None, Rotation.Euler, Precision.Full, NetworkTransform.InterpolationTypes.Lerp)] [TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.None, Rotation.Euler, Precision.Full, NetworkTransform.InterpolationTypes.SmoothDampening)] -#if !MULTIPLAYER_TOOLS [TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.None, Rotation.Euler, Precision.Half, NetworkTransform.InterpolationTypes.LegacyLerp)] [TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Full, NetworkTransform.InterpolationTypes.LegacyLerp)] [TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Half, NetworkTransform.InterpolationTypes.LegacyLerp)] [TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Full, NetworkTransform.InterpolationTypes.LegacyLerp)] [TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Half, NetworkTransform.InterpolationTypes.LegacyLerp)] - [TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.None, Rotation.Euler, Precision.Half, NetworkTransform.InterpolationTypes.Lerp)] [TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Full, NetworkTransform.InterpolationTypes.Lerp)] [TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Half, NetworkTransform.InterpolationTypes.Lerp)] [TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Full, NetworkTransform.InterpolationTypes.Lerp)] [TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Half, NetworkTransform.InterpolationTypes.Lerp)] - [TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.None, Rotation.Euler, Precision.Half, NetworkTransform.InterpolationTypes.SmoothDampening)] [TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Full, NetworkTransform.InterpolationTypes.SmoothDampening)] [TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.None, Rotation.Quaternion, Precision.Half, NetworkTransform.InterpolationTypes.SmoothDampening)] [TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Full, NetworkTransform.InterpolationTypes.SmoothDampening)] [TestFixture(HostOrServer.Host, Authority.OwnerAuthority, RotationCompression.QuaternionCompress, Rotation.Quaternion, Precision.Half, NetworkTransform.InterpolationTypes.SmoothDampening)] -#endif internal class NetworkTransformTests : NetworkTransformBase { protected const int k_TickRate = 60; @@ -162,8 +149,6 @@ private void AllChildrenLocalTransformValuesMatch(bool useSubChild, ChildrenTran } } -#if !MULTIPLAYER_TOOLS - private void UpdateTransformLocal(NetworkTransform networkTransformTestComponent) { networkTransformTestComponent.transform.localPosition += GetRandomVector3(0.5f, 2.0f); @@ -698,7 +683,6 @@ public void LateJoiningPlayerInitialScaleValues([Values] TransformSpace testLoca } } } -#endif /// /// Tests changing all axial values one at a time. diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkVariableTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkVariableTests.cs index 06ef4969d0..bde5422222 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkVariableTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkVariableTests.cs @@ -216,9 +216,7 @@ internal enum Serialization Default, } -#if !MULTIPLAYER_TOOLS [TestFixture(Serialization.EnsureLengthSafety)] -#endif [TestFixture(Serialization.Default)] internal class NetworkVariableTests : NetcodeIntegrationTest { @@ -339,7 +337,6 @@ private void InitializeServerAndClients(HostOrServer useHost) TimeTravelToNextTick(); } -#if !MULTIPLAYER_TOOLS /// /// Runs generalized tests on all predefined NetworkVariable types /// @@ -4884,4 +4881,3 @@ protected override IEnumerator OnTearDown() } } } -#endif diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/PeerDisconnectCallbackTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/PeerDisconnectCallbackTests.cs index 896aa90bf5..b7512753a4 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/PeerDisconnectCallbackTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/PeerDisconnectCallbackTests.cs @@ -83,9 +83,21 @@ private void OnConnectionEventCallback(NetworkManager networkManager, Connection } } + private bool m_TargetClientShutdown; + private NetworkManager m_TargetClient; + + private void ClientToDisconnect_OnClientStopped(bool wasHost) + { + m_TargetClient.OnClientStopped -= ClientToDisconnect_OnClientStopped; + m_TargetClientShutdown = true; + } + [UnityTest] public IEnumerator TestPeerDisconnectCallback([Values] ClientDisconnectType clientDisconnectType, [Values(1ul, 2ul, 3ul)] ulong disconnectedClient) { + m_TargetClientShutdown = false; + m_TargetClient = m_ClientNetworkManagers[disconnectedClient - 1]; + m_TargetClient.OnClientStopped += ClientToDisconnect_OnClientStopped; foreach (var client in m_ClientNetworkManagers) { client.OnConnectionEvent += OnConnectionEventCallback; @@ -129,12 +141,15 @@ public IEnumerator TestPeerDisconnectCallback([Values] ClientDisconnectType clie } else { - yield return StopOneClient(m_ClientNetworkManagers[disconnectedClient - 1]); + yield return StopOneClient(m_TargetClient); } yield return WaitForConditionOrTimeOut(hooks); + AssertOnTimeout($"Timed out waiting for all clients to receive the {nameof(ClientDisconnectedMessage)}!"); - Assert.False(s_GlobalTimeoutHelper.TimedOut); + // Make sure the target client is shutdown before performing validation + yield return WaitForConditionOrTimeOut(() => m_TargetClientShutdown); + AssertOnTimeout($"Timed out waiting for {m_TargetClient.name} to shutdown!"); foreach (var client in m_ClientNetworkManagers) { @@ -182,5 +197,6 @@ public IEnumerator TestPeerDisconnectCallback([Values] ClientDisconnectType clie // Host receives peer disconnect, dedicated server does not Assert.AreEqual(m_UseHost ? 3 : 2, m_PeerDisconnectCount); } + } } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Rpc/RpcInvocationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Rpc/RpcInvocationTests.cs index ba9708da9b..db6be63980 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Rpc/RpcInvocationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Rpc/RpcInvocationTests.cs @@ -98,11 +98,13 @@ public IEnumerator RpcInvokePermissionSendingTests() foreach (var (manager, instance) in m_InvokeInstances) { instance.ExpectedCallCounts[nameof(InvokePermissionBehaviour.OwnerInvokePermissionRpc)] = 1; + instance.ExpectedCallCounts[nameof(InvokePermissionBehaviour.OwnerRequireOwnershipRpc)] = 1; var threwException = false; try { instance.OwnerInvokePermissionRpc(); + instance.OwnerRequireOwnershipRpc(); } catch (RpcException) { @@ -167,8 +169,10 @@ public IEnumerator RpcInvokePermissionReceivingTests() foreach (var (manager, instance) in m_InvokeInstances) { instance.ExpectedCallCounts[nameof(InvokePermissionBehaviour.OwnerInvokePermissionRpc)] = 1; + instance.ExpectedCallCounts[nameof(InvokePermissionBehaviour.OwnerRequireOwnershipRpc)] = 1; SendUncheckedMessage(manager, instance, nameof(InvokePermissionBehaviour.OwnerInvokePermissionRpc)); + SendUncheckedMessage(manager, instance, nameof(InvokePermissionBehaviour.OwnerRequireOwnershipRpc)); } yield return WaitForConditionOrTimeOut(AllExpectedCallsReceived); @@ -445,6 +449,15 @@ public void ServerInvokePermissionRpc() TrackRpcCalled(GetCaller()); } + +#pragma warning disable CS0618 // Type or member is obsolete + [Rpc(SendTo.Everyone, RequireOwnership = true)] +#pragma warning restore CS0618 // Type or member is obsolete + public void OwnerRequireOwnershipRpc() + { + TrackRpcCalled(GetCaller()); + } + [Rpc(SendTo.Everyone, InvokePermission = RpcInvokePermission.Owner)] public void OwnerInvokePermissionRpc() { diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Rpc/UniversalRpcTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Rpc/UniversalRpcTests.cs index be1a15de20..e7073a8f80 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Rpc/UniversalRpcTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Rpc/UniversalRpcTests.cs @@ -1,4 +1,4 @@ -#if !MULTIPLAYER_TOOLS && !NGO_MINIMALPROJECT +#if !NGO_MINIMALPROJECT using System; using System.Collections; using System.Collections.Generic; @@ -1231,7 +1231,7 @@ private void RunTestTypeA(TestTypes testType) { foreach (var sendTo in sendToValues) { - UnityEngine.Debug.Log($"[{testType}][{sendTo}]"); + VerboseDebug($"[{testType}][{sendTo}]"); for (ulong objectOwner = 0; objectOwner <= numberOfClientsULong; objectOwner++) { for (ulong sender = 0; sender <= numberOfClientsULong; sender++) diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Timing/TimeIntegrationTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Timing/TimeIntegrationTest.cs index f6f8147dc4..d5e404a16e 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Timing/TimeIntegrationTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Timing/TimeIntegrationTest.cs @@ -1,4 +1,3 @@ -#if !MULTIPLAYER_TOOLS using System; using System.Collections; using NUnit.Framework; @@ -200,4 +199,3 @@ public override int GetHashCode() } } } -#endif diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TransformInterpolationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TransformInterpolationTests.cs index 840204d4a5..4e29237a82 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TransformInterpolationTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TransformInterpolationTests.cs @@ -1,4 +1,3 @@ -#if !MULTIPLAYER_TOOLS using System.Collections; using NUnit.Framework; using Unity.Netcode.Components; @@ -233,4 +232,3 @@ public IEnumerator TransformInterpolationTest() } } } -#endif diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/ParentingInSceneObjectsTests.cs b/testproject/Assets/Tests/Runtime/ObjectParenting/ParentingInSceneObjectsTests.cs index b227eb2fb8..ac5a7b47d8 100644 --- a/testproject/Assets/Tests/Runtime/ObjectParenting/ParentingInSceneObjectsTests.cs +++ b/testproject/Assets/Tests/Runtime/ObjectParenting/ParentingInSceneObjectsTests.cs @@ -332,21 +332,21 @@ public IEnumerator InSceneParentingTest([Values] ParentingSpace parentingSpace) { InSceneParentChildHandler.AuthorityRootParent.DeparentSetValuesAndReparent(); - yield return WaitForConditionOrTimeOut(ValidateClientsAgainstAuthorityTransformValues); + yield return WaitForConditionOrTimeOut(() => ValidateAllChildrenParentingStatus(true)); if (s_GlobalTimeoutHelper.TimedOut) { InSceneParentChildHandler.AuthorityRootParent.CheckChildren(); yield return debugWait; } - AssertOnTimeout($"[Final Pass][Deparent-Reparent-{i}] Timed out waiting for all clients transform values to match the server transform values!\n {m_ErrorValidationLog}"); + AssertOnTimeout($"[Final Pass][Deparent-Reparent-{i}] Timed out waiting for all children to be removed from their parent!\n {m_ErrorValidationLog}"); - yield return WaitForConditionOrTimeOut(() => ValidateAllChildrenParentingStatus(true)); + yield return WaitForConditionOrTimeOut(ValidateClientsAgainstAuthorityTransformValues); if (s_GlobalTimeoutHelper.TimedOut) { InSceneParentChildHandler.AuthorityRootParent.CheckChildren(); yield return debugWait; } - AssertOnTimeout($"[Final Pass][Deparent-Reparent-{i}] Timed out waiting for all children to be removed from their parent!\n {m_ErrorValidationLog}"); + AssertOnTimeout($"[Final Pass][Deparent-Reparent-{i}] Timed out waiting for all clients transform values to match the server transform values!\n {m_ErrorValidationLog}"); } // In the final pass, we remove the second generation nested child