|
1 | 1 | using System.Collections; |
2 | 2 | using System.Text; |
| 3 | +using NUnit.Framework; |
3 | 4 | using Unity.Netcode.Components; |
4 | 5 | using Unity.Netcode.TestHelpers.Runtime; |
5 | 6 | using UnityEngine; |
6 | 7 | using UnityEngine.TestTools; |
7 | 8 |
|
8 | 9 | namespace Unity.Netcode.RuntimeTests |
9 | 10 | { |
| 11 | + [TestFixture(HostOrServer.Host)] |
| 12 | + [TestFixture(HostOrServer.Server)] |
10 | 13 | internal class NetworkTransformMixedAuthorityTests : IntegrationTestWithApproximation |
11 | 14 | { |
12 | 15 | private const float k_MotionMagnitude = 5.5f; |
13 | 16 | private const int k_Iterations = 4; |
14 | 17 |
|
15 | 18 | protected override int NumberOfClients => 2; |
16 | 19 |
|
17 | | - private StringBuilder m_ErrorMsg = new StringBuilder(); |
18 | | - |
19 | | - protected override void OnCreatePlayerPrefab() |
| 20 | + /// <summary> |
| 21 | + /// The root's authority mode for each case. The nested child uses the inverse. |
| 22 | + /// </summary> |
| 23 | + private static readonly NetworkTransform.AuthorityModes[] k_RootAuthorityModes = |
20 | 24 | { |
21 | | - m_PlayerPrefab.AddComponent<NetworkTransform>(); |
| 25 | + NetworkTransform.AuthorityModes.Server, |
| 26 | + NetworkTransform.AuthorityModes.Owner, |
| 27 | + }; |
| 28 | + |
| 29 | + private GameObject[] m_MixedAuthorityPrefabs; |
22 | 30 |
|
23 | | - var childGameObject = new GameObject(); |
24 | | - childGameObject.transform.parent = m_PlayerPrefab.transform; |
25 | | - var childNetworkTransform = childGameObject.AddComponent<NetworkTransform>(); |
26 | | - childNetworkTransform.AuthorityMode = NetworkTransform.AuthorityModes.Owner; |
27 | | - childNetworkTransform.InLocalSpace = true; |
| 31 | + private StringBuilder m_ErrorMsg = new StringBuilder(); |
28 | 32 |
|
29 | | - base.OnCreatePlayerPrefab(); |
| 33 | + public NetworkTransformMixedAuthorityTests(HostOrServer hostOrServer) : base(hostOrServer) |
| 34 | + { |
30 | 35 | } |
31 | 36 |
|
32 | | - private void MovePlayers() |
| 37 | + protected override void OnServerAndClientsCreated() |
33 | 38 | { |
34 | | - foreach (var networkManager in m_NetworkManagers) |
| 39 | + m_MixedAuthorityPrefabs = new GameObject[k_RootAuthorityModes.Length]; |
| 40 | + for (int i = 0; i < k_RootAuthorityModes.Length; i++) |
35 | 41 | { |
36 | | - var direction = GetRandomVector3(-1.0f, 1.0f); |
37 | | - var playerObject = networkManager.LocalClient.PlayerObject; |
38 | | - var playerObjectId = networkManager.LocalClient.PlayerObject.NetworkObjectId; |
39 | | - // Server authoritative |
40 | | - var serverPlayerClone = m_ServerNetworkManager.SpawnManager.SpawnedObjects[playerObjectId]; |
41 | | - serverPlayerClone.transform.position += direction * k_MotionMagnitude; |
42 | | - // Owner authoritative |
43 | | - var childTransform = networkManager.LocalClient.PlayerObject.transform.GetChild(0); |
44 | | - childTransform.localPosition += direction * k_MotionMagnitude; |
| 42 | + var rootAuthorityMode = k_RootAuthorityModes[i]; |
| 43 | + var prefab = CreateNetworkObjectPrefab($"MixedAuthority-{rootAuthorityMode}Root"); |
| 44 | + prefab.AddComponent<NetworkTransform>().AuthorityMode = rootAuthorityMode; |
| 45 | + |
| 46 | + var childGameObject = new GameObject(); |
| 47 | + childGameObject.transform.parent = prefab.transform; |
| 48 | + var childNetworkTransform = childGameObject.AddComponent<NetworkTransform>(); |
| 49 | + childNetworkTransform.AuthorityMode = InverseOf(rootAuthorityMode); |
| 50 | + childNetworkTransform.InLocalSpace = true; |
| 51 | + |
| 52 | + m_MixedAuthorityPrefabs[i] = prefab; |
45 | 53 | } |
| 54 | + |
| 55 | + base.OnServerAndClientsCreated(); |
| 56 | + } |
| 57 | + |
| 58 | + private static NetworkTransform.AuthorityModes InverseOf(NetworkTransform.AuthorityModes authorityMode) |
| 59 | + { |
| 60 | + return authorityMode == NetworkTransform.AuthorityModes.Server ? NetworkTransform.AuthorityModes.Owner : NetworkTransform.AuthorityModes.Server; |
| 61 | + } |
| 62 | + |
| 63 | + /// <summary> |
| 64 | + /// Returns the instance with authority over a <see cref="NetworkTransform"/> set to the given authority mode. |
| 65 | + /// </summary> |
| 66 | + private NetworkObject GetAuthorityInstance(NetworkObject instance, NetworkManager owner, NetworkTransform.AuthorityModes authorityMode) |
| 67 | + { |
| 68 | + return GetManagersInstance(authorityMode == NetworkTransform.AuthorityModes.Server ? m_ServerNetworkManager : owner, instance); |
46 | 69 | } |
47 | 70 |
|
48 | | - private bool AllInstancePositionsMatch() |
| 71 | + private bool AllInstancePositionsMatch(NetworkObject instance, NetworkManager owner, NetworkTransform.AuthorityModes rootAuthorityMode) |
49 | 72 | { |
50 | 73 | m_ErrorMsg.Clear(); |
| 74 | + var authorityRootPosition = GetAuthorityInstance(instance, owner, rootAuthorityMode).transform.position; |
| 75 | + var authorityChildPosition = GetAuthorityInstance(instance, owner, InverseOf(rootAuthorityMode)).transform.GetChild(0).localPosition; |
| 76 | + |
| 77 | + // The authority instances are compared too. An instance with authority over one nested |
| 78 | + // NetworkTransform is still non-authority for the other. |
51 | 79 | foreach (var networkManager in m_NetworkManagers) |
52 | 80 | { |
53 | | - var playerObject = networkManager.LocalClient.PlayerObject; |
54 | | - var playerObjectId = networkManager.LocalClient.PlayerObject.NetworkObjectId; |
55 | | - var serverRootPosition = m_ServerNetworkManager.SpawnManager.SpawnedObjects[playerObjectId].transform.position; |
56 | | - var ownerChildPosition = networkManager.LocalClient.PlayerObject.transform.GetChild(0).localPosition; |
57 | | - foreach (var client in m_NetworkManagers) |
58 | | - { |
59 | | - if (client == networkManager) |
60 | | - { |
61 | | - continue; |
62 | | - } |
63 | | - var playerClone = client.SpawnManager.SpawnedObjects[playerObjectId]; |
64 | | - var cloneRootPosition = playerClone.transform.position; |
65 | | - var cloneChildPosition = playerClone.transform.GetChild(0).localPosition; |
| 81 | + var clone = GetManagersInstance(networkManager, instance); |
| 82 | + var cloneRootPosition = clone.transform.position; |
| 83 | + var cloneChildPosition = clone.transform.GetChild(0).localPosition; |
66 | 84 |
|
67 | | - if (!Approximately(serverRootPosition, cloneRootPosition)) |
68 | | - { |
69 | | - m_ErrorMsg.AppendLine($"[{playerObject.name}][{playerClone.name}] Root mismatch ({GetVector3Values(serverRootPosition)})({GetVector3Values(cloneRootPosition)})!"); |
70 | | - } |
| 85 | + if (!Approximately(authorityRootPosition, cloneRootPosition)) |
| 86 | + { |
| 87 | + m_ErrorMsg.AppendLine($"[{rootAuthorityMode}Root][{GetDisplayName(networkManager)}] Root mismatch ({GetVector3Values(authorityRootPosition)})({GetVector3Values(cloneRootPosition)})!"); |
| 88 | + } |
71 | 89 |
|
72 | | - if (!Approximately(ownerChildPosition, cloneChildPosition)) |
73 | | - { |
74 | | - m_ErrorMsg.AppendLine($"[{playerObject.name}][{playerClone.name}] Child mismatch ({GetVector3Values(ownerChildPosition)})({GetVector3Values(cloneChildPosition)})!"); |
75 | | - } |
| 90 | + if (!Approximately(authorityChildPosition, cloneChildPosition)) |
| 91 | + { |
| 92 | + m_ErrorMsg.AppendLine($"[{rootAuthorityMode}Root][{GetDisplayName(networkManager)}] Child mismatch ({GetVector3Values(authorityChildPosition)})({GetVector3Values(cloneChildPosition)})!"); |
76 | 93 | } |
77 | 94 | } |
78 | 95 | return m_ErrorMsg.Length == 0; |
79 | 96 | } |
80 | 97 |
|
81 | 98 | /// <summary> |
82 | 99 | /// Client-Server Only |
83 | | - /// Validates that mixed authority is working properly |
84 | | - /// Root -- Server Authoritative |
85 | | - /// |--Child -- Owner Authoritative |
| 100 | + /// Validates that mixed authority is working properly for both arrangements: |
| 101 | + /// Root -- Server or Owner authoritative |
| 102 | + /// |--Child -- The inverse of the root's authority mode |
86 | 103 | /// </summary> |
87 | 104 | [UnityTest] |
88 | 105 | public IEnumerator MixedAuthorityTest() |
89 | 106 | { |
90 | | - for (int i = 0; i < k_Iterations; i++) |
| 107 | + // A client owns the instance so the owner authoritative half is never also the server. |
| 108 | + var owner = m_ClientNetworkManagers[0]; |
| 109 | + for (int i = 0; i < k_RootAuthorityModes.Length; i++) |
91 | 110 | { |
92 | | - MovePlayers(); |
93 | | - yield return WaitForConditionOrTimeOut(AllInstancePositionsMatch); |
94 | | - AssertOnTimeout($"Transforms failed to synchronize!"); |
| 111 | + var rootAuthorityMode = k_RootAuthorityModes[i]; |
| 112 | + var instance = SpawnObject(m_MixedAuthorityPrefabs[i], owner).GetComponent<NetworkObject>(); |
| 113 | + yield return WaitForSpawnedOnAllOrTimeOut(instance); |
| 114 | + AssertOnTimeout($"[{rootAuthorityMode}Root] Failed to spawn {instance.name} on all clients!"); |
| 115 | + |
| 116 | + // An instance stays registered for updates while any of its nested NetworkTransform components is non-authority. |
| 117 | + foreach (var networkManager in m_NetworkManagers) |
| 118 | + { |
| 119 | + var clone = GetManagersInstance(networkManager, instance); |
| 120 | + var hasNonAuthority = false; |
| 121 | + foreach (var networkTransform in clone.NetworkTransforms) |
| 122 | + { |
| 123 | + hasNonAuthority |= !networkTransform.CanCommitToTransform; |
| 124 | + } |
| 125 | + Assert.AreEqual(hasNonAuthority, networkManager.NetworkTransformUpdate.ContainsKey(instance.NetworkObjectId), $"[{rootAuthorityMode}Root][{GetDisplayName(networkManager)}] Unexpected update registration!"); |
| 126 | + } |
| 127 | + |
| 128 | + for (int iteration = 0; iteration < k_Iterations; iteration++) |
| 129 | + { |
| 130 | + var direction = GetRandomVector3(-1.0f, 1.0f); |
| 131 | + GetAuthorityInstance(instance, owner, rootAuthorityMode).transform.position += direction * k_MotionMagnitude; |
| 132 | + GetAuthorityInstance(instance, owner, InverseOf(rootAuthorityMode)).transform.GetChild(0).localPosition += direction * k_MotionMagnitude; |
| 133 | + |
| 134 | + yield return WaitForConditionOrTimeOut(() => AllInstancePositionsMatch(instance, owner, rootAuthorityMode)); |
| 135 | + AssertOnTimeout($"[{rootAuthorityMode}Root] Transforms failed to synchronize!\n{m_ErrorMsg}"); |
| 136 | + } |
| 137 | + |
| 138 | + instance.Despawn(); |
95 | 139 | } |
96 | 140 | } |
97 | 141 | } |
|
0 commit comments