22using System . Collections . Generic ;
33using System . Linq ;
44using NUnit . Framework ;
5+ using Unity . Netcode . Components ;
56using Unity . Netcode . TestHelpers . Runtime ;
67using UnityEngine ;
78using UnityEngine . TestTools ;
@@ -25,6 +26,12 @@ public override void OnGainedOwnership()
2526 OnGainedOwnershipFired = true ;
2627 }
2728
29+ protected override void OnOwnershipChanged ( ulong previous , ulong current )
30+ {
31+ Assert . True ( previous != current , $ "[{ nameof ( OnOwnershipChanged ) } ][Invalid Parameters] Invoked and the previous ({ previous } ) equals the current ({ current } )!") ;
32+ base . OnOwnershipChanged ( previous , current ) ;
33+ }
34+
2835 public override void OnNetworkSpawn ( )
2936 {
3037 if ( ! SpawnedInstances . ContainsKey ( NetworkManager . LocalClientId ) )
@@ -39,6 +46,12 @@ public void ResetFlags()
3946 OnLostOwnershipFired = false ;
4047 OnGainedOwnershipFired = false ;
4148 }
49+
50+ [ Rpc ( SendTo . Authority ) ]
51+ public void ChangeOwnershipRpc ( RpcParams rpcParams = default )
52+ {
53+ NetworkObject . ChangeOwnership ( rpcParams . Receive . SenderClientId ) ;
54+ }
4255 }
4356
4457
@@ -71,6 +84,7 @@ protected override void OnServerAndClientsCreated()
7184 {
7285 m_OwnershipPrefab = CreateNetworkObjectPrefab ( "OnwershipPrefab" ) ;
7386 m_OwnershipPrefab . AddComponent < NetworkObjectOwnershipComponent > ( ) ;
87+ m_OwnershipPrefab . AddComponent < NetworkTransform > ( ) ;
7488 if ( m_DistributedAuthority )
7589 {
7690 m_OwnershipPrefab . GetComponent < NetworkObject > ( ) . SetOwnershipStatus ( NetworkObject . OwnershipStatus . Transferable ) ;
@@ -465,7 +479,87 @@ public IEnumerator TestOwnedObjectCounts()
465479
466480 yield return WaitForConditionOrTimeOut ( ServerHasCorrectClientOwnedObjectCount ) ;
467481 AssertOnTimeout ( $ "Server does not have the correct count for all clients spawned { k_NumberOfSpawnedObjects } { nameof ( NetworkObject ) } s!") ;
482+ }
468483
484+ /// <summary>
485+ /// Validates that when changing ownership NetworkTransform does not enter into a bad state
486+ /// because the previous and current owner identifiers are the same. For client-server this
487+ /// ends up always being the server, but for distributed authority the authority changes when
488+ /// ownership changes.
489+ /// </summary>
490+ [ UnityTest ]
491+ public IEnumerator TestAuthorityChangingOwnership ( )
492+ {
493+ var authorityManager = ( NetworkManager ) null ;
494+ var allNetworkManagers = m_ClientNetworkManagers . ToList ( ) ;
495+ allNetworkManagers . Add ( m_ServerNetworkManager ) ;
496+
497+ if ( m_DistributedAuthority )
498+ {
499+ var authorityId = Random . Range ( 1 , NumberOfClients ) - 1 ;
500+ authorityManager = m_ClientNetworkManagers [ authorityId ] ;
501+ m_OwnershipObject = SpawnObject ( m_OwnershipPrefab , authorityManager ) ;
502+ m_OwnershipNetworkObject = m_OwnershipObject . GetComponent < NetworkObject > ( ) ;
503+ }
504+ else
505+ {
506+ authorityManager = m_ServerNetworkManager ;
507+ m_OwnershipObject = SpawnObject ( m_OwnershipPrefab , m_ServerNetworkManager ) ;
508+ m_OwnershipNetworkObject = m_OwnershipObject . GetComponent < NetworkObject > ( ) ;
509+ }
510+ var ownershipNetworkObjectId = m_OwnershipNetworkObject . NetworkObjectId ;
511+ bool WaitForClientsToSpawnNetworkObject ( )
512+ {
513+ foreach ( var clientNetworkManager in m_ClientNetworkManagers )
514+ {
515+ if ( ! clientNetworkManager . SpawnManager . SpawnedObjects . ContainsKey ( ownershipNetworkObjectId ) )
516+ {
517+ return false ;
518+ }
519+ }
520+ return true ;
521+ }
522+
523+ yield return WaitForConditionOrTimeOut ( WaitForClientsToSpawnNetworkObject ) ;
524+ AssertOnTimeout ( $ "Timed out waiting for all clients to spawn the { m_OwnershipNetworkObject . name } { nameof ( NetworkObject ) } instance!") ;
525+
526+ var currentTargetOwner = ( ulong ) 0 ;
527+ bool WaitForAllInstancesToChangeOwnership ( )
528+ {
529+ foreach ( var clientNetworkManager in m_ClientNetworkManagers )
530+ {
531+ if ( ! clientNetworkManager . SpawnManager . SpawnedObjects . ContainsKey ( ownershipNetworkObjectId ) )
532+ {
533+ return false ;
534+ }
535+ if ( clientNetworkManager . SpawnManager . SpawnedObjects [ ownershipNetworkObjectId ] . OwnerClientId != currentTargetOwner )
536+ {
537+ return false ;
538+ }
539+ }
540+ return true ;
541+ }
542+
543+ // Change ownership a few times and as long as the previous and current owners are not the same when
544+ // OnOwnershipChanged is invoked then the test passed.
545+ foreach ( var networkManager in allNetworkManagers )
546+ {
547+ if ( networkManager == authorityManager )
548+ {
549+ continue ;
550+ }
551+ var clonedObject = networkManager . SpawnManager . SpawnedObjects [ ownershipNetworkObjectId ] ;
552+
553+ if ( clonedObject . OwnerClientId == networkManager . LocalClientId )
554+ {
555+ continue ;
556+ }
557+
558+ var testComponent = clonedObject . GetComponent < NetworkObjectOwnershipComponent > ( ) ;
559+ testComponent . ChangeOwnershipRpc ( ) ;
560+ yield return WaitForAllInstancesToChangeOwnership ( ) ;
561+ AssertOnTimeout ( $ "Timed out waiting for all instances to change ownership to Client-{ networkManager . LocalClientId } !") ;
562+ }
469563 }
470564 }
471565}
0 commit comments