Best Way To Link Lobby With Game Server Hosting

How can I seamlessly connect the lobby system to the game server hosting in Unity using C# to ensure a smooth transition for players between the lobby and the actual game sessions?

this is my lobby script

using System;
using System.Collections.Generic;
using Unity.Services.Authentication;
using Unity.Services.Core;
using Unity.Services.Lobbies;
using Unity.Services.Lobbies.Models;
using UnityEngine;

public class LobbyManager : MonoBehaviour {


    public static LobbyManager Instance { get; private set; }


    public const string KEY_PLAYER_NAME = "PlayerName";
    public const string KEY_PLAYER_LEVEL = "PlayerLevel";
    public const string KEY_GAME_MIN_LEVEL = "Min Level";
    public const string KEY_Profile_Pic = "PhotoUrl";


    public event EventHandler OnLeftLobby;

    public event EventHandler<LobbyEventArgs> OnCreateLobbyStarted;
    public event EventHandler<LobbyEventArgs> OnCreateLobbyFailed;

    public event EventHandler<LobbyEventArgs> OnJoinedLobby;
    public event EventHandler<LobbyEventArgs> OnJoinedLobbyUpdate;
    public event EventHandler<LobbyEventArgs> OnKickedFromLobby;

    public class LobbyEventArgs : EventArgs {
        public Lobby lobby;
    }

    public event EventHandler<OnLobbyListChangedEventArgs> OnLobbyListChanged;
    public class OnLobbyListChangedEventArgs : EventArgs {
        public List<Lobby> lobbyList;
    }


    public enum Min_Level
    {
        D,
        C,
        B,
        A,
        S,
        G,
        X,
        OMG
    }
    public enum PlayerLevel
    {
        D,
        C,
        B,
        A,
        S,
        G,
        X,
        OMG
    }

    private float heartbeatTimer;
    private float lobbyPollTimer;
    private float refreshLobbyListTimer = 5f;
    private Lobby joinedLobby;
    private string playerName;
    private string playerLevel;
    private string playerAvterURL;

    private void Awake()
    {
        Instance = this;

    }


    private async void InitializeUnityAuthentication()
    {
        if (UnityServices.State != ServicesInitializationState.Initialized)
        {
            InitializationOptions initializationOptions = new InitializationOptions();

            try
            {
                await UnityServices.InitializeAsync(initializationOptions);
            }
            catch (Exception ex)
            {
                Debug.LogException(ex);
            }


        }
    }


    private void Update()
    {
        HandleRefreshLobbyList(); // Disabled Auto Refresh for testing with multiple builds
        HandleLobbyHeartbeat();
        HandleLobbyPolling();
    }
    private void HandleRefreshLobbyList()
    {
        if (UnityServices.State == ServicesInitializationState.Initialized && AuthenticationService.Instance.IsSignedIn) {
            refreshLobbyListTimer -= Time.deltaTime;
            if (refreshLobbyListTimer < 0f) {
                float refreshLobbyListTimerMax = 5f;
                refreshLobbyListTimer = refreshLobbyListTimerMax;

                RefreshLobbyList();
            }
        }
    }

    private async void HandleLobbyHeartbeat()
    {
        if (IsLobbyHost()) {
            heartbeatTimer -= Time.deltaTime;
            if (heartbeatTimer < 0f) {
                float heartbeatTimerMax = 15f;
                heartbeatTimer = heartbeatTimerMax;

                Debug.Log("Heartbeat");
                await LobbyService.Instance.SendHeartbeatPingAsync(joinedLobby.Id);
            }
        }
    }

    private async void HandleLobbyPolling()
    {
        if (joinedLobby != null) {
            lobbyPollTimer -= Time.deltaTime;
            if (lobbyPollTimer < 0f) {
                float lobbyPollTimerMax = 1.1f;
                lobbyPollTimer = lobbyPollTimerMax;

                joinedLobby = await LobbyService.Instance.GetLobbyAsync(joinedLobby.Id);

                OnJoinedLobbyUpdate?.Invoke(this, new LobbyEventArgs { lobby = joinedLobby });

                if (!IsPlayerInLobby()) {
                    // Player was kicked out of this lobby
                    Debug.Log("Kicked from Lobby!");

                    OnKickedFromLobby?.Invoke(this, new LobbyEventArgs { lobby = joinedLobby });

                    joinedLobby = null;
                }
            }
        }
    }

    public Lobby GetJoinedLobby()
    {
        return joinedLobby;
    }

    public bool IsLobbyHost() {
        return joinedLobby != null && joinedLobby.HostId == AuthenticationService.Instance.PlayerId;
    }

    private bool IsPlayerInLobby() {
        if (joinedLobby != null && joinedLobby.Players != null) {
            foreach (Player player in joinedLobby.Players) {
                if (player.Id == AuthenticationService.Instance.PlayerId) {
                    // This player is in this lobby
                    return true;
                }
            }
        }
        return false;
    }

    private Player GetPlayer() {
        return new Player(AuthenticationService.Instance.PlayerId, null, new Dictionary<string, PlayerDataObject>
        {
            { KEY_PLAYER_NAME, new PlayerDataObject(PlayerDataObject.VisibilityOptions.Public, playerName = Main_Menu_Manger.instance.userName) },
            { KEY_PLAYER_LEVEL, new PlayerDataObject(PlayerDataObject.VisibilityOptions.Public,playerLevel = Main_Menu_Manger.instance.PlayerCurrentLevel)},
            { KEY_Profile_Pic, new PlayerDataObject(PlayerDataObject.VisibilityOptions.Public,playerAvterURL = Main_Menu_Manger.instance.PlayerAvterURL)}
        });
    }
    public async void CreateLobby(string lobbyName, int maxPlayers, bool isPrivate, string minLevel) {
        Player player = GetPlayer();

        CreateLobbyOptions options = new CreateLobbyOptions {
            Player = player,
            IsPrivate = isPrivate,
            Data = new Dictionary<string, DataObject>
            {
                { KEY_GAME_MIN_LEVEL, new DataObject(DataObject.VisibilityOptions.Public, minLevel) }
            }
        };

        Lobby lobby = await LobbyService.Instance.CreateLobbyAsync(lobbyName, (maxPlayers + 4), options);
        InitializeUnityAuthentication();
        joinedLobby = lobby;

        OnJoinedLobby?.Invoke(this, new LobbyEventArgs { lobby = lobby });

        if (lobby.LobbyCode != null)
        {
            LobbyUI.Instance.RoomCode = lobby.LobbyCode;
        }
        else
        {
            Debug.Log("The room dosen't have Code");
        }

        Debug.Log("Created Lobby " + lobby.Name + "Lobby Code" + lobby.LobbyCode);
    }

    public async void RefreshLobbyList() {
        try {
            QueryLobbiesOptions options = new QueryLobbiesOptions();
            options.Count = 25;

            // Filter for open lobbies only
            options.Filters = new List<QueryFilter> {
                new QueryFilter(
                    field: QueryFilter.FieldOptions.AvailableSlots,
                    op: QueryFilter.OpOptions.GT,
                    value: "0")
            };

            // Order by newest lobbies first
            options.Order = new List<QueryOrder> {
                new QueryOrder(
                    asc: false,
                    field: QueryOrder.FieldOptions.Created)
            };

            QueryResponse lobbyListQueryResponse = await Lobbies.Instance.QueryLobbiesAsync();

            OnLobbyListChanged?.Invoke(this, new OnLobbyListChangedEventArgs { lobbyList = lobbyListQueryResponse.Results });
        } catch (LobbyServiceException e) {
            Debug.Log(e);
        }
    }

    public async void JoinLobbyByCode(string lobbyCode)
    {
        Player player = GetPlayer();
        try
        {
            Lobby lobby = await LobbyService.Instance.JoinLobbyByCodeAsync(lobbyCode, new JoinLobbyByCodeOptions
            {
                Player = player
            });

            joinedLobby = lobby;
            Debug.Log("Joined Lobby " + lobby.Name);

            OnJoinedLobby?.Invoke(this, new LobbyEventArgs { lobby = lobby });
        }
        catch (LobbyServiceException e)
        {
            Debug.LogError(e);
            Find_Room_By_Code.instance.ErrorMessge();
        }

    }


    public async void JoinLobby(Lobby lobby) {
        Player player = GetPlayer();

        joinedLobby = await LobbyService.Instance.JoinLobbyByIdAsync(lobby.Id, new JoinLobbyByIdOptions
        {
            Player = player
        });
        Debug.Log("Joined Lobby " + lobby.Name);

        OnJoinedLobby?.Invoke(this, new LobbyEventArgs { lobby = lobby });
    }

    public async void QuickJoinLobby() {
        try {
            QuickJoinLobbyOptions options = new QuickJoinLobbyOptions();

            Lobby lobby = await LobbyService.Instance.QuickJoinLobbyAsync(options);
            joinedLobby = lobby;

            OnJoinedLobby?.Invoke(this, new LobbyEventArgs { lobby = lobby });
        } catch (LobbyServiceException e) {
            Debug.Log(e);
        }
    }

    public async void LeaveLobby() {
        if (joinedLobby != null) {
            try {
                await LobbyService.Instance.RemovePlayerAsync(joinedLobby.Id, AuthenticationService.Instance.PlayerId);

                joinedLobby = null;

                OnLeftLobby?.Invoke(this, EventArgs.Empty);
            } catch (LobbyServiceException e) {
                Debug.Log(e);
            }
        }
    }

    public async void KickPlayer(string playerId) {
        if (IsLobbyHost()) {
            try {
                await LobbyService.Instance.RemovePlayerAsync(joinedLobby.Id, playerId);
            } catch (LobbyServiceException e) {
                Debug.Log(e);
            }
        }
    }

    public Lobby GetLobby()
    {
        return joinedLobby;
    }


}

[ICODE][/ICODE]

So I want to allocated a Server on lobby create and make the player to connect to it and than load the game scene

I follow a little bit different way to do it. I'm not sure is it correct but let me show. Maybe it can help you. First, host player requests matchmaker to allocate a server. I create ticket from host with playerIds like this

        public async Task MatchMakeForLobbyAsync(List<Unity.Services.Lobbies.Models.Player> lobbyPlayers, Action<MatchmakingResult> onMatchmakeResponse)
        {
            if (_matchmaker.IsMatchmaking) return;

            var matchmakerPollingResult = await GetMatchForLobbyAsync(lobbyPlayers);
            onMatchmakeResponse?.Invoke(matchmakerPollingResult);
        }

As you can understand, player list is coming from your_current_lobby_ref.Players.

Then when match made, i get the server IP and Port informations from ticket. I don't know where do you store it but i assume that you are storing it somewhere. I just pass this informations with lobby data like this.

        public async void UpdateServerInfoForLobby(bool isGameStarted, string ip, int port, bool isOnQueue)
        {
            if (JoinedLobby == null || !IsHost) return;

            try
            {
                JoinedLobby = await LobbyService.Instance.UpdateLobbyAsync(JoinedLobby.Id, new UpdateLobbyOptions
                {
                    Data = new Dictionary<string, DataObject>
                    {
                        { Constants.IS_GAME_STARTED, new DataObject(DataObject.VisibilityOptions.Member, isGameStarted ? "true" : "false") },
                        { Constants.SERVER_IP, new DataObject(DataObject.VisibilityOptions.Member, ip) },
                        { Constants.JOIN_CODE, new DataObject(DataObject.VisibilityOptions.Member, "null") },
                        { Constants.SERVER_PORT, new DataObject(DataObject.VisibilityOptions.Member, port.ToString()) },
                        { Constants.IS_ON_QUEUE, new DataObject(DataObject.VisibilityOptions.Member, isOnQueue ? "true" : "false") },
                        { Constants.PLAYER_COUNT_IDENTIFIER, new DataObject(DataObject.VisibilityOptions.Member, JoinedLobby.Players.Count.ToString()) }
                    }
                });
            }
            catch (LobbyServiceException e)
            {
                DivinusLogger.Log(DivinusLogLevel.Error, e.Message);
            }
        }

I use a variable called IsGameStarted on lobby. Each lobby member (except host) checking this variable. If this variable is true, i check for current server informations (i also support local host so CustomQueue part is not important for you)

        private void CheckIsGameStarted(Lobby lobby)
        {
            if (lobby.Data[Constants.IS_GAME_STARTED].Value == "false")
            {
                _leaveButton.interactable = true;
                _isConnectedToMatch = false;
                return;
            }

            if (_isConnectedToMatch || _lobbyManager.IsHost) return;

            _leaveButton.interactable = false;
            _isConnectedToMatch = true;
            var queueType = lobby.Data[Constants.QUEUE_IDENTIFIER].Value == Constants.CUSTOM_QUEUE ? GameQueue.CustomMatchQueue : GameQueue.TeamQueue;
            if (lobby.Data[Constants.QUEUE_IDENTIFIER].Value == Constants.CUSTOM_QUEUE)
            {
                _ = ClientSingleton.Instance.GameManager.JoinRelayWithCodeAsync(lobby.Data[Constants.JOIN_CODE].Value);
            }
            else
            {
                ClientSingleton.Instance.GameManager.JoinMatchWithLobby(lobby.Data[Constants.SERVER_IP].Value, int.Parse(lobby.Data[Constants.SERVER_PORT].Value),queueType);
            }
        }

Lastly, i connect to this ip adress and port on my clients like this :

        private void StartClient(string ip, int port)
        {
            var transport = NetworkManager.Singleton.GetComponent<UnityTransport>();
            transport.SetConnectionData(ip, (ushort)port);
            var payload = JsonUtility.ToJson(UserData);
            var payloadBytes = Encoding.UTF8.GetBytes(payload);
            NetworkManager.Singleton.NetworkConfig.ConnectionData = payloadBytes;
            NetworkManager.Singleton.StartClient();
        }

Hope it helps. Let me know if you didn't understand anything.

so first of all this is my Server Star up script

#if SERVER
using Newtonsoft.Json;
using System;
using System.Threading.Tasks;
using Unity.Netcode;
using Unity.Netcode.Transports.UTP;
using Unity.Services.Core;
using Unity.Services.Matchmaker.Models;
using Unity.Services.Multiplay;
using UnityEngine;

public class Server_Stat_Up : MonoBehaviour
{
    public static Server_Stat_Up instance { get; private set; }

    private const string _internalServerIP = "0.0.0.0";
    private string _externalServerIP = "0.0.0.0";
    private ushort _serverPort = 7777;

    private IMultiplayService _multiplayService;
    private IServerQueryHandler _serverQueryHandler;

    private const int _multiplayServiceTimeout = 20000;

    private string _allocationId;

    private MultiplayEventCallbacks _serverCallbacks;
    private IServerEvents _serverEvents;


    private MatchmakingResults _matchmakingPayload;

    private 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();
        }
    }

    private void Update()
    {
        if (_serverQueryHandler != null)
        {
            if (NetworkManager.Singleton.IsServer)
            {
                _serverQueryHandler.CurrentPlayers = (ushort)NetworkManager.Singleton.ConnectedClientsIds.Count; 
            }
            _serverQueryHandler.UpdateServerCheck();
        }

    }

    private void StartServer()
    {
        NetworkManager.Singleton.GetComponent<UnityTransport>().SetConnectionData(_internalServerIP,_serverPort,"0.0.0.0");
        NetworkManager.Singleton.StartServer();
        NetworkManager.Singleton.OnClientDisconnectCallback += ClientDisconnected;
    }

    async Task StartServerServices()
    {
        await UnityServices.InitializeAsync();
        try
        {
            _multiplayService = MultiplayService.Instance;
            _serverQueryHandler =  await _multiplayService.StartServerQueryHandlerAsync((ushort)ConnectionApprovalHandler.maxPlayers,"WhisetCard","Normal","0","MAP");
        }
        catch(Exception ex)
        {
            Debug.LogWarning($"Something Went wrong trying to set up the SQP Service: \n{ex}");
        }
        try
        {
            _matchmakingPayload = await GetMatchmakerPayload(_multiplayServiceTimeout);

            if (_matchmakingPayload != null)
            {
                Debug.Log($"Got payload: {_matchmakingPayload}");
            }
            else
            {
                Debug.LogWarning($"Getting the Matchmaker Payload timed out , staring with defaults.");
            }
        }
        catch (Exception ex)
        {
            Debug.LogWarning($"Something Went wrong trying to set up the Allocation : \n{ex}");
        }

    }

    private async Task<MatchmakingResults> GetMatchmakerPayload(int timeout)
    {
        var matchmakerPayloadTask = SubscribeAndAwaitMatchmakerAllocation();

        if (await Task.WhenAny(matchmakerPayloadTask, Task.Delay(timeout)) == matchmakerPayloadTask)
        {
            return matchmakerPayloadTask.Result;
        }
        else
        {
            return null;
        }
    }

    private async Task<MatchmakingResults> SubscribeAndAwaitMatchmakerAllocation()
    {
        if (_multiplayService == null) return null;
        _allocationId = null;
        _serverCallbacks = new MultiplayEventCallbacks();
        _serverCallbacks.Allocate += OnMultiplayAllocation;

        _serverEvents = await _multiplayService.SubscribeToServerEventsAsync(_serverCallbacks);

        _allocationId = await AwaitAllocationId();

        var mmPayload = await GetMatchmakerAllocationPayloadAsync();
        return mmPayload;
    }

    private void OnMultiplayAllocation(MultiplayAllocation allocation)
    {
        Debug.Log($"OnAllocation: {allocation.AllocationId}");

        if (string.IsNullOrEmpty(allocation.AllocationId)) return;

        _allocationId = allocation.AllocationId;
    }

    private async Task<string> AwaitAllocationId()
    {
        var config = _multiplayService.ServerConfig;

        Debug.Log($"Awaiting Allocation. Server Config is : \n" +
            $"-Server ID: {config.ServerId}\n" +
            $"-AllocationID: {config.AllocationId}\n"+
            $"-Port:{config.Port}\n"+
            $"-QPort: {config.QueryPort}\n" +
            $"-logs: {config.ServerLogDirectory}");

        while (string.IsNullOrEmpty(_allocationId))
        {
            var configID = config.AllocationId;

            if (!string.IsNullOrEmpty(configID) && string.IsNullOrEmpty(_allocationId))
            {
                _allocationId = configID;
                break;
            }
            await Task.Delay(100);
        }
        return _allocationId;
    }
    private async Task<MatchmakingResults> GetMatchmakerAllocationPayloadAsync()
    {
        try
        {
            var payloadAllocation = await MultiplayService.Instance.GetPayloadAllocationFromJsonAs<MatchmakingResults>();

            var modelAsJson = JsonConvert.SerializeObject(payloadAllocation , Formatting.Indented);

            Debug.Log($"{nameof(GetMatchmakerAllocationPayloadAsync)}..............:\n{modelAsJson}");

            return payloadAllocation;
        }

        catch (Exception ex)
        {
            Debug.LogWarning($"Something Went wrong trying to get Matchmaker Payload in GetMatchmakerAllocationPayloadAsync: \n{ex}");
        }
        return null;
    }

    private void Dispose()
    {
        _serverCallbacks.Allocate -= OnMultiplayAllocation;
        _serverEvents?.UnsubscribeAsync();
    }

    private void ClientDisconnected(ulong clinetid )
    {
      /*  if (NetworkManager.Singleton.ConnectedClients.Count == 0)
        {
            Application.Quit();
        }*/
    }

#endif
}

It allocated and wait for the match making result

second i don't get the part

 if (_matchmaker.IsMatchmaking) return;

var matchmakerPollingResult = await GetMatchForLobbyAsync(lobbyPlayers);

and something else where do i call all of these functions ?

Hmm so you are using a bit different version. You don't have to change anything on server side. You need to change your pool settings. For example if you have 4 players in match, you should createa a queue that excepts 4 people to match them and start server.
I use the version that is available on boss room sample project. But you can do it as you want. All you have to do, when you are creating matchmaking ticket on your client, you should add your other player ID's to the ticket too.

Here is how i create ticket for lobby.

        public async Task<MatchmakingResult> MatchmakeForGroup(UserData data,
            List<Unity.Services.Lobbies.Models.Player> playersOnLobby)
        {
            cancelToken = new CancellationTokenSource();
            var queueName = data.UserGamePreferences.ToMultiplayQueue();
            var createTicketOptions = new CreateTicketOptions(queueName);
            List<Unity.Services.Matchmaker.Models.Player> players = new();
            foreach (var player in playersOnLobby)
            {
                players.Add(new Unity.Services.Matchmaker.Models.Player(player.Id, data.UserGamePreferences));
            }

            try
            {
                IsMatchmaking = true;
                var createResult = await MatchmakerService.Instance.CreateTicketAsync(players, createTicketOptions);
                lastUsedTicket = createResult.Id;
                try
                {
                    while (!cancelToken.IsCancellationRequested)
                    {
                        var checkTicket = await MatchmakerService.Instance.GetTicketAsync(lastUsedTicket);
                        if (checkTicket.Type == typeof(MultiplayAssignment))
                        {
                            var matchAssignment = (MultiplayAssignment)checkTicket.Value;
                            if (matchAssignment.Status == MultiplayAssignment.StatusOptions.Found)
                            {
                                return ReturnMatchResult(MatchmakerPollingResult.Success, "", matchAssignment);
                            }

                            if (matchAssignment.Status == MultiplayAssignment.StatusOptions.Timeout ||
                                matchAssignment.Status == MultiplayAssignment.StatusOptions.Failed)
                            {
                                return ReturnMatchResult(MatchmakerPollingResult.MatchAssignmentError,
                                    $"Ticket: {lastUsedTicket} - {matchAssignment.Status} - {matchAssignment.Message}",
                                    null);
                            }

                            DivinusLogger.Log(DivinusLogLevel.Info,$"Polled Ticket: {lastUsedTicket} Status: {matchAssignment.Status} on {queueName}");
                        }

                        await Task.Delay(TicketCooldown);
                    }
                }
                catch (MatchmakerServiceException e)
                {
                    return ReturnMatchResult(MatchmakerPollingResult.TicketRetrievalError, e.ToString(), null);
                }
            }
            catch (MatchmakerServiceException e)
            {
                return ReturnMatchResult(MatchmakerPollingResult.TicketCreationError, e.ToString(), null);
            }

            return ReturnMatchResult(MatchmakerPollingResult.TicketRetrievalError, "Cancelled Matchmaking", null);
        }

And here is the ReturnMatchResult() function that provided on example

        private MatchmakingResult ReturnMatchResult(MatchmakerPollingResult resultErrorType, string message,
            MultiplayAssignment assignment)
        {
            IsMatchmaking = false;

            if (assignment != null)
            {
                string parsedIp = assignment.Ip;
                int? parsedPort = assignment.Port;
                if (parsedPort == null)
                {
                    return new MatchmakingResult
                    {
                        result = MatchmakerPollingResult.MatchAssignmentError,
                        resultMessage = $"Port missing? - {assignment.Port}\n-{assignment.Message}"
                    };
                }

                return new MatchmakingResult
                {
                    result = MatchmakerPollingResult.Success,
                    ip = parsedIp,
                    port = (int)parsedPort,
                    resultMessage = assignment.Message
                };
            }

            return new MatchmakingResult
            {
                result = resultErrorType,
                resultMessage = message
            };
        }

Let me describe your steps again.

-First get your list of players that in lobby.
-Then add your lobby players to ticket too (i assume that you are already working matchmaking setup for dedicated servers. To create match, you are creating tickets to matchmaker service. You just need to add your lobby players to ticket)
-Listen that ticket matchmaker ticket status and check for it. Do proper actions when match made,failed etc.
-On match found, share your server's ip and port information like i did on lobby.

so this my Matchmaking script so i don't know if it will work or not but i work for me before with n server script so do i use it or not + will your provide code works for me ??

9677453--1379516--New Text Document.txt (3.04 KB)

Yes sure. It should work for your code too. Let me try.

using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Services.Matchmaker;
using Unity.Services.Matchmaker.Models;
using StatusOptions = Unity.Services.Matchmaker.Models.MultiplayAssignment.StatusOptions;
using UnityEngine;
using Unity.Services.Authentication;
using System.Threading.Tasks;
using Unity.Netcode;
using Unity.Netcode.Transports.UTP;

public class MatchMaking : MonoBehaviour
{

    private string _ticketId;

    private void Start()
    {
        Start_Client();
    }

    public void Start_Client()
    {
        CreateATicket();

    }

    private string PlayerID()
    {
        return AuthenticationService.Instance.PlayerId;
    }
    private string PlayerName()
    {
        return AuthenticationService.Instance.PlayerName;
    }


    private async void CreateATicket()
    {
        var options = new CreateTicketOptions("TEST");

        var player = new List<Player>
        {
            new Player(
                PlayerID(),
                PlayerName()
                )
        };

        var ticketResponse = await MatchmakerService.Instance.CreateTicketAsync(player, options);

        _ticketId = ticketResponse.Id;
        Debug.Log($"TicketID: {_ticketId}");

        PollTicketStatus(isForGroup : false);

    }

    private async void CreateTicketForGroup(List<Unity.Services.Lobbies.Models.Player> lobbyPlayers)
    {
            var options = new CreateTicketOptions("TEST");
            List<Unity.Services.Matchmaker.Models.Player> players = new();
            foreach (var player in lobbyPlayers)
            {
                players.Add(new Unity.Services.Matchmaker.Models.Player(player.Id, options));
            }

        var ticketResponse = await MatchmakerService.Instance.CreateTicketAsync(players, options);

        _ticketId = ticketResponse.Id;
        Debug.Log($"TicketID: {_ticketId} for lobby");

        PollTicketStatus(isForGroup : true);
    }

    private async void PollTicketStatus(bool isForGroup)
    {
        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, isForGroup);
                    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,bool isForGroup)
    {
        Debug.Log($"Ticket Assigned: {assignment.Ip}:{assignment.Port}");
        if(isForGroup)
        {
           //send your ip and port data to your lobby like i made.
           //get your ip and port data from assignment.Ip and assignment.Port
           //and when your lobby player get this information,
           // do
           //NetworkManager.Singleton.GetComponent<UnityTransport>().SetConnectionData(assignment.Ip//,(ushort)assignment.Port,"0.0.0.0");
           //NetworkManager.Singleton.StartClient();
           //too on all players on lobby.
        }

        NetworkManager.Singleton.GetComponent<UnityTransport>().SetConnectionData(assignment.Ip,(ushort)assignment.Port,"0.0.0.0");
        NetworkManager.Singleton.StartClient();
    }
}

Please let me know if you need any help on sending - fetching data on lobby players. I updated your code. You should call your CreateTicketForLobby() functions by passing your lobby players as argument. You can see how it's done on my code.