|
10 | 10 |
|
11 | 11 | namespace Unity.Netcode.RuntimeTests |
12 | 12 | { |
13 | | - [TestFixture(RigidbodyInterpolation.Interpolate, true, true)] // This should be allowed under all condistions when using Rigidbody motion |
14 | | - [TestFixture(RigidbodyInterpolation.Extrapolate, true, true)] // This should not allow extrapolation on non-auth instances when using Rigidbody motion & NT interpolation |
15 | | - [TestFixture(RigidbodyInterpolation.Extrapolate, false, true)] // This should allow extrapolation on non-auth instances when using Rigidbody & NT has no interpolation |
16 | | - [TestFixture(RigidbodyInterpolation.Interpolate, true, false)] // This should not allow kinematic instances to have Rigidbody interpolation enabled |
17 | | - [TestFixture(RigidbodyInterpolation.Interpolate, false, false)] // Testing that rigid body interpolation remains the same if NT interpolate is disabled |
| 13 | + [TestFixture(HostOrServer.Server)] |
| 14 | + [TestFixture(HostOrServer.Host)] |
| 15 | + [TestFixture(HostOrServer.DAHost)] |
18 | 16 | internal class NetworkRigidbodyTest : NetcodeIntegrationTest |
19 | 17 | { |
20 | 18 | protected override int NumberOfClients => 1; |
21 | | - private bool m_NetworkTransformInterpolate; |
22 | | - private bool m_UseRigidBodyForMotion; |
23 | | - private RigidbodyInterpolation m_RigidbodyInterpolation; |
24 | 19 |
|
25 | | - public NetworkRigidbodyTest(RigidbodyInterpolation rigidbodyInterpolation, bool networkTransformInterpolate, bool useRigidbodyForMotion) |
| 20 | + private List<(RigidbodyInterpolation interpolationType, bool enableInterpolation, bool useRigidbodyForMotion)> m_TestConfigurations = |
| 21 | + new List<(RigidbodyInterpolation interpolationType, bool enableInterpolation, bool useRigidbodyForMotion)>() |
| 22 | + { |
| 23 | + (RigidbodyInterpolation.Interpolate, true, true), // This should be allowed under all condistions when using Rigidbody motion |
| 24 | + (RigidbodyInterpolation.Extrapolate, true, true), // This should not allow extrapolation on non-auth instances when using Rigidbody motion & NT interpolation |
| 25 | + (RigidbodyInterpolation.Extrapolate, false, true), // This should allow extrapolation on non-auth instances when using Rigidbody & NT has no interpolation |
| 26 | + (RigidbodyInterpolation.Interpolate, true, false), // This should not allow kinematic instances to have Rigidbody interpolation enabled |
| 27 | + (RigidbodyInterpolation.Interpolate, false, false) // Testing that rigidbody interpolation remains the same if NT interpolate is disabled |
| 28 | + }; |
| 29 | + |
| 30 | + /// <summary> |
| 31 | + /// The current test configuration applied to the current test running. |
| 32 | + /// </summary> |
| 33 | + private (RigidbodyInterpolation interpolationType, bool enableInterpolation, bool useRigidbodyForMotion) m_CurrentConfiguration; |
| 34 | + |
| 35 | + public NetworkRigidbodyTest(HostOrServer hostOrServer) : base(hostOrServer) |
26 | 36 | { |
27 | | - m_RigidbodyInterpolation = rigidbodyInterpolation; |
28 | | - m_NetworkTransformInterpolate = networkTransformInterpolate; |
29 | | - m_UseRigidBodyForMotion = useRigidbodyForMotion; |
30 | 37 | } |
31 | 38 |
|
32 | | - protected override void OnCreatePlayerPrefab() |
| 39 | + /// <summary> |
| 40 | + /// Base prefab for <see cref="Rigidbody"/> and <see cref="NetworkRigidbody"/> |
| 41 | + /// </summary> |
| 42 | + private GameObject m_RigidbodyPrefab; |
| 43 | + private NetworkTransform m_3DNetworkTransform; |
| 44 | + private Rigidbody m_PrefabRigidbody; |
| 45 | + private NetworkRigidbody m_PrefabNetworkRigidbody; |
| 46 | + private NetworkObject m_3DAuthorityInstance; |
| 47 | + |
| 48 | + /// <summary> |
| 49 | + /// Base prefab for <see cref="Rigidbody2D"/> and <see cref="NetworkRigidbody2D"/> |
| 50 | + /// </summary> |
| 51 | + private GameObject m_Rigidbody2DPrefab; |
| 52 | + private NetworkTransform m_2DNetworkTransform; |
| 53 | + private Rigidbody2D m_PrefabRigidbody2D; |
| 54 | + private NetworkRigidbody2D m_PrefabNetworkRigidbody2D; |
| 55 | + private NetworkObject m_2DAuthorityInstance; |
| 56 | + |
| 57 | + protected override void OnServerAndClientsCreated() |
33 | 58 | { |
34 | | - var networkTransform = m_PlayerPrefab.AddComponent<NetworkTransform>(); |
35 | | - networkTransform.Interpolate = m_NetworkTransformInterpolate; |
36 | | - var rigidbody = m_PlayerPrefab.AddComponent<Rigidbody>(); |
37 | | - rigidbody.interpolation = m_RigidbodyInterpolation; |
38 | | - var networkRigidbody = m_PlayerPrefab.AddComponent<NetworkRigidbody>(); |
39 | | - networkRigidbody.UseRigidBodyForMotion = m_UseRigidBodyForMotion; |
| 59 | + m_RigidbodyPrefab = CreateNetworkObjectPrefab("RBTest"); |
| 60 | + m_3DNetworkTransform = m_RigidbodyPrefab.AddComponent<NetworkTransform>(); |
| 61 | + m_PrefabRigidbody = m_RigidbodyPrefab.AddComponent<Rigidbody>(); |
| 62 | + m_PrefabNetworkRigidbody = m_RigidbodyPrefab.AddComponent<NetworkRigidbody>(); |
| 63 | + |
| 64 | + m_Rigidbody2DPrefab = CreateNetworkObjectPrefab("RB2DTest"); |
| 65 | + m_2DNetworkTransform = m_Rigidbody2DPrefab.AddComponent<NetworkTransform>(); |
| 66 | + m_PrefabRigidbody2D = m_Rigidbody2DPrefab.AddComponent<Rigidbody2D>(); |
| 67 | + m_PrefabNetworkRigidbody2D = m_Rigidbody2DPrefab.AddComponent<NetworkRigidbody2D>(); |
| 68 | + |
| 69 | + base.OnServerAndClientsCreated(); |
| 70 | + } |
| 71 | + |
| 72 | + private string m_ConfigHeader; |
| 73 | + private void ApplyCurrentTestConfiguration() |
| 74 | + { |
| 75 | + // Configure both 3D and 2D versions based on the current test configuration |
| 76 | + m_3DNetworkTransform.Interpolate = m_CurrentConfiguration.enableInterpolation; |
| 77 | + m_PrefabRigidbody.interpolation = m_CurrentConfiguration.interpolationType; |
| 78 | + m_PrefabNetworkRigidbody.UseRigidBodyForMotion = m_CurrentConfiguration.useRigidbodyForMotion; |
| 79 | + m_2DNetworkTransform.Interpolate = m_CurrentConfiguration.enableInterpolation; |
| 80 | + m_PrefabRigidbody2D.interpolation = m_CurrentConfiguration.interpolationType == RigidbodyInterpolation.Interpolate ? RigidbodyInterpolation2D.Interpolate : RigidbodyInterpolation2D.Extrapolate; |
| 81 | + m_PrefabNetworkRigidbody2D.UseRigidBodyForMotion = m_CurrentConfiguration.useRigidbodyForMotion; |
| 82 | + |
| 83 | + // Build a header used in assert messages |
| 84 | + m_ConfigHeader = $"[{m_CurrentConfiguration.interpolationType}][Interpolate: {m_CurrentConfiguration.enableInterpolation}][RB-Motion: {m_CurrentConfiguration.useRigidbodyForMotion}]"; |
40 | 85 | } |
41 | 86 |
|
42 | 87 | /// <summary> |
43 | | - /// Tests that a server can destroy a NetworkObject and that it gets despawned correctly. |
| 88 | + /// Iterates through the <see cref="m_TestConfigurations"/> to validate various |
| 89 | + /// Rigidbody interpolation settings and kinematic states for authority and non-authority |
| 90 | + /// instances. |
44 | 91 | /// </summary> |
45 | | - /// <returns></returns> |
46 | 92 | [UnityTest] |
47 | 93 | public IEnumerator TestRigidbodyKinematicEnableDisable() |
48 | 94 | { |
49 | | - // This is the *SERVER VERSION* of the *CLIENT PLAYER* |
50 | | - var serverClientPlayerInstance = m_ServerNetworkManager.ConnectedClients[m_ClientNetworkManagers[0].LocalClientId].PlayerObject; |
| 95 | + foreach (var configuration in m_TestConfigurations) |
| 96 | + { |
| 97 | + m_CurrentConfiguration = configuration; |
| 98 | + ApplyCurrentTestConfiguration(); |
| 99 | + yield return RunTestConfiguration(); |
| 100 | + } |
| 101 | + } |
51 | 102 |
|
52 | | - // This is the *CLIENT VERSION* of the *CLIENT PLAYER* |
53 | | - var clientPlayerInstance = m_ClientNetworkManagers[0].LocalClient.PlayerObject; |
| 103 | + /// <summary> |
| 104 | + /// Validates the current applied test configuration. |
| 105 | + /// </summary> |
| 106 | + private IEnumerator RunTestConfiguration() |
| 107 | + { |
| 108 | + var authority = GetAuthorityNetworkManager(); |
| 109 | + var nonAuthority = GetNonAuthorityNetworkManager(); |
54 | 110 |
|
55 | | - Assert.IsNotNull(serverClientPlayerInstance, $"{nameof(serverClientPlayerInstance)} is null!"); |
56 | | - Assert.IsNotNull(clientPlayerInstance, $"{nameof(clientPlayerInstance)} is null!"); |
| 111 | + // Spawn instances of both the 3D and 2D prefabs configured for the current test. |
| 112 | + m_3DAuthorityInstance = SpawnObject(m_RigidbodyPrefab, authority).GetComponent<NetworkObject>(); |
| 113 | + yield return WaitForSpawnedOnAllOrTimeOut(m_3DAuthorityInstance); |
| 114 | + AssertOnTimeout($"Failed to spawn {m_3DAuthorityInstance.name} on all clients!"); |
| 115 | + |
| 116 | + m_2DAuthorityInstance = SpawnObject(m_Rigidbody2DPrefab, authority).GetComponent<NetworkObject>(); |
| 117 | + yield return WaitForSpawnedOnAllOrTimeOut(m_2DAuthorityInstance); |
| 118 | + AssertOnTimeout($"Failed to spawn {m_2DAuthorityInstance.name} on all clients!"); |
| 119 | + |
| 120 | + // Test 3D Rigidbody |
| 121 | + #region 3D Rigidbody validation |
| 122 | + var authorityRigidbody = m_3DAuthorityInstance.GetComponent<Rigidbody>(); |
| 123 | + var nonAuthorityInstance = nonAuthority.SpawnManager.SpawnedObjects[m_3DAuthorityInstance.NetworkObjectId]; |
| 124 | + var nonAuthorityRigidbody = nonAuthorityInstance.GetComponent<Rigidbody>(); |
| 125 | + var authorityHeader = $"{m_ConfigHeader}[Authority] Client-{authority.LocalClientId}'s instance of {m_3DAuthorityInstance.name}"; |
| 126 | + // The authority instance should always be non-kinematic |
| 127 | + Assert.False(authorityRigidbody.isKinematic, $"{authorityHeader} is kinematic!"); |
| 128 | + |
| 129 | + var nonAuthorityHeader = $"{m_ConfigHeader}[Non-Authority] Client-{nonAuthority.LocalClientId}'s instance of {nonAuthorityInstance.name}"; |
| 130 | + // Non-authority instances should always be kinematic |
| 131 | + Assert.True(nonAuthorityRigidbody.isKinematic, $"{nonAuthorityHeader} is not kinematic!"); |
| 132 | + var interpolateCompareNonAuthoritative = RigidbodyInterpolation.None; |
| 133 | + |
| 134 | + if (m_CurrentConfiguration.useRigidbodyForMotion) |
| 135 | + { |
| 136 | + // The authoritative instance can be None, Interpolate, or Extrapolate for the Rigidbody interpolation settings. |
| 137 | + Assert.AreEqual(m_CurrentConfiguration.interpolationType, authorityRigidbody.interpolation, $"{authorityHeader} interpolation is {authorityRigidbody.interpolation} " + |
| 138 | + $"and not {m_CurrentConfiguration.interpolationType}!"); |
57 | 139 |
|
58 | | - var serverClientInstanceRigidBody = serverClientPlayerInstance.GetComponent<Rigidbody>(); |
59 | | - var clientRigidBody = clientPlayerInstance.GetComponent<Rigidbody>(); |
| 140 | + // When using Rigidbody motion, authoritative and non-authoritative Rigidbody interpolation settings should be preserved (except when extrapolation is used |
| 141 | + interpolateCompareNonAuthoritative = m_CurrentConfiguration.enableInterpolation ? RigidbodyInterpolation.Interpolate : m_CurrentConfiguration.interpolationType; |
60 | 142 |
|
61 | | - if (m_UseRigidBodyForMotion) |
| 143 | + } |
| 144 | + else |
62 | 145 | { |
63 | | - var interpolateCompareNonAuthoritative = m_NetworkTransformInterpolate ? RigidbodyInterpolation.Interpolate : m_RigidbodyInterpolation; |
| 146 | + Assert.AreEqual(RigidbodyInterpolation.Interpolate, authorityRigidbody.interpolation, $"{authorityHeader} interpolation is {authorityRigidbody.interpolation} " + |
| 147 | + $"and not {RigidbodyInterpolation.Interpolate}!"); |
64 | 148 |
|
65 | | - // Server authoritative NT should yield non-kinematic mode for the server-side player instance |
66 | | - Assert.False(serverClientInstanceRigidBody.isKinematic, $"[Server-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} player's {nameof(Rigidbody)} is kinematic!"); |
| 149 | + // client rigidbody has no authority with NT interpolation disabled should allow Rigidbody interpolation |
| 150 | + interpolateCompareNonAuthoritative = m_CurrentConfiguration.enableInterpolation ? RigidbodyInterpolation.None : RigidbodyInterpolation.Interpolate; |
| 151 | + } |
67 | 152 |
|
| 153 | + Assert.AreEqual(interpolateCompareNonAuthoritative, nonAuthorityRigidbody.interpolation, $"{nonAuthorityHeader} interpolation is {nonAuthorityRigidbody.interpolation} " + |
| 154 | + $"and not {interpolateCompareNonAuthoritative}!"); |
| 155 | + #endregion |
| 156 | + |
| 157 | + // Test 2D Rigidbody |
| 158 | + #region 2D Rigidbody validation |
| 159 | + var authorityRigidbody2D = m_2DAuthorityInstance.GetComponent<Rigidbody2D>(); |
| 160 | + var nonAuthorityInstance2D = nonAuthority.SpawnManager.SpawnedObjects[m_2DAuthorityInstance.NetworkObjectId]; |
| 161 | + var nonAuthorityRigidbody2D = nonAuthorityInstance2D.GetComponent<Rigidbody2D>(); |
| 162 | + |
| 163 | + authorityHeader = $"{m_ConfigHeader}[Authority] Client-{authority.LocalClientId}'s instance of {m_2DAuthorityInstance.name}"; |
| 164 | + // The authority instance should always be non-kinematic |
| 165 | + Assert.False(authorityRigidbody2D.bodyType == RigidbodyType2D.Kinematic, $"{authorityHeader} is kinematic!"); |
| 166 | + |
| 167 | + nonAuthorityHeader = $"{m_ConfigHeader}[Non-Authority] Client-{nonAuthority.LocalClientId}'s instance of {nonAuthorityInstance.name}"; |
| 168 | + // Non-authority instances should always be kinematic |
| 169 | + Assert.True(nonAuthorityRigidbody2D.bodyType == RigidbodyType2D.Kinematic, $"{nonAuthorityHeader} is not kinematic!"); |
| 170 | + var interpolateCompareNonAuthoritative2D = RigidbodyInterpolation2D.None; |
| 171 | + var configInterpolation2D = m_CurrentConfiguration.interpolationType == RigidbodyInterpolation.Interpolate ? RigidbodyInterpolation2D.Interpolate : RigidbodyInterpolation2D.Extrapolate; |
| 172 | + if (m_CurrentConfiguration.useRigidbodyForMotion) |
| 173 | + { |
68 | 174 | // The authoritative instance can be None, Interpolate, or Extrapolate for the Rigidbody interpolation settings. |
69 | | - Assert.AreEqual(m_RigidbodyInterpolation, serverClientInstanceRigidBody.interpolation, $"[Server-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} " + |
70 | | - $"player's {nameof(Rigidbody)}'s interpolation is {serverClientInstanceRigidBody.interpolation} and not {m_RigidbodyInterpolation}!"); |
71 | | - |
72 | | - // Server authoritative NT should yield kinematic mode for the client-side player instance |
73 | | - Assert.True(clientRigidBody.isKinematic, $"[Client-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} player's {nameof(Rigidbody)} is not kinematic!"); |
| 175 | + Assert.AreEqual(configInterpolation2D, authorityRigidbody2D.interpolation, $"{authorityHeader} interpolation is {authorityRigidbody2D.interpolation} " + |
| 176 | + $"and not {m_CurrentConfiguration.interpolationType}!"); |
74 | 177 |
|
75 | 178 | // When using Rigidbody motion, authoritative and non-authoritative Rigidbody interpolation settings should be preserved (except when extrapolation is used |
76 | | - Assert.AreEqual(interpolateCompareNonAuthoritative, clientRigidBody.interpolation, $"[Client-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} " + |
77 | | - $"player's {nameof(Rigidbody)}'s interpolation is {clientRigidBody.interpolation} and not {interpolateCompareNonAuthoritative}!"); |
| 179 | + interpolateCompareNonAuthoritative2D = m_CurrentConfiguration.enableInterpolation ? RigidbodyInterpolation2D.Interpolate : configInterpolation2D; |
78 | 180 | } |
79 | 181 | else |
80 | 182 | { |
81 | | - // server rigidbody has authority and should not be kinematic |
82 | | - Assert.False(serverClientInstanceRigidBody.isKinematic, $"[Server-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} player's {nameof(Rigidbody)} is kinematic!"); |
83 | | - Assert.AreEqual(RigidbodyInterpolation.Interpolate, serverClientInstanceRigidBody.interpolation, $"[Server-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} " + |
84 | | - $"player's {nameof(Rigidbody)}'s interpolation is {serverClientInstanceRigidBody.interpolation} and not {nameof(RigidbodyInterpolation.Interpolate)}!"); |
85 | | - |
86 | | - // Server authoritative NT should yield kinematic mode for the client-side player instance |
87 | | - Assert.True(clientRigidBody.isKinematic, $"[Client-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} player's {nameof(Rigidbody)} is not kinematic!"); |
| 183 | + Assert.AreEqual(RigidbodyInterpolation2D.Interpolate, authorityRigidbody2D.interpolation, $"{authorityHeader} interpolation is {authorityRigidbody2D.interpolation} " + |
| 184 | + $"and not {RigidbodyInterpolation2D.Interpolate}!"); |
88 | 185 |
|
89 | 186 | // client rigidbody has no authority with NT interpolation disabled should allow Rigidbody interpolation |
90 | | - if (!m_NetworkTransformInterpolate) |
91 | | - { |
92 | | - Assert.AreEqual(RigidbodyInterpolation.Interpolate, clientRigidBody.interpolation, $"[Client-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} " + |
93 | | - $"player's {nameof(Rigidbody)}'s interpolation is {clientRigidBody.interpolation} and not {nameof(RigidbodyInterpolation.Interpolate)}!"); |
94 | | - } |
95 | | - else |
96 | | - { |
97 | | - Assert.AreEqual(RigidbodyInterpolation.None, clientRigidBody.interpolation, $"[Client-Side] Client-{m_ClientNetworkManagers[0].LocalClientId} " + |
98 | | - $"player's {nameof(Rigidbody)}'s interpolation is {clientRigidBody.interpolation} and not {nameof(RigidbodyInterpolation.None)}!"); |
99 | | - } |
| 187 | + interpolateCompareNonAuthoritative2D = m_CurrentConfiguration.enableInterpolation ? RigidbodyInterpolation2D.None : RigidbodyInterpolation2D.Interpolate; |
100 | 188 | } |
101 | 189 |
|
102 | | - // despawn the server player (but keep it around on the server) |
103 | | - serverClientPlayerInstance.Despawn(false); |
| 190 | + Assert.AreEqual(interpolateCompareNonAuthoritative2D, nonAuthorityRigidbody2D.interpolation, $"{nonAuthorityHeader} interpolation is {nonAuthorityRigidbody2D.interpolation} " + |
| 191 | + $"and not {interpolateCompareNonAuthoritative}!"); |
| 192 | + #endregion |
| 193 | + |
| 194 | + var spawnedInstances = new List<NetworkObject>() { m_3DAuthorityInstance, m_2DAuthorityInstance }; |
| 195 | + m_3DAuthorityInstance.Despawn(); |
| 196 | + m_2DAuthorityInstance.Despawn(); |
| 197 | + yield return WaitForDespawnedOnAllOrTimeOut(spawnedInstances); |
| 198 | + AssertOnTimeout($"Failed to de-spawn instances on all clients!"); |
| 199 | + m_3DAuthorityInstance = null; |
| 200 | + m_2DAuthorityInstance = null; |
| 201 | + } |
104 | 202 |
|
105 | | - yield return WaitForConditionOrTimeOut(() => !serverClientPlayerInstance.IsSpawned && !clientPlayerInstance.IsSpawned); |
106 | | - AssertOnTimeout("Timed out waiting for client player to despawn on both server and client!"); |
| 203 | + /// <summary> |
| 204 | + /// Handle clean up in case of a failed test |
| 205 | + /// </summary> |
| 206 | + protected override IEnumerator OnTearDown() |
| 207 | + { |
| 208 | + // If either of these are not null then we most likely failed and didn't cleanup. |
107 | 209 |
|
108 | | - // When despawned, we should always be kinematic (i.e. don't apply physics when despawned) |
109 | | - Assert.True(serverClientInstanceRigidBody.isKinematic, $"[Server-Side][Despawned] Client-{m_ClientNetworkManagers[0].LocalClientId} player's {nameof(Rigidbody)} is not kinematic when despawned!"); |
110 | | - Assert.IsTrue(clientPlayerInstance == null, $"[Client-Side] Player {nameof(NetworkObject)} is not null!"); |
| 210 | + // Clean-up m_3DAuthorityInstance |
| 211 | + if (m_3DAuthorityInstance) |
| 212 | + { |
| 213 | + Object.Destroy(m_3DAuthorityInstance); |
| 214 | + m_3DAuthorityInstance = null; |
| 215 | + } |
| 216 | + |
| 217 | + // Clean-up m_2DAuthorityInstance |
| 218 | + if (m_2DAuthorityInstance) |
| 219 | + { |
| 220 | + Object.Destroy(m_2DAuthorityInstance); |
| 221 | + m_2DAuthorityInstance = null; |
| 222 | + } |
| 223 | + |
| 224 | + return base.OnTearDown(); |
111 | 225 | } |
112 | 226 | } |
113 | 227 |
|
|
0 commit comments