Skip to content

Commit 4b15869

Browse files
fix: Adding exception for silent failure for clients getting other player's object #844Merge pull request #844 from Unity-Technologies/feature/adding-exception-for-client-side-player-object-get
fix: Adding exception for silent failure for clients getting other player's object. Accessing other player objects should only be available from the server. clients should only be able to access their own players. test: adding tests for spawn manager
2 parents 75de2e0 + 0ebc1e6 commit 4b15869

File tree

6 files changed

+283
-29
lines changed

6 files changed

+283
-29
lines changed

com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,12 @@ public void StopServer()
599599
var disconnectedIds = new HashSet<ulong>();
600600
//Don't know if I have to disconnect the clients. I'm assuming the NetworkTransport does all the cleaning on shtudown. But this way the clients get a disconnect message from server (so long it does't get lost)
601601

602+
// make sure all RPCs are flushed before transport disconnect clients
603+
if (RpcQueueContainer != null)
604+
{
605+
RpcQueueContainer.ProcessAndFlushRpcQueue(queueType: RpcQueueContainer.RpcQueueProcessingTypes.Send, NetworkUpdateStage.PostLateUpdate); // flushing messages in case transport's disconnect
606+
}
607+
602608
foreach (KeyValuePair<ulong, NetworkClient> pair in ConnectedClients)
603609
{
604610
if (!disconnectedIds.Contains(pair.Key))

com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,15 @@ public NetworkObject GetLocalPlayerObject()
6868
}
6969

7070
/// <summary>
71-
/// Returns the player object with a given clientId or null if one does not exist
71+
/// Returns the player object with a given clientId or null if one does not exist. This is only valid server side.
7272
/// </summary>
7373
/// <returns>The player object with a given clientId or null if one does not exist</returns>
7474
public NetworkObject GetPlayerNetworkObject(ulong clientId)
7575
{
76+
if (!NetworkManager.IsServer && NetworkManager.LocalClientId != clientId)
77+
{
78+
throw new NotServerException("Only the server can find player objects from other clients.");
79+
}
7680
if (NetworkManager.ConnectedClients.TryGetValue(clientId, out NetworkClient networkClient))
7781
{
7882
return networkClient.PlayerObject;

com.unity.multiplayer.mlapi/Tests/Runtime/MultiInstance/MultiInstanceHelpers.cs

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,20 @@ public static class MultiInstanceHelpers
2626
/// <param name="clients">The clients NetworkManagers</param>
2727
public static bool Create(int clientCount, out NetworkManager server, out NetworkManager[] clients)
2828
{
29-
clients = new NetworkManager[clientCount];
29+
NetworkManagerInstances = new List<NetworkManager>();
30+
31+
CreateNewClients(clientCount, out clients);
3032

31-
for (int i = 0; i < clientCount; i++)
3233
{
3334
// Create gameObject
34-
var go = new GameObject("NetworkManager - Client - " + i);
35+
var go = new GameObject("NetworkManager - Server");
36+
3537
// Create networkManager component
36-
clients[i] = go.AddComponent<NetworkManager>();
38+
server = go.AddComponent<NetworkManager>();
39+
NetworkManagerInstances.Insert(0, server);
3740

3841
// Set the NetworkConfig
39-
clients[i].NetworkConfig = new NetworkConfig()
42+
server.NetworkConfig = new NetworkConfig()
4043
{
4144
// Set the current scene to prevent unexpected log messages which would trigger a failure
4245
RegisteredScenes = new List<string>() { SceneManager.GetActiveScene().name },
@@ -45,18 +48,28 @@ public static bool Create(int clientCount, out NetworkManager server, out Networ
4548
};
4649
}
4750

48-
NetworkManagerInstances = new List<NetworkManager>(clients);
51+
return true;
52+
}
53+
54+
/// <summary>
55+
/// Used to add a client to the already existing list of clients
56+
/// </summary>
57+
/// <param name="clientCount">The amount of clients</param>
58+
/// <param name="clients"></param>
59+
/// <returns></returns>
60+
public static bool CreateNewClients(int clientCount, out NetworkManager[] clients)
61+
{
62+
clients = new NetworkManager[clientCount];
4963

64+
for (int i = 0; i < clientCount; i++)
5065
{
5166
// Create gameObject
52-
var go = new GameObject("NetworkManager - Server");
53-
67+
var go = new GameObject("NetworkManager - Client - " + i);
5468
// Create networkManager component
55-
server = go.AddComponent<NetworkManager>();
56-
NetworkManagerInstances.Insert(0, server);
69+
clients[i] = go.AddComponent<NetworkManager>();
5770

5871
// Set the NetworkConfig
59-
server.NetworkConfig = new NetworkConfig()
72+
clients[i].NetworkConfig = new NetworkConfig()
6073
{
6174
// Set the current scene to prevent unexpected log messages which would trigger a failure
6275
RegisteredScenes = new List<string>() { SceneManager.GetActiveScene().name },
@@ -65,9 +78,21 @@ public static bool Create(int clientCount, out NetworkManager server, out Networ
6578
};
6679
}
6780

81+
NetworkManagerInstances.AddRange(clients);
6882
return true;
6983
}
7084

85+
/// <summary>
86+
/// Stops one single client and makes sure to cleanup any static variables in this helper
87+
/// </summary>
88+
/// <param name="clientToStop"></param>
89+
public static void StopOneClient(NetworkManager clientToStop)
90+
{
91+
clientToStop.StopClient();
92+
Object.Destroy(clientToStop.gameObject);
93+
NetworkManagerInstances.Remove(clientToStop);
94+
}
95+
7196
/// <summary>
7297
/// Should always be invoked when finished with a single unit test
7398
/// (i.e. during TearDown)
@@ -112,7 +137,7 @@ public static bool Start(bool host, NetworkManager server, NetworkManager[] clie
112137
}
113138
else
114139
{
115-
server.StartClient();
140+
server.StartServer();
116141
}
117142

118143
for (int i = 0; i < clients.Length; i++)
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
using System;
2+
using System.Collections;
3+
using MLAPI.Exceptions;
4+
using NUnit.Framework;
5+
using UnityEngine;
6+
using UnityEngine.TestTools;
7+
using Object = System.Object;
8+
9+
namespace MLAPI.RuntimeTests
10+
{
11+
public class NetworkSpawnManagerTests
12+
{
13+
private NetworkManager m_ServerNetworkManager;
14+
private NetworkManager[] m_ClientNetworkManagers;
15+
private GameObject m_PlayerPrefab;
16+
private int m_OriginalTargetFrameRate;
17+
18+
private ulong serverSideClientId => m_ServerNetworkManager.ServerClientId;
19+
private ulong clientSideClientId => m_ClientNetworkManagers[0].LocalClientId;
20+
private ulong otherClientSideClientId => m_ClientNetworkManagers[1].LocalClientId;
21+
22+
[UnitySetUp]
23+
public IEnumerator Setup()
24+
{
25+
// Just always track the current target frame rate (will be re-applied upon TearDown)
26+
m_OriginalTargetFrameRate = Application.targetFrameRate;
27+
28+
// Since we use frame count as a metric, we need to assure it runs at a "common update rate"
29+
// between platforms (i.e. Ubuntu seems to run at much higher FPS when set to -1)
30+
if (Application.targetFrameRate < 0 || Application.targetFrameRate > 120)
31+
{
32+
Application.targetFrameRate = 120;
33+
}
34+
35+
// Create multiple NetworkManager instances
36+
if (!MultiInstanceHelpers.Create(2, out NetworkManager server, out NetworkManager[] clients))
37+
{
38+
Debug.LogError("Failed to create instances");
39+
Assert.Fail("Failed to create instances");
40+
}
41+
42+
m_ServerNetworkManager = server;
43+
m_ClientNetworkManagers = clients;
44+
45+
// Create playerPrefab
46+
m_PlayerPrefab = new GameObject("Player");
47+
NetworkObject networkObject = m_PlayerPrefab.AddComponent<NetworkObject>();
48+
49+
// Make it a prefab
50+
MultiInstanceHelpers.MakeNetworkedObjectTestPrefab(networkObject);
51+
52+
// Set the player prefab
53+
server.NetworkConfig.PlayerPrefab = m_PlayerPrefab;
54+
55+
for (int i = 0; i < clients.Length; i++)
56+
{
57+
clients[i].NetworkConfig.PlayerPrefab = m_PlayerPrefab;
58+
}
59+
60+
// Start the instances
61+
if (!MultiInstanceHelpers.Start(true, server, clients))
62+
{
63+
Debug.LogError("Failed to start instances");
64+
Assert.Fail("Failed to start instances");
65+
}
66+
67+
// Wait for connection on client side
68+
for (int i = 0; i < clients.Length; i++)
69+
{
70+
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientConnected(clients[i]));
71+
}
72+
73+
// Wait for connection on server side
74+
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientsConnectedToServer(server, clientCount: 3));
75+
}
76+
77+
[Test]
78+
public void TestServerCanAccessItsOwnPlayer()
79+
{
80+
// server can access its own player
81+
var serverSideServerPlayerObject = m_ServerNetworkManager.SpawnManager.GetPlayerNetworkObject(serverSideClientId);
82+
Assert.NotNull(serverSideServerPlayerObject);
83+
Assert.AreEqual(serverSideClientId, serverSideServerPlayerObject.OwnerClientId);
84+
}
85+
86+
[Test]
87+
public void TestServerCanAccessOtherPlayers()
88+
{
89+
// server can access other players
90+
var serverSideClientPlayerObject = m_ServerNetworkManager.SpawnManager.GetPlayerNetworkObject(clientSideClientId);
91+
Assert.NotNull(serverSideClientPlayerObject);
92+
Assert.AreEqual(clientSideClientId, serverSideClientPlayerObject.OwnerClientId);
93+
94+
var serverSideOtherClientPlayerObject = m_ServerNetworkManager.SpawnManager.GetPlayerNetworkObject(otherClientSideClientId);
95+
Assert.NotNull(serverSideOtherClientPlayerObject);
96+
Assert.AreEqual(otherClientSideClientId, serverSideOtherClientPlayerObject.OwnerClientId);
97+
}
98+
99+
[Test]
100+
public void TestClientCantAccessServerPlayer()
101+
{
102+
// client can't access server player
103+
Assert.Throws<NotServerException>(() =>
104+
{
105+
m_ClientNetworkManagers[0].SpawnManager.GetPlayerNetworkObject(serverSideClientId);
106+
});
107+
}
108+
109+
[Test]
110+
public void TestClientCanAccessOwnPlayer()
111+
{
112+
// client can access own player
113+
var clientSideClientPlayerObject = m_ClientNetworkManagers[0].SpawnManager.GetPlayerNetworkObject(clientSideClientId);
114+
Assert.NotNull(clientSideClientPlayerObject);
115+
Assert.AreEqual(clientSideClientId, clientSideClientPlayerObject.OwnerClientId);
116+
}
117+
118+
[Test]
119+
public void TestClientCantAccessOtherPlayer()
120+
{
121+
// client can't access other player
122+
Assert.Throws<NotServerException>(() =>
123+
{
124+
m_ClientNetworkManagers[0].SpawnManager.GetPlayerNetworkObject(otherClientSideClientId);
125+
});
126+
}
127+
128+
[Test]
129+
public void TestServerGetsNullValueIfInvalidId()
130+
{
131+
// server gets null value if invalid id
132+
var nullPlayer = m_ServerNetworkManager.SpawnManager.GetPlayerNetworkObject(9999);
133+
Assert.Null(nullPlayer);
134+
}
135+
136+
[Test]
137+
public void TestServerCanUseGetLocalPlayerObject()
138+
{
139+
// test server can use GetLocalPlayerObject
140+
var serverSideServerPlayerObject = m_ServerNetworkManager.SpawnManager.GetLocalPlayerObject();
141+
Assert.NotNull(serverSideServerPlayerObject);
142+
Assert.AreEqual(serverSideClientId, serverSideServerPlayerObject.OwnerClientId);
143+
}
144+
145+
[Test]
146+
public void TestClientCanUseGetLocalPlayerObject()
147+
{
148+
// test client can use GetLocalPlayerObject
149+
var clientSideClientPlayerObject = m_ClientNetworkManagers[0].SpawnManager.GetLocalPlayerObject();
150+
Assert.NotNull(clientSideClientPlayerObject);
151+
Assert.AreEqual(clientSideClientId, clientSideClientPlayerObject.OwnerClientId);
152+
}
153+
154+
[UnityTest]
155+
public IEnumerator TestConnectAndDisconnect()
156+
{
157+
// test when client connects, player object is now available
158+
159+
// connect new client
160+
if (!MultiInstanceHelpers.CreateNewClients(1, out NetworkManager[] clients))
161+
{
162+
Debug.LogError("Failed to create instances");
163+
Assert.Fail("Failed to create instances");
164+
}
165+
var newClientNetworkManager = clients[0];
166+
newClientNetworkManager.NetworkConfig.PlayerPrefab = m_PlayerPrefab;
167+
newClientNetworkManager.StartClient();
168+
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientConnected(newClientNetworkManager));
169+
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForCondition(() => m_ServerNetworkManager.ConnectedClients.ContainsKey(newClientNetworkManager.LocalClientId)));
170+
var newClientLocalClientId = newClientNetworkManager.LocalClientId;
171+
172+
// test new client can get that itself locally
173+
var newPlayerObject = newClientNetworkManager.SpawnManager.GetLocalPlayerObject();
174+
Assert.NotNull(newPlayerObject);
175+
Assert.AreEqual(newClientLocalClientId, newPlayerObject.OwnerClientId);
176+
// test server can get that new client locally
177+
var serverSideNewClientPlayer = m_ServerNetworkManager.SpawnManager.GetPlayerNetworkObject(newClientLocalClientId);
178+
Assert.NotNull(serverSideNewClientPlayer);
179+
Assert.AreEqual(newClientLocalClientId, serverSideNewClientPlayer.OwnerClientId);
180+
181+
// test when client disconnects, player object no longer available.
182+
var nbConnectedClients = m_ServerNetworkManager.ConnectedClients.Count;
183+
MultiInstanceHelpers.StopOneClient(newClientNetworkManager);
184+
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForCondition(() => m_ServerNetworkManager.ConnectedClients.Count == nbConnectedClients - 1));
185+
serverSideNewClientPlayer = m_ServerNetworkManager.SpawnManager.GetPlayerNetworkObject(newClientLocalClientId);
186+
Assert.Null(serverSideNewClientPlayer);
187+
}
188+
189+
[UnityTearDown]
190+
public IEnumerator Teardown()
191+
{
192+
// Shutdown and clean up both of our NetworkManager instances
193+
MultiInstanceHelpers.Destroy();
194+
UnityEngine.Object.Destroy(m_PlayerPrefab);
195+
196+
// Set the application's target frame rate back to its original value
197+
Application.targetFrameRate = m_OriginalTargetFrameRate;
198+
yield return new WaitForSeconds(0); // wait for next frame so everything is destroyed, so following tests can execute from clean environment
199+
}
200+
}
201+
}

com.unity.multiplayer.mlapi/Tests/Runtime/NetworkSpawnManagerTests.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)