Hello everyone, for the past week I’ve been trying to setup my game with unity multiplay host and matchmaker, and for the life of me I can’t figure out why I am not able to connect to the server, I’ve tried all of the solutions I could find, maybe I am missing something and the thing is the log file for the server doesn’t always have the same error but ill paste the most common 2. First one is when I was trying to dynamically assign the IP and stuff and I triple checked if my parameters are correct and they were but I tried hardcoding it anyways, and the second log was when I hardcoded it (still couldn’t connect to the server). Anyways if someone can help me here is my relevant code and the log file.
The startup code
using System.Collections.Generic;
#if !UNITY_EDITOR
using System;
#endif
using UnityEngine;
using Unity.Netcode.Transports.UTP;
using UnityEngine.SceneManagement;
using Unity.Services.Matchmaker.Models;
using Unity.Netcode;
using System.Threading.Tasks;
using Unity.Services.Core;
using Unity.Services.Multiplay;
using System;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Unity.Services.Matchmaker;
public class StartUpNetwork : MonoBehaviour
{
public static event System.Action ClientInstance;
private const string InternalIP = "0.0.0.0";
private string _externalServerIP = "0.0.0.0";
private ushort _serverPort = 7777;
private string _externalConnectionString => $"{_externalServerIP}:{_serverPort} ";
private IMultiplayService _multiplayService;
const int _multiplayServiceTimeout = 20000;
private string _allocationsId;
private MultiplayEventCallbacks _serverCallbacks;
private IServerEvents _serverEvents;
private BackfillTicket _localBackFillTicket;
private CreateBackfillTicketOptions _createBackfillTicketOptions;
private const int _ticketCheckMs = 1000;
private MatchmakingResults _matchmakingPayload;
private bool _backfilling = false;
async void Start()
{
bool server = false;
var args = System.Environment.GetCommandLineArgs();
for (int i = 0; i < args.Length; i++)
{
if (args[i] == "-dedicatedServer")
{
server = true;
}
if (args[i] == "-port" && (i + 1) < args.Length)
{
_serverPort = (ushort)int.Parse(args[i + 1]);
}
if (args[i] == "-ip" && (i + 1 < args.Length))
{
_externalServerIP = args[i + 1];
}
}
if (server)
{
StartServer();
await StartServerServices();
}
else
{
ClientInstance?.Invoke();
}
}
private void StartServer()
{
NetworkManager.Singleton.GetComponent<UnityTransport>().SetConnectionData(InternalIP, _serverPort, "0.0.0.0");
NetworkManager.Singleton.StartServer();
NetworkManager.Singleton.OnClientDisconnectCallback += ClientDisconnected;
}
async Task StartServerServices()
{
await UnityServices.InitializeAsync();
try
{
_multiplayService = MultiplayService.Instance;
await _multiplayService.StartServerQueryHandlerAsync((ushort)ConnectionHandler.MaxPlayer, serverName: "n/a", gameType: "n/a", buildId: "0", map: "n/a");
}
catch (Exception ex)
{
Debug.LogWarning("Something Went Wrong trying to set up SQRP services");
}
try
{
_matchmakingPayload = await GetMatchmakerPayload(_multiplayServiceTimeout);
if (_matchmakingPayload != null)
{
Debug.Log($"Got Payload: {_matchmakingPayload}");
await StartBackfill(_matchmakingPayload);
}
else
{
Debug.LogWarning("Getting The Matchmaker Timed out, starting with default");
}
}
catch (Exception ex)
{
Debug.LogWarning("Something Went Wrong trying to set up Allocation and Backfill Services");
}
}
private async Task<MatchmakingResults> GetMatchmakerPayload(int timeout)
{
var mathchmakerPayloadTask = SubscribeAndAwaitMatchmakerAllocation();
if (await Task.WhenAny(mathchmakerPayloadTask, Task.Delay(timeout)) == mathchmakerPayloadTask)
{
return mathchmakerPayloadTask.Result;
}
return null;
}
private async Task<MatchmakingResults> SubscribeAndAwaitMatchmakerAllocation()
{
if (_multiplayService == null) return null;
_allocationsId = null;
_serverCallbacks = new MultiplayEventCallbacks();
_serverCallbacks.Allocate += OnMultiplayAllocation;
_serverEvents = await _multiplayService.SubscribeToServerEventsAsync(_serverCallbacks);
_allocationsId = await AwaitAllocationID();
var mmPayload = await GetMatchmakerAllocationPayloadAsync();
return mmPayload;
}
private void OnMultiplayAllocation(MultiplayAllocation allocation)
{
if (string.IsNullOrEmpty(allocation.AllocationId)) return;
_allocationsId = allocation.AllocationId;
}
private async Task<string> AwaitAllocationID()
{
var config = _multiplayService.ServerConfig;
while (string.IsNullOrEmpty(_allocationsId))
{
var configId = config.AllocationId;
if (!string.IsNullOrEmpty(configId) && string.IsNullOrEmpty(_allocationsId))
{
_allocationsId = configId;
break;
}
await Task.Delay(100);
}
return _allocationsId;
}
private async Task<MatchmakingResults> GetMatchmakerAllocationPayloadAsync()
{
try
{
var payloadAllocation = await MultiplayService.Instance.GetPayloadAllocationFromJsonAs<MatchmakingResults>();
var modelAsJson = JsonConvert.SerializeObject(payloadAllocation, Formatting.Indented);
return payloadAllocation;
}
catch (Exception ex)
{
Debug.LogWarning($"Something Went Wrong trying to get the matchmaker payload in GetMatchmakerAllocationPayloadAsync:\n{ex}");
}
return null;
}
private async Task StartBackfill(MatchmakingResults payload)
{
var backfillProperties = new BackfillTicketProperties(payload.MatchProperties);
_localBackFillTicket = new BackfillTicket { Id = payload.MatchProperties.BackfillTicketId, Properties = backfillProperties };
await BeginBackFilling(payload);
}
private async Task BeginBackFilling(MatchmakingResults payload)
{
if (string.IsNullOrEmpty(_localBackFillTicket.Id))
{
var matchProperties = payload.MatchProperties;
_createBackfillTicketOptions = new CreateBackfillTicketOptions
{
Connection = _externalConnectionString,
QueueName = payload.QueueName,
Properties = new BackfillTicketProperties(matchProperties)
};
_localBackFillTicket.Id = await MatchmakerService.Instance.CreateBackfillTicketAsync(_createBackfillTicketOptions);
}
_backfilling = true;
#pragma warning disable 4014
BackfillLoop();
#pragma warning restore 4014
}
private async Task BackfillLoop()
{
while (_backfilling && NeedPlayers())
{
_localBackFillTicket = await MatchmakerService.Instance.ApproveBackfillTicketAsync(_localBackFillTicket.Id);
if (!NeedPlayers())
{
await MatchmakerService.Instance.DeleteBackfillTicketAsync(_localBackFillTicket.Id);
_localBackFillTicket.Id = null;
_backfilling = false;
return;
}
await Task.Delay(_ticketCheckMs);
}
_backfilling = false;
}
private void ClientDisconnected(ulong clientId)
{
if (!_backfilling && NetworkManager.Singleton.ConnectedClients.Count > 0 && NeedPlayers())
{
BeginBackFilling(_matchmakingPayload);
}
}
private bool NeedPlayers()
{
return NetworkManager.Singleton.ConnectedClients.Count < ConnectionHandler.MaxPlayer;
}
private void Dispose()
{
_serverCallbacks.Allocate -= OnMultiplayAllocation;
_serverEvents?.UnsubscribeAsync();
}
}
Matchmaker code
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Unity.Services.Authentication;
using UnityEngine;
using Unity.Services.Core;
using UnityEngine.Timeline;
using Unity.Services.Matchmaker;
using Unity.Services.Matchmaker.Models;
using StatusOptions = Unity.Services.Matchmaker.Models.MultiplayAssignment.StatusOptions;
using Unity.Netcode;
using Unity.Netcode.Transports.UTP;
#if UNITY_EDITOR
using ParrelSync;
#endif
public class MatchmakerClient : MonoBehaviour
{
private String _ticketId;
private void OnEnable()
{
StartUpNetwork.ClientInstance += SignIn;
}
private void OnDisable()
{
StartUpNetwork.ClientInstance -= SignIn;
}
private async void SignIn()
{
await ClientSignIn(serviceProfileName: "Hall");
await AuthenticationService.Instance.SignInAnonymouslyAsync();
}
private async Task ClientSignIn(string serviceProfileName = null)
{
if (serviceProfileName != null)
{
#if UNITY_EDITOR
serviceProfileName = $"{serviceProfileName}{GetCloneNumberSuffix()}";
#endif
var initOptions = new InitializationOptions() ;
initOptions.SetProfile(serviceProfileName) ;
await UnityServices.InitializeAsync(initOptions);
}
else
{
await UnityServices.InitializeAsync();
}
}
private string PlayerID()
{
return AuthenticationService.Instance.PlayerId;
}
#if UNITY_EDITOR
private string GetCloneNumberSuffix()
{
{
string projectPath = ClonesManager.GetCurrentProjectPath();
int lastUnderscore = projectPath.LastIndexOf("_");
string projectCloneSuffix = projectPath.Substring(lastUnderscore + 1);
if (projectCloneSuffix.Length != 1)
projectCloneSuffix = "";
return projectCloneSuffix;
}
}
#endif
public void StartClient()
{
CreateATicket();
}
private async void CreateATicket()
{
var options = new CreateTicketOptions(queueName: "Hall 1");
var players = new List<Player>
{
new Player(PlayerID(),
new MatchmakingPlayerData
{
Skill = 1
}
)
};
var ticketResponse = await MatchmakerService.Instance.CreateTicketAsync(players, options);
_ticketId = ticketResponse.Id;
Debug.Log($"Ticket ID: {_ticketId}");
PollticketStatus();
}
private async void PollticketStatus()
{
MultiplayAssignment multiplayAssignment = null;
bool gotAssignment = false;
do
{
await Task.Delay(TimeSpan.FromSeconds(1f));
var ticketStatus = await MatchmakerService.Instance.GetTicketAsync(_ticketId);
if (ticketStatus == null) continue;
if (ticketStatus.Type == typeof(MultiplayAssignment))
{
multiplayAssignment = ticketStatus.Value as MultiplayAssignment;
}
switch (multiplayAssignment.Status)
{
case StatusOptions.Found:
gotAssignment = true;
TicketAssigned(multiplayAssignment);
break;
case StatusOptions.InProgress:
break;
case StatusOptions.Failed:
gotAssignment = true;
Debug.LogError($"Failed To Get Ticket Status. Error: {multiplayAssignment.Message}");
break;
case StatusOptions.Timeout:
gotAssignment= true;
Debug.LogError("Failed To Get Ticket Status, Ticket Timed Out");
break;
default:
throw new InvalidOperationException();
}
}
while ( !gotAssignment );
}
private void TicketAssigned(MultiplayAssignment assignment)
{
Debug.Log($"Ticket Assigned: {assignment.Ip}:{assignment.Port}");
NetworkManager.Singleton.GetComponent<UnityTransport>().SetConnectionData( assignment.Ip, (ushort)assignment.Port );
NetworkManager.Singleton.StartClient();
}
[Serializable]
public class MatchmakingPlayerData
{
public int Skill;
}
}
Logfile 1
SQP server: SQP server started on 0.0.0.0:9010 [Wire]: Attempting connection on: ws://127.0.0.1:8086/v1/connection/websocket [Wire]: Websocket connected to : ws://127.0.0.1:8086/v1/connection/websocket. Initiating Wire handshake. Something Went Wrong trying to get the matchmaker payload in GetMatchmakerAllocationPayloadAsync: Unity.Services.Multiplay.Http.HttpException: (404) HTTP/1.1 404 Not Found at Unity.Services.Multiplay.Http.ResponseHandler.HandleAsyncResponse (Unity.Services.Multiplay.Http.HttpClientResponse response, System.Collections.Generic.Dictionary
2[TKey,TValue] statusCodeToTypeMap) [0x0006c] in <f565300f4a4545dfa1e98df5d8a6a525>:0
at Unity.Services.Multiplay.Http.ResponseHandler.HandleAsyncResponse[T] (Unity.Services.Multiplay.Http.HttpClientResponse response, System.Collections.Generic.Dictionary
2[TKey,TValue] statusCodeToTypeMap) [0x00000] in <f565300f4a4545dfa1e98df5d8a6a525>:0
at Unity.Services.Multiplay.Apis.Payload.PayloadApiClient.PayloadAllocationAsync (Unity.Services.Multiplay.Payload.PayloadAllocationRequest request, Unity.Services.Multiplay.Configuration operationConfiguration) [0x0011f] in <f565300f4a4545dfa1e98df5d8a6a525>:0 at Unity.Services.Multiplay.Internal.WrappedMultiplayService.GetPayloadAllocationAsPlainText () [0x00098] in <f565300f4a4545dfa1e98df5d8a6a525>:0 at Unity.Services.Multiplay.Internal.WrappedMultiplayService.GetPayloadAllocationFromJsonAs[TPayload] (System.Boolean throwOnMissingMembers) [0x00065] in <f565300f4a4545dfa1e98df5d8a6a525>:0 at StartUpNetwork.GetMatchmakerAllocationPayloadAsync () [0x00063] in <15f7c7149de847b496af4d01d00007bc>:0 Getting The Matchmaker Timed out, starting with default
Logfile 2
Server started on 127.0.0.1:7777, query port: 9010 SQP server: SQP server started on 0.0.0.0:9010 [Wire]: Attempting connection on: ws://127.0.0.1:8086/v1/connection/websocket [Wire]: Websocket connected to : ws://127.0.0.1:8086/v1/connection/websocket. Initiating Wire handshake. Error getting matchmaker payload in GetMatchmakerAllocationPayloadAsync: (404) HTTP/1.1 404 Not Found Getting the matchmaking payload timed out, starting with default.