11using System . Collections ;
2+ using System . Text . RegularExpressions ;
23using NUnit . Framework ;
34using Unity . Netcode . TestHelpers . Runtime ;
45using UnityEngine . TestTools ;
@@ -9,164 +10,133 @@ namespace Unity.Netcode.RuntimeTests
910 [ TestFixture ( HostOrServer . Host ) ]
1011 internal class NetworkSpawnManagerTests : NetcodeIntegrationTest
1112 {
12- private ulong serverSideClientId => NetworkManager . ServerClientId ;
13- private ulong clientSideClientId => m_ClientNetworkManagers [ 0 ] . LocalClientId ;
14- private ulong otherClientSideClientId => m_ClientNetworkManagers [ 1 ] . LocalClientId ;
15-
1613 protected override int NumberOfClients => 2 ;
1714
18- // TODO: [CmbServiceTests] Adapt to run with the service. Combine server and client tests
19- protected override bool UseCMBService ( )
20- {
21- return false ;
22- }
23-
2415 public NetworkSpawnManagerTests ( HostOrServer hostOrServer ) : base ( hostOrServer ) { }
2516
2617 [ Test ]
27- public void TestServerCanAccessItsOwnPlayer ( )
28- {
29- // server can access its own player
30- var serverSideServerPlayerObject = m_ServerNetworkManager . SpawnManager . GetPlayerNetworkObject ( serverSideClientId ) ;
31- Assert . NotNull ( serverSideServerPlayerObject ) ;
32- Assert . AreEqual ( serverSideClientId , serverSideServerPlayerObject . OwnerClientId ) ;
33- }
34-
35-
36- /// <summary>
37- /// Test was converted from a Test to UnityTest so distributed authority mode will pass this test.
38- /// In distributed authority mode, client-side player spawning is enabled by default which requires
39- /// all client (including DAHost) instances to wait for all players to be spawned.
40- /// </summary>
41- [ UnityTest ]
42- public IEnumerator TestServerCanAccessOtherPlayers ( )
43- {
44- yield return null ;
45- // server can access other players
46- var serverSideClientPlayerObject = m_ServerNetworkManager . SpawnManager . GetPlayerNetworkObject ( clientSideClientId ) ;
47- Assert . NotNull ( serverSideClientPlayerObject ) ;
48- Assert . AreEqual ( clientSideClientId , serverSideClientPlayerObject . OwnerClientId ) ;
49-
50- var serverSideOtherClientPlayerObject = m_ServerNetworkManager . SpawnManager . GetPlayerNetworkObject ( otherClientSideClientId ) ;
51- Assert . NotNull ( serverSideOtherClientPlayerObject ) ;
52- Assert . AreEqual ( otherClientSideClientId , serverSideOtherClientPlayerObject . OwnerClientId ) ;
53- }
54-
55- [ Test ]
56- public void TestClientCantAccessServerPlayer ( )
18+ public void TestGetPlayerNetworkObject ( )
5719 {
58- if ( m_DistributedAuthority )
20+ foreach ( var toTest in m_NetworkManagers )
5921 {
60- VerboseDebug ( $ "Ignoring test: Clients have access to other player objects in { m_NetworkTopologyType } mode.") ;
61- return ;
22+ foreach ( var toGet in m_NetworkManagers )
23+ {
24+ // GetPlayerNetworkObject should only be able to get the object
25+ // - When using DA
26+ // - When testing the Server NetworkManager
27+ // - And when the client is getting their own PlayerObject.
28+ var canFetch = m_DistributedAuthority || toTest . IsServer || toTest == toGet ;
29+
30+ if ( ! canFetch )
31+ {
32+ LogAssert . Expect ( UnityEngine . LogType . Error , new Regex ( "Only the server can find player objects from other clients." ) ) ;
33+ }
34+
35+ var playerObject = toTest . SpawnManager . GetPlayerNetworkObject ( toGet . LocalClientId ) ;
36+
37+ if ( canFetch )
38+ {
39+ Assert . That ( playerObject , Is . Not . Null ) ;
40+ Assert . That ( toGet . LocalClientId , Is . EqualTo ( playerObject . OwnerClientId ) ) ;
41+ }
42+ else
43+ {
44+ Assert . That ( playerObject , Is . Null ) ;
45+ }
46+ }
6247 }
63- // client can't access server player
64- string expectedLog = $ "[Netcode-Server Sender=0] { serverSideClientId } Only the server can find player objects from other clients.";
65- LogAssert . Expect ( UnityEngine . LogType . Error , expectedLog ) ;
66- m_ClientNetworkManagers [ 0 ] . SpawnManager . GetPlayerNetworkObject ( serverSideClientId ) ;
67- }
6848
69- [ Test ]
70- public void TestClientCanAccessOwnPlayer ( )
71- {
72- // client can access own player
73- var clientSideClientPlayerObject = m_ClientNetworkManagers [ 0 ] . SpawnManager . GetPlayerNetworkObject ( clientSideClientId ) ;
74- Assert . NotNull ( clientSideClientPlayerObject ) ;
75- Assert . AreEqual ( clientSideClientId , clientSideClientPlayerObject . OwnerClientId ) ;
49+ // finally, test that an invalid clientId returns a null object
50+ var invalid = GetAuthorityNetworkManager ( ) . SpawnManager . GetPlayerNetworkObject ( 9999 ) ;
51+ Assert . That ( invalid , Is . Null ) ;
7652 }
7753
7854 [ Test ]
79- public void TestClientCanAccessOtherPlayer ( )
55+ public void TestGetLocalPlayerObject ( )
8056 {
81-
82- if ( ! m_DistributedAuthority )
57+ foreach ( var manager in m_NetworkManagers )
8358 {
84- VerboseDebug ( $ "Ignoring test: Clients do not have access to other player objects in { m_NetworkTopologyType } mode.") ;
85- return ;
59+ var playerObject = manager . SpawnManager . GetLocalPlayerObject ( ) ;
60+ Assert . That ( playerObject , Is . Not . Null ) ;
61+ Assert . That ( manager . LocalClientId , Is . EqualTo ( playerObject . OwnerClientId ) ) ;
62+ Assert . That ( manager . LocalClient . PlayerObject , Is . EqualTo ( playerObject ) ) ;
8663 }
87-
88- var otherClientPlayer = m_ClientNetworkManagers [ 0 ] . SpawnManager . GetPlayerNetworkObject ( otherClientSideClientId ) ;
89- Assert . NotNull ( otherClientPlayer , $ "Failed to obtain Client{ otherClientSideClientId } 's player object!") ;
9064 }
9165
92- [ Test ]
93- public void TestClientCantAccessOtherPlayer ( )
66+ public enum DestroyWithOwner
9467 {
95- if ( m_DistributedAuthority )
96- {
97- VerboseDebug ( $ "Ignoring test: Clients have access to other player objects in { m_NetworkTopologyType } mode.") ;
98- return ;
99- }
100-
101- // client can't access other player
102- string expectedLog = $ "[Netcode-Server Sender=0] { otherClientSideClientId } Only the server can find player objects from other clients.";
103- LogAssert . Expect ( UnityEngine . LogType . Error , expectedLog ) ;
104- m_ClientNetworkManagers [ 0 ] . SpawnManager . GetPlayerNetworkObject ( otherClientSideClientId ) ;
68+ DestroyWithOwner ,
69+ DontDestroyWithOwner
10570 }
10671
107- [ Test ]
108- public void TestServerGetsNullValueIfInvalidId ( )
109- {
110- // server gets null value if invalid id
111- var nullPlayer = m_ServerNetworkManager . SpawnManager . GetPlayerNetworkObject ( 9999 ) ;
112- Assert . Null ( nullPlayer ) ;
113- }
114-
115- [ Test ]
116- public void TestServerCanUseGetLocalPlayerObject ( )
117- {
118- // test server can use GetLocalPlayerObject
119- var serverSideServerPlayerObject = m_ServerNetworkManager . SpawnManager . GetLocalPlayerObject ( ) ;
120- Assert . NotNull ( serverSideServerPlayerObject ) ;
121- Assert . AreEqual ( serverSideClientId , serverSideServerPlayerObject . OwnerClientId ) ;
122- }
123-
124- [ Test ]
125- public void TestClientCanUseGetLocalPlayerObject ( )
72+ [ UnityTest ]
73+ public IEnumerator TestPlayerPrefabConnectAndDisconnect ( [ Values ] DestroyWithOwner destroySetting )
12674 {
127- // test client can use GetLocalPlayerObject
128- var clientSideClientPlayerObject = m_ClientNetworkManagers [ 0 ] . SpawnManager . GetLocalPlayerObject ( ) ;
129- Assert . NotNull ( clientSideClientPlayerObject ) ;
130- Assert . AreEqual ( clientSideClientId , clientSideClientPlayerObject . OwnerClientId ) ;
131- }
75+ var destroyWithOwner = destroySetting == DestroyWithOwner . DestroyWithOwner ;
76+ var authority = GetAuthorityNetworkManager ( ) ;
77+ // Regression test: Ensure the authority's player object is set
78+ Assert . That ( authority . LocalClient . PlayerObject != null , Is . True , "The server should have a player object!" ) ;
13279
133- private bool m_ClientDisconnected ;
80+ // Mark PlayerPrefab as DontDestroyWithOwner
81+ m_PlayerPrefab . GetComponent < NetworkObject > ( ) . DontDestroyWithOwner = ! destroyWithOwner ;
13482
135- [ UnityTest ]
136- public IEnumerator TestConnectAndDisconnect ( )
137- {
13883 // test when client connects, player object is now available
139- yield return CreateAndStartNewClient ( ) ;
140- var newClientNetworkManager = m_ClientNetworkManagers [ NumberOfClients ] ;
141- var newClientLocalClientId = newClientNetworkManager . LocalClientId ;
84+ var newClient = CreateNewClient ( ) ;
85+ yield return StartClient ( newClient ) ;
86+ var newClientId = newClient . LocalClientId ;
14287
14388 // test new client can get that itself locally
144- var newPlayerObject = newClientNetworkManager . SpawnManager . GetLocalPlayerObject ( ) ;
89+ var newPlayerObject = newClient . SpawnManager . GetLocalPlayerObject ( ) ;
14590 Assert . NotNull ( newPlayerObject ) ;
146- Assert . AreEqual ( newClientLocalClientId , newPlayerObject . OwnerClientId ) ;
91+ Assert . AreEqual ( newClientId , newPlayerObject . OwnerClientId ) ;
92+
14793 // test server can get that new client locally
148- var serverSideNewClientPlayer = m_ServerNetworkManager . SpawnManager . GetPlayerNetworkObject ( newClientLocalClientId ) ;
94+ var serverSideNewClientPlayer = authority . SpawnManager . GetPlayerNetworkObject ( newClientId ) ;
14995 Assert . NotNull ( serverSideNewClientPlayer ) ;
150- Assert . AreEqual ( newClientLocalClientId , serverSideNewClientPlayer . OwnerClientId ) ;
96+ Assert . AreEqual ( newClientId , serverSideNewClientPlayer . OwnerClientId ) ;
15197
15298 // test when client disconnects, player object no longer available.
153- var nbConnectedClients = m_ServerNetworkManager . ConnectedClients . Count ;
154- m_ClientDisconnected = false ;
155- newClientNetworkManager . OnClientDisconnectCallback += ClientNetworkManager_OnClientDisconnectCallback ;
156- m_ServerNetworkManager . DisconnectClient ( newClientLocalClientId ) ;
157- yield return WaitForConditionOrTimeOut ( ( ) => m_ClientDisconnected ) ;
158- Assert . IsFalse ( s_GlobalTimeoutHelper . TimedOut , "Timed out waiting for client to disconnect" ) ;
99+ var nbConnectedClients = authority . ConnectedClients . Count ;
100+ authority . DisconnectClient ( newClientId ) ;
101+
102+ yield return WaitForConditionOrTimeOut ( ( ) => ! newClient . IsConnectedClient ) ;
103+ AssertOnTimeout ( "Timed out waiting for client to disconnect" ) ;
104+
159105 // Call this to clean up NetcodeIntegrationTestHelpers
160- NetcodeIntegrationTestHelpers . StopOneClient ( newClientNetworkManager ) ;
106+ yield return StopOneClient ( newClient ) ;
161107
162- Assert . AreEqual ( m_ServerNetworkManager . ConnectedClients . Count , nbConnectedClients - 1 ) ;
163- serverSideNewClientPlayer = m_ServerNetworkManager . SpawnManager . GetPlayerNetworkObject ( newClientLocalClientId ) ;
164- Assert . Null ( serverSideNewClientPlayer ) ;
165- }
108+ Assert . AreEqual ( authority . ConnectedClients . Count , nbConnectedClients - 1 ) ;
109+ Assert . True ( newPlayerObject == null , "The client's player object should have been destroyed!" ) ;
166110
167- private void ClientNetworkManager_OnClientDisconnectCallback ( ulong obj )
168- {
169- m_ClientDisconnected = true ;
111+ if ( destroyWithOwner )
112+ {
113+ Assert . That ( serverSideNewClientPlayer == null , Is . True , "The server's version of the client's player object should have been destroyed" ) ;
114+ }
115+ else
116+ {
117+ Assert . That ( serverSideNewClientPlayer == null , Is . False , "The server's version of the client's player object shouldn't have been destroyed!" ) ;
118+ Assert . That ( serverSideNewClientPlayer . OwnerClientId , Is . EqualTo ( authority . LocalClientId ) , "Ownership should have transferred to the authority!" ) ;
119+ Assert . That ( serverSideNewClientPlayer . IsPlayerObject , Is . True , $ "{ nameof ( NetworkObject . IsPlayerObject ) } should still be set!") ;
120+
121+ // Requesting the player's object after they've left should still while the object hasn't been destroyed
122+ var playerObject = authority . SpawnManager . GetPlayerNetworkObject ( newClientId ) ;
123+ Assert . That ( playerObject != null , Is . True , "The authority should still be able to get the player object after the client has disconnected" ) ;
124+
125+ // Despawn and destroy the player object
126+ playerObject . Despawn ( ) ;
127+
128+ // Check that now the player object is null
129+ yield return WaitForConditionOrTimeOut ( ( ) => playerObject == null ) ;
130+ AssertOnTimeout ( "Timed out waiting for the object to be destroyed." ) ;
131+
132+ // Regression test:
133+ // check that the authority's player object isn't destroyed
134+ Assert . That ( authority . LocalClient . PlayerObject != null , Is . True , "The server's player object should not have been destroyed!" ) ;
135+ }
136+
137+ // sanity check that requesting the object from the client who left after the object was destroyed is now null
138+ var sanity = authority . SpawnManager . GetPlayerNetworkObject ( newClientId ) ;
139+ Assert . Null ( sanity , $ "{ nameof ( NetworkSpawnManager . GetPlayerNetworkObject ) } shouldn't be able to get the player object after the client has disconnected!") ;
170140 }
171141 }
172142}
0 commit comments