Thank you for your time. I created a minimum example, to be used as component of a Netcode for Game Objects’ NetworkManager,of how I am using relay+lobby. In this example host migration does not happen.
There is also a strange, in my opinion, behavior. Alice sets the allocationId (line 109) when the host creates the lobby and relay (CreateMyRelayAndMyLobby). But when Bob quick joins Alice’s lobby (TryToJoinExistingLobbyOrCreateNewIfNoneFound) Alice’s allocationId is nowhere to be found (it didn’t appear on the logs) and the host migration fails when the button that calls OnMigrateClick is clicked.
From what I understood the allocationId is the link between the Relay and Lobby and if it isn’t there the lobby won’t play along with the relay. Is this correct?
What am I doing wrong?
I don’t know it it matters but I am doing the tests using two editor instances on the same machine, cloned using ParrelSync.
Relay is @ 1.0.1-pre.6,
Unity Transport for Netcode for Game Objects @ 1.0.0-pre.6
Netcode For Game Objects @ 1.0.0-pre.6
Lobby @ 1.0.0-pre.6
Authentication 1.0.0-pre.37
Operating System is Win10, Unity 2020.3.29f1.1603 Personal
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using Unity.Services.Authentication;
using Unity.Services.Core;
using Unity.Services.Lobbies;
using Unity.Services.Lobbies.Models;
using UnityEngine;
using System.Linq;
using Unity.Services.Relay;
using Unity.Services.Relay.Models;
using Unity.Netcode;
using System;
public class HostMigrationTest : MonoBehaviour
{
/// <summary>
/// It's value comes from either TryToQuickJoin or CreateMyRelayAndMyLobby
/// </summary>
private Lobby lobby;
/// <summary>
/// It's value comes from either CreateRelayAllocation
/// </summary>
private Allocation allocation;
/// <summary>
/// Comes from TryToJoinExistingLobbyOrCreateNewIfNoneFound
/// </summary>
private JoinAllocation joinAllocation;
private string joinCode;
public async void Start()
{
Debug.Log("??");
await InitializeServicesAndSignInAsync();
await TryToJoinExistingLobbyOrCreateNewIfNoneFound();
}
/// <summary>
/// Is there a lobby that I can join using quick join? If there is such a lobby
/// then join it, get it's allocation and join the relay. If not create the relay
/// and create the lobby.
/// </summary>
/// <returns></returns>
private async Task TryToJoinExistingLobbyOrCreateNewIfNoneFound()
{
try
{
await TryToQuickJoin();
Debug.Log("If I arrived here I found an open lobby.");
///Maybe there is a issue here: when Alice creates a Lobby it sets an allocationId on herself.
///But when Bob gets the lobby created by Alice via quick join Alice's allocationId is gone.
Debug.Log($"id:{lobby.Id}, hostId:{lobby.HostId}, numberOfPlayers:{lobby.Players.Count}, " +
$"allocationId:{lobby.Players.Where(p=>p.Id==lobby.HostId).First().AllocationId}");
var connectionInfo = lobby.Players.Where(p => p.Id == lobby.HostId).First().ConnectionInfo;
joinAllocation = await Relay.Instance.JoinAllocationAsync(connectionInfo);
}
catch (LobbyServiceException e)
{
if (e.Reason == LobbyExceptionReason.NoOpenLobbies)
{
Debug.Log("no lobby found, creating my own lobby");
await CreateMyRelayAndMyLobby();
}
else
{
throw e;
}
}
}
private async Task InitializeServicesAndSignInAsync()
{
try
{
await UnityServices.InitializeAsync();
await AuthenticationService.Instance.SignInAnonymouslyAsync();
Debug.Log("init done");
}
catch(Exception ex)
{
Debug.LogError(ex);
}
}
/// <summary>
/// Join an existing lobby. ***IS THIS CORRECT?***
/// </summary>
/// <returns></returns>
private async Task TryToQuickJoin()
{
Debug.Log("Looking for existing lobby");
QuickJoinLobbyOptions options = new QuickJoinLobbyOptions();
options.Filter = new List<QueryFilter>()
{
new QueryFilter(
field: QueryFilter.FieldOptions.MaxPlayers,
op: QueryFilter.OpOptions.GE,
value: "2"
)
};
lobby = await Lobbies.Instance.QuickJoinLobbyAsync();
}
/// <summary>
/// Create the allocation and then the lobby using the allocation data.
/// </summary>
/// <returns></returns>
private async Task CreateMyRelayAndMyLobby()
{
await CreateRelayAllocation();
CreateLobbyOptions o = new CreateLobbyOptions();
o.IsPrivate = false;
o.Player = new Player(
allocationId: allocation.AllocationId.ToString(),//****Alice's allocationId: is this correct ?****
connectionInfo: joinCode,//****is this correct ?****
id: AuthenticationService.Instance.PlayerId
);
lobby = await Lobbies.Instance.CreateLobbyAsync("foobar", 2, o);
Debug.Log($"Created my lobby:{lobby.Id}, host:{lobby.HostId}, number of" +
$"players:{lobby.Players.Count}");
}
/// <summary>
/// Creates the allocation, the values that matter go to the allocation and joinCode
/// fields.
/// </summary>
/// <returns></returns>
private async Task CreateRelayAllocation()
{
allocation = await Relay.Instance.CreateAllocationAsync(2);
joinCode = await Relay.Instance.GetJoinCodeAsync(allocation.AllocationId);
Debug.Log($"Created allocation:{allocation.AllocationId}");
}
private bool createdHost = false;
private bool createdClient = false;
public void Update()
{
var nm = NetworkManager.Singleton;
if (allocation!=null && createdHost == false)
{
createdHost = true;
nm.StartHost();
}
if(joinAllocation!=null && createdClient == false)
{
createdClient = true;
nm.StartClient();
}
}
public void OnMigrateClick()
{
Debug.Log("clicou");
doMigrate();
}
private async void doMigrate()
{
var updatedLobby = await Lobbies.Instance.GetLobbyAsync(lobby.Id);
var nm = NetworkManager.Singleton;
if (nm.IsHost)
{
Debug.Log("I am the host, have to migrate host to someone else");
Lobby shouldHaveDifferentHost = await Lobbies.Instance.UpdateLobbyAsync(updatedLobby.Id, new UpdateLobbyOptions()
{
HostId = updatedLobby.Players.Where(p => p.Id != updatedLobby.HostId).First().Id
});
Debug.Log($"Has the host id changed? {shouldHaveDifferentHost.HostId != updatedLobby.HostId}");
await Lobbies.Instance.RemovePlayerAsync(updatedLobby.Id, updatedLobby.HostId);
await Task.Delay(5000);//Waiting because host migration is not instantaneous
Debug.Log("Waited for migration, time do disconnect from the game");
nm.DisconnectClient(nm.LocalClientId);
nm.Shutdown(true);
Debug.Log("I am dead. Did the host migrate?");
}
}
}