1010
1111namespace Unity . Netcode . RuntimeTests
1212{
13-
13+ /// <summary>
14+ /// This test validates various spawn sequences to verify message
15+ /// ordering is preserved and each sequence action is invoked
16+ /// on non-authority instances in the order they were invoked on the
17+ /// authority instance (i.e. preserving order of operations).
18+ /// <see cref="OrderOfOperations"/>: Test entry point.<br />
19+ /// <see cref="RunTestSequences"/>: Runs a test based a series of sequences configured sequences.<br />
20+ /// <see cref="SpawnSequence"/>: Derived from to create a spawn sequence.
21+ /// <see cref="SpawnSequenceController"/>: Iterates through all defined/configured spawn sequences
22+ /// on both authoritative and non-authoritative instances for a test configuration.
23+ /// </summary>
1424 [ TestFixture ( HostOrServer . DAHost ) ]
1525 [ TestFixture ( HostOrServer . Host ) ]
1626 [ TestFixture ( HostOrServer . Server ) ]
@@ -25,7 +35,7 @@ internal class NetworkTransformOrderOfOperations : IntegrationTestWithApproximat
2535 private SpawnSequenceController m_AuthoritySeqControllerInstance ;
2636
2737 private NetworkManager m_AuthorityNetworkManager ;
28- private List < NetworkObject > m_AuthorityGenericInstances = new List < NetworkObject > ( ) ;
38+ private List < NetworkObject > m_AuthorityParentInstances = new List < NetworkObject > ( ) ;
2939
3040 public NetworkTransformOrderOfOperations ( HostOrServer host ) : base ( host )
3141 {
@@ -53,7 +63,7 @@ protected override void OnServerAndClientsCreated()
5363 private bool VerifyGenericsSpawned ( StringBuilder errorLog )
5464 {
5565 var conditionMet = true ;
56- foreach ( var networkObject in m_AuthorityGenericInstances )
66+ foreach ( var networkObject in m_AuthorityParentInstances )
5767 {
5868 var networkObjectId = networkObject . NetworkObjectId ;
5969 foreach ( var networkManager in m_NetworkManagers )
@@ -75,7 +85,7 @@ private bool VerifyGenericsSpawned(StringBuilder errorLog)
7585
7686 private IEnumerator SpawnGenericParents ( )
7787 {
78- m_AuthorityGenericInstances . Clear ( ) ;
88+ m_AuthorityParentInstances . Clear ( ) ;
7989 for ( int i = 0 ; i < 3 ; i ++ )
8090 {
8191 var instance = Object . Instantiate ( m_GenericObject ) ;
@@ -85,7 +95,7 @@ private IEnumerator SpawnGenericParents()
8595 eulerAngles = GetRandomVector3 ( - 180 , 180 ) ,
8696 } ;
8797 SpawnObjectInstance ( instance , m_AuthorityNetworkManager ) ;
88- m_AuthorityGenericInstances . Add ( instance ) ;
98+ m_AuthorityParentInstances . Add ( instance ) ;
8999 }
90100 yield return WaitForConditionOrTimeOut ( VerifyGenericsSpawned ) ;
91101 AssertOnTimeout ( "Failure to spawn generics on one or more clients!" ) ;
@@ -154,120 +164,125 @@ private bool TransformsMatch(StringBuilder errorLog)
154164
155165 #region OrderOfOperations Core Test Methods
156166 [ UnityTest ]
157-
158167 public IEnumerator OrderOfOperations ( )
159168 {
160169 m_EnableVerboseDebug = true ;
161170 SpawnSequenceController . VerboseLog = m_EnableVerboseDebug ;
162171 m_AuthorityNetworkManager = GetAuthorityNetworkManager ( ) ;
163172 yield return SpawnGenericParents ( ) ;
173+ var parent1 = m_AuthorityParentInstances [ 0 ] ;
174+ var parent2 = m_AuthorityParentInstances [ 1 ] ;
164175
165- ConfigureSequencesTest1 ( m_AuthorityGenericInstances [ 0 ] ) ;
176+ ConfigureSequencesTest1 ( parent1 ) ;
166177 yield return RunTestSequences ( ) ;
167178
168- ConfigureSequencesTest2 ( m_AuthorityGenericInstances [ 0 ] ) ;
179+ ConfigureSequencesTest2 ( parent1 ) ;
169180 yield return RunTestSequences ( ) ;
170181
171- ConfigureSequencesTest3 ( m_AuthorityGenericInstances [ 0 ] , m_AuthorityGenericInstances [ 1 ] ) ;
182+ ConfigureSequencesTest3 ( parent1 , parent2 ) ;
172183 yield return RunTestSequences ( ) ;
173184
174- ConfigureSequencesTest4 ( m_AuthorityGenericInstances [ 0 ] , m_AuthorityGenericInstances [ 1 ] ) ;
185+ ConfigureSequencesTest4 ( parent1 , parent2 ) ;
175186 yield return RunTestSequences ( false ) ;
176187
177- ConfigureSequencesTest5 ( m_AuthorityGenericInstances [ 0 ] ) ;
188+ ConfigureSequencesTest5 ( parent1 ) ;
178189 yield return RunTestSequences ( ) ;
179190
180- ConfigureSequencesTest6 ( m_AuthorityGenericInstances [ 0 ] ) ;
191+ ConfigureSequencesTest6_ClientServerOnly ( parent1 ) ;
181192 yield return RunTestSequences ( ) ;
182193
183- ConfigureSequencesTest7 ( m_AuthorityGenericInstances [ 0 ] ) ;
194+ ConfigureSequencesTest7_ClientServerOnly ( parent1 ) ;
184195 yield return RunTestSequences ( spawnWithOwnership : true ) ;
185196
186- ConfigureSequencesTest8 ( m_AuthorityGenericInstances [ 0 ] ) ;
197+ ConfigureSequencesTest8 ( parent1 ) ;
187198 yield return RunTestSequences ( spawnWithObservers : false ) ;
188199
189- ConfigureSequencesTest9 ( m_AuthorityGenericInstances [ 0 ] ) ;
200+ ConfigureSequencesTest9 ( parent1 ) ;
190201 yield return RunTestSequences ( spawnWithObservers : false ) ;
191202
192- ConfigureSequencesTest10 ( m_AuthorityGenericInstances [ 0 ] , m_AuthorityGenericInstances [ 1 ] ) ;
203+ ConfigureSequencesTest10_ClientServerOnly ( parent1 , parent2 ) ;
193204 yield return RunTestSequences ( spawnWithOwnership : true ) ;
194205
195- ConfigureSequencesTest11 ( m_AuthorityGenericInstances [ 0 ] , m_AuthorityGenericInstances [ 1 ] ) ;
206+ ConfigureSequencesTest11_ClientServerOnly ( parent1 , parent2 ) ;
196207 yield return RunTestSequences ( spawnWithOwnership : true ) ;
197208 }
198209
210+
199211 private IEnumerator RunTestSequences ( bool spawnWithObservers = true , bool spawnWithOwnership = false )
200212 {
201- if ( SpawnSequenceController . ShouldRun ( m_AuthorityNetworkManager ) )
202- {
203- VerboseDebug ( $ "Running { SpawnSequenceController . CurrentTest } ") ;
204- var instance = Object . Instantiate ( m_ObjectToTest ) ;
205- instance . SpawnWithObservers = spawnWithObservers ;
206- SpawnObjectInstance ( instance , spawnWithOwnership ? GetNonAuthorityNetworkManager ( ) : m_AuthorityNetworkManager ) ;
207- m_AuthoritySeqControllerInstance = instance . GetComponent < SpawnSequenceController > ( ) ;
208- var authorityObjectId = m_AuthoritySeqControllerInstance . NetworkObjectId ;
209- m_AuthoritySeqControllerInstance . AfterSpawn ( ) ;
210- if ( spawnWithObservers )
211- {
212- yield return WaitForSpawnedOnAllOrTimeOut ( m_AuthoritySeqControllerInstance . NetworkObjectId ) ;
213- AssertOnTimeout ( $ "All clients did not spawn { m_AuthoritySeqControllerInstance . name } !") ;
214- foreach ( var networkManager in m_NetworkManagers )
215- {
216- if ( networkManager == m_AuthorityNetworkManager )
217- {
218- continue ;
219- }
220- networkManager . SpawnManager . SpawnedObjects [ authorityObjectId ] . GetComponent < SpawnSequenceController > ( ) . AfterSpawn ( ) ;
221- }
222- }
213+ yield return __RunTestSequences ( spawnWithObservers , spawnWithOwnership ) ;
214+
215+ // Assure the generic parents are all at the root hierarchy.
216+ foreach ( var parent in m_AuthorityParentInstances )
217+ {
218+ parent . transform . parent = null ;
219+ }
220+
221+ // Reset the controller's global settings
222+ SpawnSequenceController . Clear ( ) ;
223+ }
223224
224- // Assure all sequenced actions have been invoked.
225- yield return WaitForConditionOrTimeOut ( SpawnSequenceController . AllActionsInvoked ) ;
226- if ( s_GlobalTimeoutHelper . HasTimedOut ( ) )
225+ private IEnumerator __RunTestSequences ( bool spawnWithObservers = true , bool spawnWithOwnership = false )
226+ {
227+ // Exit early if we shouldn't run
228+ if ( ! SpawnSequenceController . ShouldRun ( m_AuthorityNetworkManager ) )
229+ {
230+ VerboseDebug ( $ "Skipping { SpawnSequenceController . CurrentTest } ") ;
231+ yield break ;
232+ }
233+
234+ VerboseDebug ( $ "Running { SpawnSequenceController . CurrentTest } ") ;
235+ var instance = Object . Instantiate ( m_ObjectToTest ) ;
236+ instance . SpawnWithObservers = spawnWithObservers ;
237+ SpawnObjectInstance ( instance , spawnWithOwnership ? GetNonAuthorityNetworkManager ( ) : m_AuthorityNetworkManager ) ;
238+ m_AuthoritySeqControllerInstance = instance . GetComponent < SpawnSequenceController > ( ) ;
239+ var authorityObjectId = m_AuthoritySeqControllerInstance . NetworkObjectId ;
240+ m_AuthoritySeqControllerInstance . AfterSpawn ( ) ;
241+ if ( spawnWithObservers )
242+ {
243+ yield return WaitForSpawnedOnAllOrTimeOut ( m_AuthoritySeqControllerInstance . NetworkObjectId ) ;
244+ AssertOnTimeout ( $ "All clients did not spawn { m_AuthoritySeqControllerInstance . name } !") ;
245+ foreach ( var networkManager in m_NetworkManagers )
227246 {
228- // If we timed out, then check for pending and if found wait for the condition
229- // once more.
230- if ( SpawnSequenceController . ActionIsPending ( ) )
247+ if ( networkManager == m_AuthorityNetworkManager )
231248 {
232- yield return WaitForConditionOrTimeOut ( SpawnSequenceController . AllActionsInvoked ) ;
249+ continue ;
233250 }
251+ networkManager . SpawnManager . SpawnedObjects [ authorityObjectId ] . GetComponent < SpawnSequenceController > ( ) . AfterSpawn ( ) ;
234252 }
235- AssertOnTimeout ( $ "[{ SpawnSequenceController . CurrentTest } ] Not all actions were invoked for the current test sequence!\n { SpawnSequenceController . ErrorLog } ") ;
236- yield return WaitForConditionOrTimeOut ( TransformsMatch ) ;
237- AssertOnTimeout ( $ "Not all { m_AuthoritySeqControllerInstance . name } instances' transforms match!") ;
253+ }
238254
239- // De-spawn the test object
240- if ( m_AuthoritySeqControllerInstance . HasAuthority )
241- {
242- m_AuthoritySeqControllerInstance . NetworkObject . Despawn ( ) ;
243- }
244- else
255+ // Assure all sequenced actions have been invoked.
256+ yield return WaitForConditionOrTimeOut ( SpawnSequenceController . AllActionsInvoked ) ;
257+ if ( s_GlobalTimeoutHelper . HasTimedOut ( ) )
258+ {
259+ // If we timed out, then check for pending and if found wait for the condition
260+ // once more.
261+ if ( SpawnSequenceController . ActionIsPending ( ) )
245262 {
246- foreach ( var networkManager in m_NetworkManagers )
247- {
248- if ( networkManager . SpawnManager . SpawnedObjects [ m_AuthoritySeqControllerInstance . NetworkObjectId ] . HasAuthority )
249- {
250- networkManager . SpawnManager . SpawnedObjects [ m_AuthoritySeqControllerInstance . NetworkObjectId ] . Despawn ( ) ;
251- break ;
252- }
253- }
263+ yield return WaitForConditionOrTimeOut ( SpawnSequenceController . AllActionsInvoked ) ;
254264 }
265+ }
266+ AssertOnTimeout ( $ "[{ SpawnSequenceController . CurrentTest } ] Not all actions were invoked for the current test sequence!\n { SpawnSequenceController . ErrorLog } ") ;
267+ yield return WaitForConditionOrTimeOut ( TransformsMatch ) ;
268+ AssertOnTimeout ( $ "Not all { m_AuthoritySeqControllerInstance . name } instances' transforms match!") ;
255269
256- // Assure the generic parents are all at the root hierarchy.
257- foreach ( var parent in m_AuthorityGenericInstances )
270+ // De-spawn the test object
271+ if ( m_AuthoritySeqControllerInstance . HasAuthority )
272+ {
273+ m_AuthoritySeqControllerInstance . NetworkObject . Despawn ( ) ;
274+ }
275+ else
276+ {
277+ foreach ( var networkManager in m_NetworkManagers )
258278 {
259- if ( parent . transform . parent != null )
279+ if ( networkManager . SpawnManager . SpawnedObjects [ m_AuthoritySeqControllerInstance . NetworkObjectId ] . HasAuthority )
260280 {
261- parent . transform . parent = null ;
281+ networkManager . SpawnManager . SpawnedObjects [ m_AuthoritySeqControllerInstance . NetworkObjectId ] . Despawn ( ) ;
282+ break ;
262283 }
263284 }
264285 }
265- else
266- {
267- VerboseDebug ( $ "Skipping { SpawnSequenceController . CurrentTest } ") ;
268- }
269- // Reset the controller's global settings
270- SpawnSequenceController . Clear ( ) ;
271286 }
272287 #endregion
273288
@@ -451,7 +466,7 @@ private void ConfigureSequencesTest5(NetworkObject parent1)
451466 /// Authority-> Spawn, change ownership, change parent (1), Teleport RPC with NetworkBehaviourReference
452467 /// ClientOwner-> Teleport RPC using NetworkBehaviourReference
453468 /// </summary>
454- private void ConfigureSequencesTest6 ( NetworkObject parent1 )
469+ private void ConfigureSequencesTest6_ClientServerOnly ( NetworkObject parent1 )
455470 {
456471 SpawnSequenceController . CurrentTest = "Test6 (Client-Server Only)" ;
457472 if ( m_AuthorityNetworkManager . DistributedAuthorityMode )
@@ -496,7 +511,7 @@ private void ConfigureSequencesTest6(NetworkObject parent1)
496511 /// Authority-> Spawn with ownership,change parent (1), Teleport RPC with NetworkBehaviourReference
497512 /// ClientOwner-> Teleport RPC using NetworkBehaviourReference
498513 /// </summary>
499- private void ConfigureSequencesTest7 ( NetworkObject parent1 )
514+ private void ConfigureSequencesTest7_ClientServerOnly ( NetworkObject parent1 )
500515 {
501516 SpawnSequenceController . CurrentTest = "Test7 (Client-Server Only)" ;
502517 if ( m_AuthorityNetworkManager . DistributedAuthorityMode )
@@ -606,7 +621,7 @@ private void ConfigureSequencesTest9(NetworkObject parent1)
606621 /// Authority-> Spawn with ownership, change parent (1), Wait (1), Parent RPC with NetworkObjectReference
607622 /// ClientOwner-> Re-parent (2) RPC using NetworkObjectReference
608623 /// </summary>
609- private void ConfigureSequencesTest10 ( NetworkObject parent1 , NetworkObject parent2 )
624+ private void ConfigureSequencesTest10_ClientServerOnly ( NetworkObject parent1 , NetworkObject parent2 )
610625 {
611626 SpawnSequenceController . CurrentTest = "Test10 (Client-Server Only)" ;
612627 if ( m_AuthorityNetworkManager . DistributedAuthorityMode )
@@ -641,7 +656,7 @@ private void ConfigureSequencesTest10(NetworkObject parent1, NetworkObject paren
641656 /// ClientOwner-> Parent (1) RPC using NetworkObjectReference
642657 /// Server-> On the parent changing --> re-parent (2)
643658 /// </summary>
644- private void ConfigureSequencesTest11 ( NetworkObject parent1 , NetworkObject parent2 )
659+ private void ConfigureSequencesTest11_ClientServerOnly ( NetworkObject parent1 , NetworkObject parent2 )
645660 {
646661 SpawnSequenceController . CurrentTest = "Test11 (Client-Server Only)" ;
647662 if ( m_AuthorityNetworkManager . DistributedAuthorityMode )
@@ -650,7 +665,6 @@ private void ConfigureSequencesTest11(NetworkObject parent1, NetworkObject paren
650665 return ;
651666 }
652667
653-
654668 var parentRpc = new ReferenceRpcSequence ( )
655669 {
656670 IsParentRPC = true ,
@@ -867,6 +881,11 @@ protected override void OnAction()
867881 }
868882 }
869883
884+ /// <summary>
885+ /// Derive from this to create a new type of spawn sequence
886+ /// or derive from an existing one to modify or extend the
887+ /// sequence's behavior.
888+ /// </summary>
870889 internal class SpawnSequence
871890 {
872891 public enum SpawnStage
@@ -947,12 +966,18 @@ private IEnumerator TimeDelayCoroutine(SpawnStage stage, SpawnSequenceController
947966 }
948967 }
949968
969+ /// <summary>
970+ /// Process the current giveen set of configured spawn sequences. <br />
971+ /// <see cref="s_SpawnSequencedActions"/> contains the spawn sequences for a test configuraiton. <br />
972+ /// Some spawn sequences might only run under certain conditions determined within <see cref="ShouldRun(NetworkManager)"/>.
973+ /// </summary>
950974 public class SpawnSequenceController : NetworkTransform
951975 {
952976 public static bool VerboseLog ;
953977 public static string CurrentTest ;
954978
955979 public static bool ClientServerOnly ;
980+ public static bool DistributedAuthorityOnly ;
956981
957982 public static bool ShouldRun ( NetworkManager authorityNetworkManager )
958983 {
@@ -1059,6 +1084,23 @@ public void TeleportRpc(Vector3 position, Quaternion rotation, RpcParams rpcPara
10591084 }
10601085 }
10611086
1087+ /// <summary>
1088+ /// This is added to the generic spawned objects (parents) to validate that
1089+ /// after having spawned a NetworkObject, as the authority, and then invoking
1090+ /// an RPC, that accepts an <see cref="NetworkBehaviourReference"/> or <see cref="NetworkObjectReference"/>
1091+ /// which references the newly spawned <see cref="NetworkObject"/> or an associated <see cref="NetworkBehaviour"/>
1092+ /// component on an already known spawned object, that the object will have been spawned prior to the
1093+ /// RPC being invoked on the non-authority side.
1094+ /// </summary>
1095+ /// <remarks>
1096+ /// Currently, NGO does not support this usage pattern if you:
1097+ /// - spawn with no observers
1098+ /// - invoke the RPC with a reference to the spawned object within the same frame/call-stack
1099+ /// This limitation is due to the network show defers the queuing of the <see cref="CreateObjectMessage"/>
1100+ /// until the end of the frame as opposed to generating it when spawned. This could be supported if
1101+ /// we convert to more of a command based system that is applied locally on the spawn authority side
1102+ /// but queued and then messages are generated from the queued commands at the end of the frame.
1103+ /// </remarks>
10621104 public class ReferenceRpcHelper : NetworkBehaviour
10631105 {
10641106 [ Rpc ( SendTo . NotMe ) ]
0 commit comments