Skip to content

Commit a997d6e

Browse files
test
This adds an integration test to validate the fix for this PR.
1 parent c7ca276 commit a997d6e

File tree

2 files changed

+118
-29
lines changed

2 files changed

+118
-29
lines changed

com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/OwnerModifiedTests.cs

Lines changed: 93 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
using System.Collections;
22
using System.Collections.Generic;
3+
using System.Text;
34
using NUnit.Framework;
45
using Unity.Netcode.TestHelpers.Runtime;
56
using UnityEngine;
67
using UnityEngine.TestTools;
78

89
namespace Unity.Netcode.RuntimeTests
910
{
10-
// This is a bit of a quirky test.
11-
// Addresses MTT-4386 #2109
12-
// Where the NetworkVariable updates would be repeated on some clients.
13-
// The twist comes fom the updates needing to happens very specifically for the issue to repro in tests
1411

1512
internal class OwnerModifiedObject : NetworkBehaviour, INetworkUpdateSystem
1613
{
@@ -74,17 +71,36 @@ public void InitializeLastCient()
7471
}
7572
}
7673

74+
internal class ChangeValueOnAuthority : NetworkBehaviour
75+
{
76+
public NetworkVariable<int> SomeIntValue = new NetworkVariable<int>();
77+
78+
public override void OnNetworkSpawn()
79+
{
80+
if (HasAuthority)
81+
{
82+
SomeIntValue.Value++;
83+
}
84+
base.OnNetworkSpawn();
85+
}
86+
87+
protected override void OnNetworkPostSpawn()
88+
{
89+
if (HasAuthority)
90+
{
91+
SomeIntValue.Value++;
92+
}
93+
base.OnNetworkPostSpawn();
94+
}
95+
}
96+
7797
[TestFixture(HostOrServer.DAHost)]
7898
[TestFixture(HostOrServer.Host)]
7999
internal class OwnerModifiedTests : NetcodeIntegrationTest
80100
{
81101
protected override int NumberOfClients => 2;
82102

83-
// TODO: [CmbServiceTests] Adapt to run with the service
84-
protected override bool UseCMBService()
85-
{
86-
return false;
87-
}
103+
private GameObject m_SpawnObject;
88104

89105
public OwnerModifiedTests(HostOrServer hostOrServer) : base(hostOrServer) { }
90106

@@ -93,13 +109,33 @@ protected override void OnCreatePlayerPrefab()
93109
m_PlayerPrefab.AddComponent<OwnerModifiedObject>();
94110
}
95111

112+
protected override void OnServerAndClientsCreated()
113+
{
114+
m_SpawnObject = CreateNetworkObjectPrefab("SpawnObj");
115+
m_SpawnObject.AddComponent<ChangeValueOnAuthority>();
116+
base.OnServerAndClientsCreated();
117+
}
118+
119+
private NetworkManager m_LastClient;
120+
121+
protected override void OnNewClientStartedAndConnected(NetworkManager networkManager)
122+
{
123+
m_LastClient = networkManager;
124+
base.OnNewClientStartedAndConnected(networkManager);
125+
}
126+
127+
/// <summary>
128+
/// Addresses MTT-4386 #2109
129+
/// Verify NetworkVariable updates are not repeated on some clients.
130+
/// TODO: This test needs to be re-written/overhauled.
131+
/// </summary>
96132
[UnityTest]
97-
public IEnumerator OwnerModifiedTest()
133+
public IEnumerator VerifyDoesNotRepeatOnSomeClients()
98134
{
99135
OwnerModifiedObject.EnableVerbose = m_EnableVerboseDebug;
100136
// We use this to assure we are the "last client" connected.
101137
yield return CreateAndStartNewClient();
102-
var ownerModLastClient = m_ClientNetworkManagers[2].LocalClient.PlayerObject.GetComponent<OwnerModifiedObject>();
138+
var ownerModLastClient = m_LastClient.LocalClient.PlayerObject.GetComponent<OwnerModifiedObject>();
103139
ownerModLastClient.InitializeLastCient();
104140

105141
// Run through all update loops setting the value once every 5 frames
@@ -116,5 +152,51 @@ public IEnumerator OwnerModifiedTest()
116152
// We'll have at least one update per stage per client, if all goes well.
117153
Assert.True(OwnerModifiedObject.Updates > 20);
118154
}
155+
156+
157+
private ChangeValueOnAuthority m_SessionAuthorityInstance;
158+
private ChangeValueOnAuthority m_InstanceAuthority;
159+
160+
private bool NetworkVariablesMatch(StringBuilder errorLog)
161+
{
162+
foreach (var networkManager in m_NetworkManagers)
163+
{
164+
if (networkManager == m_InstanceAuthority.NetworkManager)
165+
{
166+
continue;
167+
}
168+
169+
var changeValue = networkManager.SpawnManager.SpawnedObjects[m_InstanceAuthority.NetworkObjectId].GetComponent<ChangeValueOnAuthority>();
170+
if (changeValue.SomeIntValue.Value != m_InstanceAuthority.SomeIntValue.Value)
171+
{
172+
errorLog.AppendLine($"[Client-{networkManager.LocalClientId}] {changeValue.name} value is {changeValue.SomeIntValue.Value} but was expecting {m_InstanceAuthority.SomeIntValue.Value}!");
173+
}
174+
}
175+
176+
return errorLog.Length == 0;
177+
}
178+
179+
/// <summary>
180+
/// Verifies that when running a distributed authority network topology
181+
/// </summary>
182+
[UnityTest]
183+
public IEnumerator OwnershipSpawnedAndUpdatedDuringSpawn()
184+
{
185+
var authority = GetAuthorityNetworkManager();
186+
var nonAuthority = GetNonAuthorityNetworkManager();
187+
m_SessionAuthorityInstance = Object.Instantiate(m_SpawnObject).GetComponent<ChangeValueOnAuthority>();
188+
189+
SpawnInstanceWithOwnership(m_SessionAuthorityInstance.GetComponent<NetworkObject>(), authority, nonAuthority.LocalClientId);
190+
yield return WaitForSpawnedOnAllOrTimeOut(m_SessionAuthorityInstance.NetworkObjectId);
191+
AssertOnTimeout($"Failed to spawn {m_SessionAuthorityInstance.name} on all clients!");
192+
193+
m_InstanceAuthority = nonAuthority.SpawnManager.SpawnedObjects[m_SessionAuthorityInstance.NetworkObjectId].GetComponent<ChangeValueOnAuthority>();
194+
195+
yield return WaitForConditionOrTimeOut(NetworkVariablesMatch);
196+
AssertOnTimeout($"The {nameof(ChangeValueOnAuthority.SomeIntValue)} failed to synchronize on all clients!");
197+
198+
Assert.IsTrue(m_SessionAuthorityInstance.SomeIntValue.Value == 2, "No values were updated on the spawn authority instance!");
199+
Assert.IsTrue(m_InstanceAuthority.SomeIntValue.Value == 2, "No values were updated on the owner's instance!");
200+
}
119201
}
120202
}

com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using UnityEngine;
1212
using UnityEngine.SceneManagement;
1313
using UnityEngine.TestTools;
14+
using static UnityEngine.UI.GridLayoutGroup;
1415
using Object = UnityEngine.Object;
1516

1617
namespace Unity.Netcode.TestHelpers.Runtime
@@ -2171,42 +2172,35 @@ protected GameObject SpawnPlayerObject(GameObject prefabGameObject, NetworkManag
21712172
return SpawnObject(prefabNetworkObject, owner, destroyWithScene, true);
21722173
}
21732174

2174-
/// <summary>
2175-
/// Spawn an already instantiated instance of a network prefab.
2176-
/// Note: If you pass in the NetworkPrefab itself this method will not create an instance but will spawn the pefab itself. (don't do this)
2177-
/// </summary>
2178-
/// <param name="networkObjectToSpawn">the instance of a prefab <see cref="NetworkObject"/> to spawn</param>
2179-
/// <param name="owner">the owner of the instance</param>
2180-
/// <param name="destroyWithScene">default is false</param>
2181-
/// <param name="isPlayerObject">when <see cref="true"/>, the object will be spawned as the <see cref="NetworkManager.LocalClientId"/> owned player.</param>
2182-
protected void SpawnObjectInstance(NetworkObject networkObjectToSpawn, NetworkManager owner, bool destroyWithScene = false, bool isPlayerObject = false)
2175+
2176+
internal void SpawnInstanceWithOwnership(NetworkObject networkObjectToSpawn, NetworkManager spawnAuthority, ulong clientId, bool destroyWithScene = false, bool isPlayerObject = false)
21832177
{
2184-
if (owner.NetworkConfig.NetworkTopology == NetworkTopologyTypes.DistributedAuthority)
2178+
if (spawnAuthority.NetworkConfig.NetworkTopology == NetworkTopologyTypes.DistributedAuthority)
21852179
{
2186-
networkObjectToSpawn.NetworkManagerOwner = owner; // Required to assure the client does the spawning
2180+
networkObjectToSpawn.NetworkManagerOwner = spawnAuthority; // Required to assure the client does the spawning
21872181
if (isPlayerObject)
21882182
{
2189-
networkObjectToSpawn.SpawnAsPlayerObject(owner.LocalClientId, destroyWithScene);
2183+
networkObjectToSpawn.SpawnAsPlayerObject(clientId, destroyWithScene);
21902184
}
21912185
else
21922186
{
2193-
networkObjectToSpawn.SpawnWithOwnership(owner.LocalClientId, destroyWithScene);
2187+
networkObjectToSpawn.SpawnWithOwnership(clientId, destroyWithScene);
21942188
}
21952189
}
21962190
else
21972191
{
21982192
networkObjectToSpawn.NetworkManagerOwner = m_ServerNetworkManager; // Required to assure the server does the spawning
2199-
if (owner == m_ServerNetworkManager)
2193+
if (spawnAuthority == m_ServerNetworkManager)
22002194
{
22012195
if (m_UseHost)
22022196
{
22032197
if (isPlayerObject)
22042198
{
2205-
networkObjectToSpawn.SpawnAsPlayerObject(owner.LocalClientId, destroyWithScene);
2199+
networkObjectToSpawn.SpawnAsPlayerObject(clientId, destroyWithScene);
22062200
}
22072201
else
22082202
{
2209-
networkObjectToSpawn.SpawnWithOwnership(owner.LocalClientId, destroyWithScene);
2203+
networkObjectToSpawn.SpawnWithOwnership(clientId, destroyWithScene);
22102204
}
22112205
}
22122206
else
@@ -2218,16 +2212,29 @@ protected void SpawnObjectInstance(NetworkObject networkObjectToSpawn, NetworkMa
22182212
{
22192213
if (isPlayerObject)
22202214
{
2221-
networkObjectToSpawn.SpawnAsPlayerObject(owner.LocalClientId, destroyWithScene);
2215+
networkObjectToSpawn.SpawnAsPlayerObject(clientId, destroyWithScene);
22222216
}
22232217
else
22242218
{
2225-
networkObjectToSpawn.SpawnWithOwnership(owner.LocalClientId, destroyWithScene);
2219+
networkObjectToSpawn.SpawnWithOwnership(clientId, destroyWithScene);
22262220
}
22272221
}
22282222
}
22292223
}
22302224

2225+
/// <summary>
2226+
/// Spawn an already instantiated instance of a network prefab.
2227+
/// Note: If you pass in the NetworkPrefab itself this method will not create an instance but will spawn the pefab itself. (don't do this)
2228+
/// </summary>
2229+
/// <param name="networkObjectToSpawn">the instance of a prefab <see cref="NetworkObject"/> to spawn</param>
2230+
/// <param name="owner">the owner of the instance</param>
2231+
/// <param name="destroyWithScene">default is false</param>
2232+
/// <param name="isPlayerObject">when <see cref="true"/>, the object will be spawned as the <see cref="NetworkManager.LocalClientId"/> owned player.</param>
2233+
protected void SpawnObjectInstance(NetworkObject networkObjectToSpawn, NetworkManager owner, bool destroyWithScene = false, bool isPlayerObject = false)
2234+
{
2235+
SpawnInstanceWithOwnership(networkObjectToSpawn, owner, owner.LocalClientId, destroyWithScene, isPlayerObject);
2236+
}
2237+
22312238
/// <summary>
22322239
/// Spawn a NetworkObject prefab instance
22332240
/// </summary>

0 commit comments

Comments
 (0)