The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). ## [2.0.0-exp.4] - 2024-05-31 ### Added - Added `NetworkRigidbodyBase.AttachToFixedJoint` and `NetworkRigidbodyBase.DetachFromFixedJoint` to replace parenting for rigid bodies that have `NetworkRigidbodyBase.UseRigidBodyForMotion` enabled. (#2933) - Added `NetworkBehaviour.OnNetworkPreSpawn` and `NetworkBehaviour.OnNetworkPostSpawn` methods that provide the ability to handle pre and post spawning actions during the `NetworkObject` spawn sequence. (#2912) - Added a client-side only `NetworkBehaviour.OnNetworkSessionSynchronized` convenience method that is invoked on all `NetworkBehaviour`s after a newly joined client has finished synchronizing with the network session in progress. (#2912) - Added `NetworkBehaviour.OnInSceneObjectsSpawned` convenience method that is invoked when all in-scene `NetworkObject`s have been spawned after a scene has been loaded or upon a host or server starting. (#2912) ### Fixed - Fixed issue where non-authoritative rigid bodies with `NetworkRigidbodyBase.UseRigidBodyForMotion` enabled would constantly log errors about the renderTime being before `StartTimeConsumed`. (#2933) - Fixed issue where in-scene placed NetworkObjects could be destroyed if a client disconnects early and/or before approval. (#2924) - Fixed issue where a `NetworkObject` component's associated `NetworkBehaviour` components would not be detected if scene loading is disabled in the editor and the currently loaded scene has in-scene placed `NetworkObject`s. (#2912) - Fixed issue where an in-scene placed `NetworkObject` with `NetworkTransform` that is also parented under a `GameObject` would not properly synchronize when the parent `GameObject` had a world space position other than 0,0,0. (#2898) ### Changed - Change all the access modifiers of test class from Public to Internal (#2930) - Changed messages are now sorted by enum values as opposed to ordinally sorting the messages by their type name. (#2929) - Changed `NetworkClient.SessionModeTypes` to `NetworkClient.NetworkTopologyTypes`. (#2875) - Changed `NetworkClient.SessionModeType` to `NetworkClient.NetworkTopologyType`. (#2875) - Changed `NetworkConfig.SessionMode` to `NeworkConfig.NetworkTopology`. (#2875)
170 lines
7.4 KiB
C#
170 lines
7.4 KiB
C#
using System.Collections;
|
|
using NUnit.Framework;
|
|
using Unity.Netcode.TestHelpers.Runtime;
|
|
using UnityEngine.TestTools;
|
|
|
|
namespace Unity.Netcode.RuntimeTests
|
|
{
|
|
[TestFixture(HostOrServer.DAHost)]
|
|
[TestFixture(HostOrServer.Host)]
|
|
internal class NetworkSpawnManagerTests : NetcodeIntegrationTest
|
|
{
|
|
private ulong serverSideClientId => NetworkManager.ServerClientId;
|
|
private ulong clientSideClientId => m_ClientNetworkManagers[0].LocalClientId;
|
|
private ulong otherClientSideClientId => m_ClientNetworkManagers[1].LocalClientId;
|
|
|
|
protected override int NumberOfClients => 2;
|
|
|
|
public NetworkSpawnManagerTests(HostOrServer hostOrServer) : base(hostOrServer) { }
|
|
|
|
[Test]
|
|
public void TestServerCanAccessItsOwnPlayer()
|
|
{
|
|
// server can access its own player
|
|
var serverSideServerPlayerObject = m_ServerNetworkManager.SpawnManager.GetPlayerNetworkObject(serverSideClientId);
|
|
Assert.NotNull(serverSideServerPlayerObject);
|
|
Assert.AreEqual(serverSideClientId, serverSideServerPlayerObject.OwnerClientId);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Test was converted from a Test to UnityTest so distributed authority mode will pass this test.
|
|
/// In distributed authority mode, client-side player spawning is enabled by default which requires
|
|
/// all client (including DAHost) instances to wait for all players to be spawned.
|
|
/// </summary>
|
|
[UnityTest]
|
|
public IEnumerator TestServerCanAccessOtherPlayers()
|
|
{
|
|
yield return null;
|
|
// server can access other players
|
|
var serverSideClientPlayerObject = m_ServerNetworkManager.SpawnManager.GetPlayerNetworkObject(clientSideClientId);
|
|
Assert.NotNull(serverSideClientPlayerObject);
|
|
Assert.AreEqual(clientSideClientId, serverSideClientPlayerObject.OwnerClientId);
|
|
|
|
var serverSideOtherClientPlayerObject = m_ServerNetworkManager.SpawnManager.GetPlayerNetworkObject(otherClientSideClientId);
|
|
Assert.NotNull(serverSideOtherClientPlayerObject);
|
|
Assert.AreEqual(otherClientSideClientId, serverSideOtherClientPlayerObject.OwnerClientId);
|
|
|
|
}
|
|
|
|
[Test]
|
|
public void TestClientCantAccessServerPlayer()
|
|
{
|
|
if (m_DistributedAuthority)
|
|
{
|
|
VerboseDebug($"Ignoring test: Clients have access to other player objects in {m_NetworkTopologyType} mode.");
|
|
return;
|
|
}
|
|
// client can't access server player
|
|
Assert.Throws<NotServerException>(() =>
|
|
{
|
|
m_ClientNetworkManagers[0].SpawnManager.GetPlayerNetworkObject(serverSideClientId);
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public void TestClientCanAccessOwnPlayer()
|
|
{
|
|
// client can access own player
|
|
var clientSideClientPlayerObject = m_ClientNetworkManagers[0].SpawnManager.GetPlayerNetworkObject(clientSideClientId);
|
|
Assert.NotNull(clientSideClientPlayerObject);
|
|
Assert.AreEqual(clientSideClientId, clientSideClientPlayerObject.OwnerClientId);
|
|
}
|
|
|
|
[Test]
|
|
public void TestClientCanAccessOtherPlayer()
|
|
{
|
|
|
|
if (!m_DistributedAuthority)
|
|
{
|
|
VerboseDebug($"Ignoring test: Clients do not have access to other player objects in {m_NetworkTopologyType} mode.");
|
|
return;
|
|
}
|
|
|
|
var otherClientPlayer = m_ClientNetworkManagers[0].SpawnManager.GetPlayerNetworkObject(otherClientSideClientId);
|
|
Assert.NotNull(otherClientPlayer, $"Failed to obtain Client{otherClientSideClientId}'s player object!");
|
|
}
|
|
|
|
[Test]
|
|
public void TestClientCantAccessOtherPlayer()
|
|
{
|
|
if (m_DistributedAuthority)
|
|
{
|
|
VerboseDebug($"Ignoring test: Clients have access to other player objects in {m_NetworkTopologyType} mode.");
|
|
return;
|
|
}
|
|
|
|
// client can't access other player
|
|
Assert.Throws<NotServerException>(() =>
|
|
{
|
|
m_ClientNetworkManagers[0].SpawnManager.GetPlayerNetworkObject(otherClientSideClientId);
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public void TestServerGetsNullValueIfInvalidId()
|
|
{
|
|
// server gets null value if invalid id
|
|
var nullPlayer = m_ServerNetworkManager.SpawnManager.GetPlayerNetworkObject(9999);
|
|
Assert.Null(nullPlayer);
|
|
}
|
|
|
|
[Test]
|
|
public void TestServerCanUseGetLocalPlayerObject()
|
|
{
|
|
// test server can use GetLocalPlayerObject
|
|
var serverSideServerPlayerObject = m_ServerNetworkManager.SpawnManager.GetLocalPlayerObject();
|
|
Assert.NotNull(serverSideServerPlayerObject);
|
|
Assert.AreEqual(serverSideClientId, serverSideServerPlayerObject.OwnerClientId);
|
|
}
|
|
|
|
[Test]
|
|
public void TestClientCanUseGetLocalPlayerObject()
|
|
{
|
|
// test client can use GetLocalPlayerObject
|
|
var clientSideClientPlayerObject = m_ClientNetworkManagers[0].SpawnManager.GetLocalPlayerObject();
|
|
Assert.NotNull(clientSideClientPlayerObject);
|
|
Assert.AreEqual(clientSideClientId, clientSideClientPlayerObject.OwnerClientId);
|
|
}
|
|
|
|
private bool m_ClientDisconnected;
|
|
|
|
[UnityTest]
|
|
public IEnumerator TestConnectAndDisconnect()
|
|
{
|
|
// test when client connects, player object is now available
|
|
yield return CreateAndStartNewClient();
|
|
var newClientNetworkManager = m_ClientNetworkManagers[NumberOfClients];
|
|
var newClientLocalClientId = newClientNetworkManager.LocalClientId;
|
|
|
|
// test new client can get that itself locally
|
|
var newPlayerObject = newClientNetworkManager.SpawnManager.GetLocalPlayerObject();
|
|
Assert.NotNull(newPlayerObject);
|
|
Assert.AreEqual(newClientLocalClientId, newPlayerObject.OwnerClientId);
|
|
// test server can get that new client locally
|
|
var serverSideNewClientPlayer = m_ServerNetworkManager.SpawnManager.GetPlayerNetworkObject(newClientLocalClientId);
|
|
Assert.NotNull(serverSideNewClientPlayer);
|
|
Assert.AreEqual(newClientLocalClientId, serverSideNewClientPlayer.OwnerClientId);
|
|
|
|
// test when client disconnects, player object no longer available.
|
|
var nbConnectedClients = m_ServerNetworkManager.ConnectedClients.Count;
|
|
m_ClientDisconnected = false;
|
|
newClientNetworkManager.OnClientDisconnectCallback += ClientNetworkManager_OnClientDisconnectCallback;
|
|
m_ServerNetworkManager.DisconnectClient(newClientLocalClientId);
|
|
yield return WaitForConditionOrTimeOut(() => m_ClientDisconnected);
|
|
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for client to disconnect");
|
|
// Call this to clean up NetcodeIntegrationTestHelpers
|
|
NetcodeIntegrationTestHelpers.StopOneClient(newClientNetworkManager);
|
|
|
|
Assert.AreEqual(m_ServerNetworkManager.ConnectedClients.Count, nbConnectedClients - 1);
|
|
serverSideNewClientPlayer = m_ServerNetworkManager.SpawnManager.GetPlayerNetworkObject(newClientLocalClientId);
|
|
Assert.Null(serverSideNewClientPlayer);
|
|
}
|
|
|
|
private void ClientNetworkManager_OnClientDisconnectCallback(ulong obj)
|
|
{
|
|
m_ClientDisconnected = true;
|
|
}
|
|
}
|
|
}
|