By default, a connection to a dedicated server is authorised as long as a player has the connection data (eg. IP and port), but this can lead to malicious activity within a game server, or games featuring more players than officially supported. This guide will outline how to use the Matchmaker Service alongside our NetworkManager to add some verification that players attempting to connect are expected within the server, and refuse the connection otherwise. This can also be a useful feature when whitelisting/blacklisting player connections.
This guide will utilise concepts covered in Netcode for Game Objects - Connection Approval, and MatchmakingResults.
Integration Steps
In order to integrate our authorisation logic into the game, we must make edits to the following:
- Server Allocation logic: At the point of a server allocation via the matchmaker, we will fetch our matchmaking results to track all the playerIds from tickets that have been assigned to the server instance
- Add
ConnectionApprovalCallbackhandler: this is where we will validate the player Id is authorised using the above information. - Client connection logic: For playerId validation, we must add the incoming connection PlayerId to the connection data manually
- Our Network Manager Prefab: To make sure that we have Connection Approval enabled
Add Server code to set our authorised players
During allocation, we want to save our list of players that are authorised to connect to this server, in this case, in our Allocate event, we want to save the valid playerIds from the Matchmaking Results.
The following code uses: GetAllocationPayloadFromJsonAsAsync, StoredMatchmakingResults and StoredMatchProperties
using System.Collections.Generic;
private static HashSet<string> _authorizedPlayerIds = new HashSet<string>();
async Task SetAuthorizedPlayers()
{
// fetch our matchmaking results
var matchmakingResults = await MultiplayerService.Instance.GetAllocationPayloadFromJsonAsAsync<MatchmakingResults>();
var matchProperties = matchmakingResults?.MatchProperties;
var players = matchProperties?.Players;
// Set our authorized players
if (players != null)
{
foreach (var player in players)
{
_authorizedPlayerIds.Add(player.Id);
}
}
else
{
Debug.LogWarning("No players");
}
// set our callback to the ConnectionApprovalCallback to use a custom function
if (NetworkManager.Singleton != null)
{
NetworkManager.Singleton.ConnectionApprovalCallback = ApprovalCheck;
}
}Add Approval Check logic
From the above code block, you can see that we set up a ConnectionApprovalCallback, but did not define our ApprovalCheck logic. In our case, we want to validate that an incoming playerId matches those that we stored in our _authorizedPlayerIds HashSet on allocation. This uses the concepts covered in the Connection Approval docs.
private static void ApprovalCheck(NetworkManager.ConnectionApprovalRequest request, NetworkManager.ConnectionApprovalResponse response)
{
// retrieved the playerId from the payload
// IMPORTANT - this needs to be sent by the client in the NetworkConfig.ConnectionData
var playerId = Encoding.UTF8.GetString(request.Payload);
Debug.Log($"Approval Check - Player {playerId}");
var isAuthorized = _authorizedPlayerIds.Contains(playerId);
// when true - player is approved, otherwise they are denied
response.Approved = isAuthorized;
// sets whether the server should spawn a prefab for the connecting client
response.CreatePlayerObject = true;
// The type of player prefab to use, when null it will use the default value
response.PlayerPrefabHash = null;
// The position and rotation of the player when spawned
response.Position = Vector3.zero;
response.Rotation = Quaternion.identity;
// Optional: Reject with a reason
if (!isAuthorized)
{
response.Reason = "Player not found in Matchmaking allocation";
Debug.LogWarning($"Connection Denied - Player {playerId} is not in the allocation list");
}
else
{
Debug.Log($"Connection Approved - Player {playerId} found");
}
// when pending is set to true, allows for custom delays (eg. checking blacklists)
response.Pending = false;
}You can read more about the response fields in these docs.
Add Player ID to Connection Data for clients
Each of our connecting players must send the server their playerId for it to be compared against our array of expected players, this can be done using the following:
using Unity.Netcode;
using Unity.Services.Authentication;
// retrieve our playerId and get bytes value
var playerId = AuthenticationService.Instance.PlayerId;
var data = System.Text.Encoding.ASCII.GetBytes(playerId;
// set our connection data and start client to trigger connection
NetworkManager.Singleton.NetworkConfig.ConnectionData = data;
NetworkManager.Singleton.StartClient();Set ConnectionApproval Flag
The last step in this process is to navigate to your NetworkManager in your scene/the prefab, and ensure that the ConnectionApproval flag has been enabled, failing to do this will result in an error in your server as the NetworkManager is not expecting an Approval flow in the code.
Final Steps
That's it, at this point you should be able to connect via matchmaker and have incoming players' Id be validated by the server. Deploy and Test to make sure that it works as intended. You can also expand the data in your approval flow to incorporate any custom logic that may suit your game.