Skip to content

Commit 297af21

Browse files
fix: decoupling SessionManager from NetworkManager [MTT-2603] (#581)
* Decoupled SessionManager from NetworkManager * simplified flow of clearing disconnected players data and reinitializing the rest on session end * Added HasSessionStarted field to decide if we keep or delete a disconnecting player's data * Renamed Clear to OnServerEnded and added call to it when host disconnects
1 parent 7c209f8 commit 297af21

File tree

5 files changed

+67
-82
lines changed

5 files changed

+67
-82
lines changed

Assets/BossRoom/Scripts/Server/Game/State/ServerBossRoomState.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ public override void OnNetworkSpawn()
6161
NetworkManager.SceneManager.OnSceneEvent += OnClientSceneChanged;
6262

6363
DoInitialSpawnIfPossible();
64+
65+
SessionManager<SessionPlayerData>.Instance.OnSessionStarted();
6466
}
6567
}
6668

Assets/BossRoom/Scripts/Server/Game/State/ServerCharSelectState.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,6 @@ public override void OnNetworkSpawn()
204204
CharSelectData.OnClientChangedSeat += OnClientChangedSeat;
205205

206206
NetworkManager.Singleton.SceneManager.OnSceneEvent += OnSceneEvent;
207-
208-
SessionManager<SessionPlayerData>.Instance.OnSessionStarted();
209207
}
210208
}
211209

Assets/BossRoom/Scripts/Shared/Net/ConnectionManagement/GameNetPortal.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,10 +239,10 @@ public void RequestDisconnect()
239239
if (NetManager.IsServer)
240240
{
241241
NetManager.SceneManager.OnSceneEvent -= OnSceneEvent;
242+
SessionManager<SessionPlayerData>.Instance.OnServerEnded();
242243
}
243244
m_ClientPortal.OnUserDisconnectRequest();
244245
m_ServerPortal.OnUserDisconnectRequest();
245-
SessionManager<SessionPlayerData>.Instance.OnUserDisconnectRequest();
246246
NetManager.Shutdown();
247247
}
248248

Assets/BossRoom/Scripts/Shared/Net/ConnectionManagement/ServerGameNetPortal.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ void OnClientDisconnect(ulong clientId)
102102
{
103103
m_LobbyServiceFacade.DeleteLobbyAsync(m_LobbyServiceFacade.CurrentUnityLobby.Id);
104104
}
105+
SessionManager<SessionPlayerData>.Instance.OnServerEnded();
105106
}
106107
else
107108
{
@@ -112,6 +113,7 @@ void OnClientDisconnect(ulong clientId)
112113
{
113114
m_LobbyServiceFacade.RemovePlayerFromLobbyAsync(playerId, m_LobbyServiceFacade.CurrentUnityLobby.Id, null, null);
114115
}
116+
SessionManager<SessionPlayerData>.Instance.DisconnectClient(clientId);
115117
}
116118
}
117119
}

Packages/com.unity.multiplayer.samples.coop/Utilities/Net/SessionManager.cs

Lines changed: 62 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.Collections.Generic;
3-
using Unity.Netcode;
43
using UnityEngine;
54

65
namespace Unity.Multiplayer.Samples.BossRoom
@@ -13,40 +12,24 @@ public interface ISessionPlayerData
1312
}
1413

1514
/// <summary>
16-
/// This class uses a GUID to bind a player to a session. Once that player connects to a host, the host associates
17-
/// the current ClientID to the player's session GUID. If the player disconnects and reconnects to the same host,
18-
/// the session is preserved.
15+
/// This class uses a unique player ID to bind a player to a session. Once that player connects to a host, the host
16+
/// associates the current ClientID to the player's unique ID. If the player disconnects and reconnects to the same
17+
/// host, the session is preserved.
1918
/// </summary>
2019
/// <remarks>
21-
/// Using a client-generated GUID and sending it directly could be problematic, as a malicious user could intercept
22-
/// it and reuse it to impersonate the original user. We are currently investigating this to offer a solution that
23-
/// handles security better.
20+
/// Using a client-generated player ID and sending it directly could be problematic, as a malicious user could
21+
/// intercept it and reuse it to impersonate the original user. We are currently investigating this to offer a
22+
/// solution that handles security better.
2423
/// </remarks>
2524
/// <typeparam name="T"></typeparam>
2625
public class SessionManager<T> where T : struct, ISessionPlayerData
2726
{
28-
NetworkManager m_NetworkManager;
29-
3027
SessionManager()
3128
{
32-
m_NetworkManager = NetworkManager.Singleton;
33-
if (m_NetworkManager)
34-
{
35-
m_NetworkManager.OnServerStarted += ServerStartedHandler;
36-
}
37-
3829
m_ClientData = new Dictionary<string, T>();
3930
m_ClientIDToPlayerId = new Dictionary<ulong, string>();
4031
}
4132

42-
~SessionManager()
43-
{
44-
if (m_NetworkManager)
45-
{
46-
m_NetworkManager.OnServerStarted -= ServerStartedHandler;
47-
}
48-
}
49-
5033
public static SessionManager<T> Instance => s_Instance ??= new SessionManager<T>();
5134

5235
static SessionManager<T> s_Instance;
@@ -61,46 +44,45 @@ public class SessionManager<T> where T : struct, ISessionPlayerData
6144
/// </summary>
6245
Dictionary<ulong, string> m_ClientIDToPlayerId;
6346

47+
bool m_HasSessionStarted;
48+
6449
/// <summary>
65-
/// Handles the case where NetworkManager has told us a client has disconnected. This includes ourselves, if we're the host,
66-
/// and the server is stopped."
50+
/// Handles client disconnect."
6751
/// </summary>
68-
void OnClientDisconnect(ulong clientId)
52+
public void DisconnectClient(ulong clientId)
6953
{
70-
if (m_ClientIDToPlayerId.TryGetValue(clientId, out var playerId))
54+
if (m_HasSessionStarted)
7155
{
72-
if (GetPlayerData(playerId)?.ClientID == clientId)
56+
// Mark client as disconnected, but keep their data so they can reconnect.
57+
if (m_ClientIDToPlayerId.TryGetValue(clientId, out var playerId))
7358
{
74-
var clientData = m_ClientData[playerId];
75-
clientData.IsConnected = false;
76-
m_ClientData[playerId] = clientData;
59+
if (GetPlayerData(playerId)?.ClientID == clientId)
60+
{
61+
var clientData = m_ClientData[playerId];
62+
clientData.IsConnected = false;
63+
m_ClientData[playerId] = clientData;
64+
}
7765
}
7866
}
79-
80-
if (clientId == m_NetworkManager.LocalClientId)
67+
else
8168
{
82-
//the SessionManager may be initialized again, which will cause its OnNetworkSpawn to be called again.
83-
//Consequently we need to unregister anything we registered, when the NetworkManager is shutting down.
84-
m_NetworkManager.OnClientDisconnectCallback -= OnClientDisconnect;
69+
// Session has not started, no need to keep their data
70+
if (m_ClientIDToPlayerId.TryGetValue(clientId, out var playerId))
71+
{
72+
m_ClientIDToPlayerId.Remove(clientId);
73+
if (GetPlayerData(playerId)?.ClientID == clientId)
74+
{
75+
m_ClientData.Remove(playerId);
76+
}
77+
}
8578
}
8679
}
8780

8881
/// <summary>
89-
/// Handles the flow when a user has requested a disconnect via UI (which can be invoked on the Host, and thus must be
90-
/// handled in server code).
82+
///
9183
/// </summary>
92-
public void OnUserDisconnectRequest()
93-
{
94-
Clear();
95-
}
96-
97-
void Clear()
98-
{
99-
//resets all our runtime state.
100-
m_ClientData.Clear();
101-
m_ClientIDToPlayerId.Clear();
102-
}
103-
84+
/// <param name="playerId">This is the playerId that is unique to this client and persists across multiple logins from the same client</param>
85+
/// <returns>True if a player with this ID is already connected.</returns>
10486
public bool IsDuplicateConnection(string playerId)
10587
{
10688
return m_ClientData.ContainsKey(playerId) && m_ClientData[playerId].IsConnected;
@@ -148,14 +130,19 @@ public void SetupConnectingPlayerSessionData(ulong clientId, string playerId, T
148130
m_ClientData[playerId] = sessionPlayerData;
149131
}
150132

133+
/// <summary>
134+
///
135+
/// </summary>
136+
/// <param name="clientId"> id of the client whose data is requested</param>
137+
/// <returns>The Player ID matching the given client ID</returns>
151138
public string GetPlayerId(ulong clientId)
152139
{
153140
if (m_ClientIDToPlayerId.TryGetValue(clientId, out string playerId))
154141
{
155142
return playerId;
156143
}
157144

158-
Debug.LogError($"No client guid found mapped to the given client ID: {clientId}");
145+
Debug.LogError($"No client player ID found mapped to the given client ID: {clientId}");
159146
return null;
160147
}
161148

@@ -211,20 +198,11 @@ public void SetPlayerData(ulong clientId, T sessionPlayerData)
211198
}
212199

213200
/// <summary>
214-
/// Called after the server is created- This is primarily meant for the host server to clean up or handle/set state as its starting up
201+
/// Marks the current session as started, so from now on we keep the data of disconnected players.
215202
/// </summary>
216-
void ServerStartedHandler()
217-
{
218-
if (m_NetworkManager.IsServer)
219-
{
220-
//O__O if adding any event registrations here, please add an unregistration in OnClientDisconnect.
221-
m_NetworkManager.OnClientDisconnectCallback += OnClientDisconnect;
222-
}
223-
}
224-
225203
public void OnSessionStarted()
226204
{
227-
ClearDisconnectedPlayersData();
205+
m_HasSessionStarted = true;
228206
}
229207

230208
/// <summary>
@@ -233,36 +211,41 @@ public void OnSessionStarted()
233211
public void OnSessionEnded()
234212
{
235213
ClearDisconnectedPlayersData();
236-
List<ulong> connectedClientIds = new List<ulong>(m_NetworkManager.ConnectedClientsIds);
214+
ReinitializePlayersData();
215+
m_HasSessionStarted = false;
216+
}
217+
218+
/// <summary>
219+
/// Resets all our runtime state, so it is ready to be reinitialized when starting a new server
220+
/// </summary>
221+
public void OnServerEnded()
222+
{
223+
m_ClientData.Clear();
224+
m_ClientIDToPlayerId.Clear();
225+
m_HasSessionStarted = false;
226+
}
227+
228+
void ReinitializePlayersData()
229+
{
237230
foreach (var id in m_ClientIDToPlayerId.Keys)
238231
{
239-
if (connectedClientIds.Contains(id))
240-
{
241-
string playerId = m_ClientIDToPlayerId[id];
242-
T sessionPlayerData = m_ClientData[playerId];
243-
sessionPlayerData.Reinitialize();
244-
m_ClientData[playerId] = sessionPlayerData;
245-
}
232+
string playerId = m_ClientIDToPlayerId[id];
233+
T sessionPlayerData = m_ClientData[playerId];
234+
sessionPlayerData.Reinitialize();
235+
m_ClientData[playerId] = sessionPlayerData;
246236
}
247237
}
248238

249239
void ClearDisconnectedPlayersData()
250240
{
251241
List<ulong> idsToClear = new List<ulong>();
252-
List<ulong> connectedClientIds = new List<ulong>(m_NetworkManager.ConnectedClientsIds);
253242
foreach (var id in m_ClientIDToPlayerId.Keys)
254243
{
255-
if (!connectedClientIds.Contains(id))
244+
var data = GetPlayerData(id);
245+
if (data is {IsConnected: false})
256246
{
257247
idsToClear.Add(id);
258248
}
259-
else
260-
{
261-
string playerId = m_ClientIDToPlayerId[id];
262-
T sessionPlayerData = m_ClientData[playerId];
263-
sessionPlayerData.Reinitialize();
264-
m_ClientData[playerId] = sessionPlayerData;
265-
}
266249
}
267250

268251
foreach (var id in idsToClear)

0 commit comments

Comments
 (0)