Skip to content

Commit 55c1e62

Browse files
authored
refactor!: connection approval (#2002)
1 parent fd816c5 commit 55c1e62

File tree

5 files changed

+83
-20
lines changed

5 files changed

+83
-20
lines changed

com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ internal static string PrefabDebugHelper(NetworkPrefab networkPrefab)
6060

6161
private NetworkPrefabHandler m_PrefabHandler;
6262

63+
internal Dictionary<ulong, ConnectionApprovalResponse> ClientsToApprove = new Dictionary<ulong, ConnectionApprovalResponse>();
64+
6365
public NetworkPrefabHandler PrefabHandler
6466
{
6567
get
@@ -367,13 +369,15 @@ public IReadOnlyList<ulong> ConnectedClientsIds
367369
/// <param name="PlayerPrefabHash">The prefabHash to use for the client. If createPlayerObject is false, this is ignored. If playerPrefabHash is null, the default player prefab is used.</param>
368370
/// <param name="Position">The position to spawn the client at. If null, the prefab position is used.</param>
369371
/// <param name="Rotation">The rotation to spawn the client with. If null, the prefab position is used.</param>
370-
public struct ConnectionApprovalResponse
372+
/// <param name="Pending">If the Approval decision cannot be made immediately, the client code can set Pending to true, keep a reference to the ConnectionApprovalResponse object and write to it later. Client code must exercise care to setting all the members to the value it wants before marking Pending to false, to indicate completion. If the field is set as Pending = true, we'll monitor the object until it gets set to not pending anymore and use the parameters then.</param>
373+
public class ConnectionApprovalResponse
371374
{
372375
public bool Approved;
373376
public bool CreatePlayerObject;
374377
public uint? PlayerPrefabHash;
375378
public Vector3? Position;
376379
public Quaternion? Rotation;
380+
public bool Pending;
377381
}
378382

379383
/// <summary>
@@ -390,7 +394,7 @@ public struct ConnectionApprovalRequest
390394
/// <summary>
391395
/// The callback to invoke during connection approval. Allows client code to decide whether or not to allow incoming client connection
392396
/// </summary>
393-
public Func<ConnectionApprovalRequest, ConnectionApprovalResponse> ConnectionApprovalCallback
397+
public Action<ConnectionApprovalRequest, ConnectionApprovalResponse> ConnectionApprovalCallback
394398
{
395399
get => m_ConnectionApprovalCallback;
396400
set
@@ -406,7 +410,7 @@ public Func<ConnectionApprovalRequest, ConnectionApprovalResponse> ConnectionApp
406410
}
407411
}
408412

409-
private Func<ConnectionApprovalRequest, ConnectionApprovalResponse> m_ConnectionApprovalCallback;
413+
private Action<ConnectionApprovalRequest, ConnectionApprovalResponse> m_ConnectionApprovalCallback;
410414

411415
/// <summary>
412416
/// The current NetworkConfig
@@ -941,6 +945,7 @@ private void ClearClients()
941945
m_ConnectedClientIds.Clear();
942946
LocalClient = null;
943947
NetworkObject.OrphanChildren.Clear();
948+
ClientsToApprove.Clear();
944949
}
945950

946951
/// <summary>
@@ -1048,7 +1053,8 @@ public bool StartHost()
10481053

10491054
if (NetworkConfig.ConnectionApproval && ConnectionApprovalCallback != null)
10501055
{
1051-
var response = ConnectionApprovalCallback(new ConnectionApprovalRequest { Payload = NetworkConfig.ConnectionData, ClientNetworkId = ServerClientId });
1056+
var response = new ConnectionApprovalResponse();
1057+
ConnectionApprovalCallback(new ConnectionApprovalRequest { Payload = NetworkConfig.ConnectionData, ClientNetworkId = ServerClientId }, response);
10521058
if (!response.Approved)
10531059
{
10541060
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
@@ -1403,13 +1409,52 @@ public void NetworkUpdate(NetworkUpdateStage updateStage)
14031409
}
14041410
}
14051411

1412+
private void ProcessPendingApprovals()
1413+
{
1414+
List<ulong> senders = null;
1415+
1416+
foreach (var responsePair in ClientsToApprove)
1417+
{
1418+
var response = responsePair.Value;
1419+
var senderId = responsePair.Key;
1420+
1421+
if (!response.Pending)
1422+
{
1423+
try
1424+
{
1425+
HandleConnectionApproval(senderId, response);
1426+
1427+
if (senders == null)
1428+
{
1429+
senders = new List<ulong>();
1430+
}
1431+
senders.Add(senderId);
1432+
}
1433+
catch (Exception e)
1434+
{
1435+
Debug.LogException(e);
1436+
}
1437+
}
1438+
}
1439+
1440+
if (senders != null)
1441+
{
1442+
foreach (var sender in senders)
1443+
{
1444+
ClientsToApprove.Remove(sender);
1445+
}
1446+
}
1447+
}
1448+
14061449
private void OnNetworkEarlyUpdate()
14071450
{
14081451
if (!IsListening)
14091452
{
14101453
return;
14111454
}
14121455

1456+
ProcessPendingApprovals();
1457+
14131458
#if DEVELOPMENT_BUILD || UNITY_EDITOR
14141459
s_TransportPoll.Begin();
14151460
#endif

com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionRequestMessage.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,15 @@ public void Handle(ref NetworkContext context)
101101
{
102102
// Note: Delegate creation allocates.
103103
// Note: ToArray() also allocates. :(
104-
var response = networkManager.ConnectionApprovalCallback(
104+
var response = new NetworkManager.ConnectionApprovalResponse();
105+
networkManager.ClientsToApprove[senderId] = response;
106+
107+
networkManager.ConnectionApprovalCallback(
105108
new NetworkManager.ConnectionApprovalRequest
106109
{
107110
Payload = ConnectionData,
108111
ClientNetworkId = senderId
109-
});
110-
networkManager.HandleConnectionApproval(senderId, response);
112+
}, response);
111113
}
112114
else
113115
{

com.unity.netcode.gameobjects/Tests/Runtime/ConnectionApproval.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,8 @@ public IEnumerator ConnectionApproval()
4747
Assert.True(m_IsValidated);
4848
}
4949

50-
private NetworkManager.ConnectionApprovalResponse NetworkManagerObject_ConnectionApprovalCallback(NetworkManager.ConnectionApprovalRequest request)
50+
private void NetworkManagerObject_ConnectionApprovalCallback(NetworkManager.ConnectionApprovalRequest request, NetworkManager.ConnectionApprovalResponse response)
5151
{
52-
var response = new NetworkManager.ConnectionApprovalResponse();
5352
var stringGuid = Encoding.UTF8.GetString(request.Payload);
5453
if (m_ValidationToken.ToString() == stringGuid)
5554
{
@@ -61,8 +60,6 @@ private NetworkManager.ConnectionApprovalResponse NetworkManagerObject_Connectio
6160
response.Position = null;
6261
response.Rotation = null;
6362
response.PlayerPrefabHash = null;
64-
65-
return response;
6663
}
6764

6865
[TearDown]

testproject/Assets/Tests/Manual/Scripts/ConnectionApprovalComponent.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,8 @@ public void OnDisconnectClient()
153153
/// </summary>
154154
/// <param name="request">The connection approval request</param>
155155
/// <returns>ConnectionApprovalResult with the approval decision, with parameters</returns>
156-
private NetworkManager.ConnectionApprovalResponse ConnectionApprovalCallback(NetworkManager.ConnectionApprovalRequest request)
156+
private void ConnectionApprovalCallback(NetworkManager.ConnectionApprovalRequest request, NetworkManager.ConnectionApprovalResponse response)
157157
{
158-
var response = new NetworkManager.ConnectionApprovalResponse();
159158
string approvalToken = Encoding.ASCII.GetString(request.Payload);
160159
var isTokenValid = approvalToken == m_ApprovalToken;
161160
if (m_SimulateFailure && m_SimulateFailure.isOn && IsServer && request.ClientNetworkId != NetworkManager.LocalClientId)
@@ -193,8 +192,6 @@ private NetworkManager.ConnectionApprovalResponse ConnectionApprovalCallback(Net
193192

194193
m_ConnectionMessageToDisplay.gameObject.SetActive(true);
195194
}
196-
197-
return response;
198195
}
199196

200197
/// <summary>

testproject/Assets/Tests/Runtime/MultiClientConnectionApproval.cs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,18 @@ public class MultiClientConnectionApproval
1919

2020
private GameObject m_PlayerPrefab;
2121
private GameObject m_PlayerPrefabOverride;
22+
private bool m_DelayedApproval;
23+
private List<NetworkManager.ConnectionApprovalResponse> m_ResponseToSet = new List<NetworkManager.ConnectionApprovalResponse>();
2224

2325
/// <summary>
2426
/// Tests connection approval and connection approval failure
2527
/// </summary>
2628
/// <returns></returns>
2729
[UnityTest]
28-
public IEnumerator ConnectionApproval()
30+
public IEnumerator ConnectionApproval([Values(true, false)] bool delayedApproval)
2931
{
3032
m_ConnectionToken = "ThisIsTheRightPassword";
33+
m_DelayedApproval = delayedApproval;
3134
return ConnectionApprovalHandler(3, 1);
3235
}
3336

@@ -130,6 +133,22 @@ private IEnumerator ConnectionApprovalHandler(int numClients, int failureTestCou
130133
Assert.Fail("Failed to start instances");
131134
}
132135

136+
if (m_DelayedApproval)
137+
{
138+
// This is necessary so that clients gets the time to attempt connecting and fill the pending approval responses
139+
var nextFrameNumber = Time.frameCount + 10;
140+
yield return new WaitUntil(() => Time.frameCount >= nextFrameNumber);
141+
142+
foreach (var response in m_ResponseToSet)
143+
{
144+
// perform delayed approval
145+
// The response class has already been filled, when created in ConnectionApprovalCallback()
146+
yield return new WaitForSeconds(0.2f);
147+
response.Pending = false;
148+
}
149+
m_ResponseToSet.Clear();
150+
}
151+
133152
// [Client-Side] Wait for a connection to the server
134153
yield return NetcodeIntegrationTestHelpers.WaitForClientsConnected(clientsAdjustedList.ToArray(), null, 512);
135154

@@ -173,12 +192,17 @@ private IEnumerator ConnectionApprovalHandler(int numClients, int failureTestCou
173192
/// <param name="connectionData">the NetworkConfig.ConnectionData sent from the client being approved</param>
174193
/// <param name="clientId">the client id being approved</param>
175194
/// <param name="callback">the callback invoked to handle approval</param>
176-
private NetworkManager.ConnectionApprovalResponse ConnectionApprovalCallback(NetworkManager.ConnectionApprovalRequest request)
195+
private void ConnectionApprovalCallback(NetworkManager.ConnectionApprovalRequest request, NetworkManager.ConnectionApprovalResponse response)
177196
{
178-
var response = new NetworkManager.ConnectionApprovalResponse();
179197
string approvalToken = Encoding.ASCII.GetString(request.Payload);
180198
var isApproved = approvalToken == m_ConnectionToken;
181199

200+
if (m_DelayedApproval)
201+
{
202+
response.Pending = true;
203+
m_ResponseToSet.Add(response);
204+
}
205+
182206
if (isApproved)
183207
{
184208
m_SuccessfulConnections++;
@@ -204,8 +228,6 @@ private NetworkManager.ConnectionApprovalResponse ConnectionApprovalCallback(Net
204228
response.Rotation = null;
205229
response.PlayerPrefabHash = m_PrefabOverrideGlobalObjectIdHash;
206230
}
207-
208-
return response;
209231
}
210232

211233

0 commit comments

Comments
 (0)