Skip to content

Commit 56f6bd8

Browse files
feat: flag to prevent dev and release builds to connect (#482)
* Added flag preventing release and debug build from connecting and appropriate message to the user when it happens * Simplified connection approval flow
1 parent 165cd9b commit 56f6bd8

File tree

5 files changed

+68
-60
lines changed

5 files changed

+68
-60
lines changed

Assets/BossRoom/Scripts/Client/UI/ConnectionStatusMessageUIManager.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,11 @@ void OnConnectStatus(ConnectStatus status)
4242
case ConnectStatus.LoggedInAgain:
4343
PopupPanel.ShowPopupPanel("Connection Failed", "You have logged in elsewhere using the same account.");
4444
break;
45+
case ConnectStatus.IncompatibleBuildType:
46+
PopupPanel.ShowPopupPanel("Connection Failed", "Server and client builds are not compatible. You cannot connect a release build to a development build or an in-editor session.");
47+
break;
4548
case ConnectStatus.GenericDisconnect:
46-
PopupPanel.ShowPopupPanel("Disconnected From Host", "The connection to the host was lost");
49+
PopupPanel.ShowPopupPanel("Disconnected From Host", "The connection to the host was lost.");
4750
break;
4851
default:
4952
Debug.LogWarning($"New ConnectStatus {status} has been added, but no connect message defined for it.");

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,8 @@ void ConnectClient()
222222
{
223223
playerId = m_Portal.GetPlayerId(),
224224
clientScene = SceneManager.GetActiveScene().buildIndex,
225-
playerName = m_Portal.PlayerName
225+
playerName = m_Portal.PlayerName,
226+
isDebug = Debug.isDebugBuild
226227
});
227228

228229
var payloadBytes = System.Text.Encoding.UTF8.GetBytes(payload);

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public enum ConnectStatus
2020
LoggedInAgain, //logged in on a separate client, causing this one to be kicked out.
2121
UserRequestedDisconnect, //Intentional Disconnect triggered by the user.
2222
GenericDisconnect, //server disconnected, but no specific reason given.
23+
IncompatibleBuildType, //client build type is incompatible with server.
2324
}
2425

2526
public enum OnlineMode
@@ -36,6 +37,7 @@ public class ConnectionPayload
3637
public string playerId;
3738
public int clientScene = -1;
3839
public string playerName;
40+
public bool isDebug;
3941
}
4042

4143
/// <summary>

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

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public class ServerGameNetPortal : MonoBehaviour
3131
/// <summary>
3232
/// The active server scene index.
3333
/// </summary>
34-
public int ServerScene { get { return UnityEngine.SceneManagement.SceneManager.GetActiveScene().buildIndex; } }
34+
static int ServerScene => SceneManager.GetActiveScene().buildIndex;
3535

3636
LobbyServiceFacade m_LobbyServiceFacade;
3737

@@ -164,6 +164,8 @@ void ApprovalCheck(byte[] connectionData, ulong clientId, NetworkManager.Connect
164164
{
165165
if (connectionData.Length > k_MaxConnectPayload)
166166
{
167+
// If connectionData too high, deny immediately to avoid wasting time on the server. This is intended as
168+
// a bit of light protection against DOS attacks that rely on sending silly big buffers of garbage.
167169
connectionApprovedCallback(false, 0, false, null, null);
168170
return;
169171
}
@@ -178,67 +180,66 @@ void ApprovalCheck(byte[] connectionData, ulong clientId, NetworkManager.Connect
178180
return;
179181
}
180182

181-
string payload = System.Text.Encoding.UTF8.GetString(connectionData);
183+
var payload = System.Text.Encoding.UTF8.GetString(connectionData);
182184
var connectionPayload = JsonUtility.FromJson<ConnectionPayload>(payload); // https://docs.unity3d.com/2020.2/Documentation/Manual/JSONSerialization.html
183-
184-
ConnectStatus gameReturnStatus;
185-
186-
// Test for over-capacity connection. This needs to be done asap, to make sure we refuse connections asap and don't spend useless time server side
187-
// on invalid users trying to connect
188-
// todo this is currently still spending too much time server side.
189-
if (m_Portal.NetManager.ConnectedClientsIds.Count >= CharSelectData.k_MaxLobbyPlayers)
190-
{
191-
gameReturnStatus = ConnectStatus.ServerFull;
192-
}
193-
else
194-
{
195-
Debug.Log("Host ApprovalCheck: connecting client with player ID: " + connectionPayload.playerId);
196-
197-
gameReturnStatus = SessionManager<SessionPlayerData>.Instance.SetupConnectingPlayerSessionData(clientId, connectionPayload.playerId,
198-
new SessionPlayerData(clientId, connectionPayload.playerName, m_Portal.AvatarRegistry.GetRandomAvatar().Guid.ToNetworkGuid(), 0, true))
199-
? ConnectStatus.Success
200-
: ConnectStatus.LoggedInAgain;
201-
}
185+
var gameReturnStatus = GetConnectStatus(connectionPayload);
202186

203187
if (gameReturnStatus == ConnectStatus.Success)
204188
{
205-
int clientScene = connectionPayload.clientScene;
189+
SessionManager<SessionPlayerData>.Instance.SetupConnectingPlayerSessionData(clientId, connectionPayload.playerId,
190+
new SessionPlayerData(clientId, connectionPayload.playerName, m_Portal.AvatarRegistry.GetRandomAvatar().Guid.ToNetworkGuid(), 0, true));
206191
SendServerToClientConnectResult(clientId, gameReturnStatus);
207192

208-
//Populate our dictionaries with the playerData
209-
m_ClientSceneMap[clientId] = clientScene;
193+
//Populate our client scene map
194+
m_ClientSceneMap[clientId] = connectionPayload.clientScene;
210195

211196
connectionApprovedCallback(true, null, true, Vector3.zero, Quaternion.identity);
212197
// connection approval will create a player object for you
213198
}
214199
else
215200
{
216201
//TODO-FIXME:Netcode Issue #796. We should be able to send a reason and disconnect without a coroutine delay.
217-
//TODO:Netcode: In the future we expect Netcode to allow us to return more information as part of
218-
//the approval callback, so that we can provide more context on a reject. In the meantime we must provide the extra information ourselves,
219-
//and then manually close down the connection.
202+
//TODO:Netcode: In the future we expect Netcode to allow us to return more information as part of the
203+
//approval callback, so that we can provide more context on a reject. In the meantime we must provide
204+
//the extra information ourselves, and then wait a short time before manually close down the connection.
220205
SendServerToClientConnectResult(clientId, gameReturnStatus);
221206
SendServerToClientSetDisconnectReason(clientId, gameReturnStatus);
222-
StartCoroutine(WaitToDisconnect(clientId));
207+
StartCoroutine(WaitToDenyApproval(connectionApprovedCallback));
223208
if (m_LobbyServiceFacade.CurrentUnityLobby != null)
224209
{
225210
m_LobbyServiceFacade.RemovePlayerFromLobbyAsync(connectionPayload.playerId, m_LobbyServiceFacade.CurrentUnityLobby.Id, null, null);
226211
}
227212
}
228213
}
229214

230-
IEnumerator WaitToDisconnect(ulong clientId)
215+
ConnectStatus GetConnectStatus(ConnectionPayload connectionPayload)
216+
{
217+
if (m_Portal.NetManager.ConnectedClientsIds.Count >= CharSelectData.k_MaxLobbyPlayers)
218+
{
219+
return ConnectStatus.ServerFull;
220+
}
221+
222+
if (connectionPayload.isDebug != Debug.isDebugBuild)
223+
{
224+
return ConnectStatus.IncompatibleBuildType;
225+
}
226+
227+
return SessionManager<SessionPlayerData>.Instance.IsDuplicateConnection(connectionPayload.playerId) ?
228+
ConnectStatus.LoggedInAgain : ConnectStatus.Success;
229+
}
230+
231+
static IEnumerator WaitToDenyApproval(NetworkManager.ConnectionApprovedDelegate connectionApprovedCallback)
231232
{
232233
yield return new WaitForSeconds(0.5f);
233-
m_Portal.NetManager.DisconnectClient(clientId);
234+
connectionApprovedCallback(false, 0, false, null, null);
234235
}
235236

236237
/// <summary>
237238
/// Sends a DisconnectReason to the indicated client. This should only be done on the server, prior to disconnecting the client.
238239
/// </summary>
239240
/// <param name="clientID"> id of the client to send to </param>
240241
/// <param name="status"> The reason for the upcoming disconnect.</param>
241-
public void SendServerToClientSetDisconnectReason(ulong clientID, ConnectStatus status)
242+
static void SendServerToClientSetDisconnectReason(ulong clientID, ConnectStatus status)
242243
{
243244
var writer = new FastBufferWriter(sizeof(ConnectStatus), Allocator.Temp);
244245
writer.WriteValueSafe(status);
@@ -250,7 +251,7 @@ public void SendServerToClientSetDisconnectReason(ulong clientID, ConnectStatus
250251
/// </summary>
251252
/// <param name="clientID"> id of the client to send to </param>
252253
/// <param name="status"> the status to pass to the client</param>
253-
public void SendServerToClientConnectResult(ulong clientID, ConnectStatus status)
254+
static void SendServerToClientConnectResult(ulong clientID, ConnectStatus status)
254255
{
255256
var writer = new FastBufferWriter(sizeof(ConnectStatus), Allocator.Temp);
256257
writer.WriteValueSafe(status);

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

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -101,50 +101,51 @@ void Clear()
101101
m_ClientIDToPlayerId.Clear();
102102
}
103103

104+
public bool IsDuplicateConnection(string playerId)
105+
{
106+
return m_ClientData.ContainsKey(playerId) && m_ClientData[playerId].IsConnected;
107+
}
108+
104109
/// <summary>
105-
/// Adds a connecting player's session data if it is a new connection, or updates their session data in case of a reconnection. If the connection is not valid, simply returns false.
110+
/// Adds a connecting player's session data if it is a new connection, or updates their session data in case of a reconnection.
106111
/// </summary>
107112
/// <param name="clientId">This is the clientId that Netcode assigned us on login. It does not persist across multiple logins from the same client. </param>
108113
/// <param name="playerId">This is the playerId that is unique to this client and persists across multiple logins from the same client</param>
109114
/// <param name="sessionPlayerData">The player's initial data</param>
110-
/// <returns>True if the player connection is valid (i.e. not a duplicate connection)</returns>
111-
public bool SetupConnectingPlayerSessionData(ulong clientId, string playerId, T sessionPlayerData)
115+
public void SetupConnectingPlayerSessionData(ulong clientId, string playerId, T sessionPlayerData)
112116
{
113-
bool success = true;
117+
var isReconnecting = false;
114118

115-
//Test for Duplicate Login.
116-
if (m_ClientData.ContainsKey(playerId))
119+
// Test for duplicate connection
120+
if (IsDuplicateConnection(playerId))
117121
{
118-
bool isReconnecting = false;
122+
Debug.LogError($"Player ID {playerId} already exists. This is a duplicate connection. Rejecting this session data.");
123+
return;
124+
}
119125

120-
// If another client is connected with the same playerId
121-
if (m_ClientData[playerId].IsConnected)
122-
{
123-
success = false;
124-
}
125-
else
126+
// If another client exists with the same playerId
127+
if (m_ClientData.ContainsKey(playerId))
128+
{
129+
if (!m_ClientData[playerId].IsConnected)
126130
{
131+
// If this connecting client has the same player Id as a disconnected client, this is a reconnection.
127132
isReconnecting = true;
128133
}
129134

130-
// Reconnecting. Give data from old player to new player
131-
if (isReconnecting)
132-
{
133-
// Update player session data
134-
sessionPlayerData = m_ClientData[playerId];
135-
sessionPlayerData.ClientID = clientId;
136-
sessionPlayerData.IsConnected = true;
137-
}
138135
}
139136

140-
//Populate our dictionaries with the SessionPlayerData
141-
if (success)
137+
// Reconnecting. Give data from old player to new player
138+
if (isReconnecting)
142139
{
143-
m_ClientIDToPlayerId[clientId] = playerId;
144-
m_ClientData[playerId] = sessionPlayerData;
140+
// Update player session data
141+
sessionPlayerData = m_ClientData[playerId];
142+
sessionPlayerData.ClientID = clientId;
143+
sessionPlayerData.IsConnected = true;
145144
}
146145

147-
return success;
146+
//Populate our dictionaries with the SessionPlayerData
147+
m_ClientIDToPlayerId[clientId] = playerId;
148+
m_ClientData[playerId] = sessionPlayerData;
148149
}
149150

150151
public string GetPlayerId(ulong clientId)

0 commit comments

Comments
 (0)