1
1
using System ;
2
2
using System . Collections . Generic ;
3
- using Unity . Netcode ;
4
3
using UnityEngine ;
5
4
6
5
namespace Unity . Multiplayer . Samples . BossRoom
@@ -13,40 +12,24 @@ public interface ISessionPlayerData
13
12
}
14
13
15
14
/// <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.
19
18
/// </summary>
20
19
/// <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.
24
23
/// </remarks>
25
24
/// <typeparam name="T"></typeparam>
26
25
public class SessionManager < T > where T : struct , ISessionPlayerData
27
26
{
28
- NetworkManager m_NetworkManager ;
29
-
30
27
SessionManager ( )
31
28
{
32
- m_NetworkManager = NetworkManager . Singleton ;
33
- if ( m_NetworkManager )
34
- {
35
- m_NetworkManager . OnServerStarted += ServerStartedHandler ;
36
- }
37
-
38
29
m_ClientData = new Dictionary < string , T > ( ) ;
39
30
m_ClientIDToPlayerId = new Dictionary < ulong , string > ( ) ;
40
31
}
41
32
42
- ~ SessionManager ( )
43
- {
44
- if ( m_NetworkManager )
45
- {
46
- m_NetworkManager . OnServerStarted -= ServerStartedHandler ;
47
- }
48
- }
49
-
50
33
public static SessionManager < T > Instance => s_Instance ??= new SessionManager < T > ( ) ;
51
34
52
35
static SessionManager < T > s_Instance ;
@@ -61,46 +44,45 @@ public class SessionManager<T> where T : struct, ISessionPlayerData
61
44
/// </summary>
62
45
Dictionary < ulong , string > m_ClientIDToPlayerId ;
63
46
47
+ bool m_HasSessionStarted ;
48
+
64
49
/// <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."
67
51
/// </summary>
68
- void OnClientDisconnect ( ulong clientId )
52
+ public void DisconnectClient ( ulong clientId )
69
53
{
70
- if ( m_ClientIDToPlayerId . TryGetValue ( clientId , out var playerId ) )
54
+ if ( m_HasSessionStarted )
71
55
{
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 ) )
73
58
{
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
+ }
77
65
}
78
66
}
79
-
80
- if ( clientId == m_NetworkManager . LocalClientId )
67
+ else
81
68
{
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
+ }
85
78
}
86
79
}
87
80
88
81
/// <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
+ ///
91
83
/// </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>
104
86
public bool IsDuplicateConnection ( string playerId )
105
87
{
106
88
return m_ClientData . ContainsKey ( playerId ) && m_ClientData [ playerId ] . IsConnected ;
@@ -148,14 +130,19 @@ public void SetupConnectingPlayerSessionData(ulong clientId, string playerId, T
148
130
m_ClientData [ playerId ] = sessionPlayerData ;
149
131
}
150
132
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>
151
138
public string GetPlayerId ( ulong clientId )
152
139
{
153
140
if ( m_ClientIDToPlayerId . TryGetValue ( clientId , out string playerId ) )
154
141
{
155
142
return playerId ;
156
143
}
157
144
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 } ") ;
159
146
return null ;
160
147
}
161
148
@@ -211,20 +198,11 @@ public void SetPlayerData(ulong clientId, T sessionPlayerData)
211
198
}
212
199
213
200
/// <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.
215
202
/// </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
-
225
203
public void OnSessionStarted ( )
226
204
{
227
- ClearDisconnectedPlayersData ( ) ;
205
+ m_HasSessionStarted = true ;
228
206
}
229
207
230
208
/// <summary>
@@ -233,36 +211,41 @@ public void OnSessionStarted()
233
211
public void OnSessionEnded ( )
234
212
{
235
213
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
+ {
237
230
foreach ( var id in m_ClientIDToPlayerId . Keys )
238
231
{
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 ;
246
236
}
247
237
}
248
238
249
239
void ClearDisconnectedPlayersData ( )
250
240
{
251
241
List < ulong > idsToClear = new List < ulong > ( ) ;
252
- List < ulong > connectedClientIds = new List < ulong > ( m_NetworkManager . ConnectedClientsIds ) ;
253
242
foreach ( var id in m_ClientIDToPlayerId . Keys )
254
243
{
255
- if ( ! connectedClientIds . Contains ( id ) )
244
+ var data = GetPlayerData ( id ) ;
245
+ if ( data is { IsConnected : false } )
256
246
{
257
247
idsToClear . Add ( id ) ;
258
248
}
259
- else
260
- {
261
- string playerId = m_ClientIDToPlayerId [ id ] ;
262
- T sessionPlayerData = m_ClientData [ playerId ] ;
263
- sessionPlayerData . Reinitialize ( ) ;
264
- m_ClientData [ playerId ] = sessionPlayerData ;
265
- }
266
249
}
267
250
268
251
foreach ( var id in idsToClear )
0 commit comments