Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Multiplayer working between editor and build but not build to build

Discussion in 'Lobby' started by PersonInAGauntlet, Apr 12, 2023.

  1. PersonInAGauntlet

    PersonInAGauntlet

    Joined:
    May 25, 2020
    Posts:
    3
    I'm trying to set up a lobby system for a unity game and it mostly works.

    But i have run into an issue where for some reason the lobbies are not working for a build connecting to a build but it works with the editor connecting to the build or the build connecting to the editor.

    I have no idea why this is happening.

    This is the lobby code


    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using System.Threading.Tasks;
    using Unity.Netcode;
    using Unity.Netcode.Transports.UTP;
    using Unity.Services.Authentication;
    using Unity.Services.Core;
    using Unity.Services.Lobbies;
    using Unity.Services.Lobbies.Models;
    using Unity.Services.Relay;
    using Unity.Services.Relay.Models;
    using UnityEngine.UI;
    using TMPro;
    using System;

    public class SimpleLobby : MonoBehaviour
    {
    private Lobby _connectedLobby;
    private QueryResponse _lobbies;
    private UnityTransport _transport;
    private const string JoinCodeKey = "j";
    public string playerName;
    private string _playerId;
    private string lobbyCode;
    [SerializeField] private TextMeshProUGUI lobbyCodeText;

    [SerializeField] private Button playButton;
    [SerializeField] private TMP_InputField nameInputField;
    [SerializeField] private TMP_InputField codeInputField;
    [SerializeField] private GameObject lobbyUI;

    private void Awake() => _transport = FindObjectOfType<UnityTransport>();

    public async void CreateOrJoinLobby()
    {
    playerName = nameInputField.text;

    await Authenticate(playerName);

    _connectedLobby = await QuickJoinLobby() ?? await CreateLobby();

    if (_connectedLobby != null) lobbyUI.SetActive(false);
    }

    public async void JoinByCode()
    {
    playerName = nameInputField.text;

    await Authenticate(playerName);

    _connectedLobby = await GetLobbyByCode();

    if (_connectedLobby != null) lobbyUI.SetActive(false);
    }

    private async Task<Lobby> GetLobbyByCode()
    {
    try
    {
    // Attempt to join a lobby in progress
    var lobby = await Lobbies.Instance.JoinLobbyByCodeAsync(codeInputField.text);

    // If we found one, grab the relay allocation details
    var a = await RelayService.Instance.JoinAllocationAsync(lobby.Data[JoinCodeKey].Value);

    // Set the details to the transform
    SetTransformAsClient(a);

    // Join the game room as a client
    NetworkManager.Singleton.StartClient();
    return lobby;
    }
    catch (LobbyServiceException e)
    {
    Debug.Log($"No lobbies available via quick join");
    return null;
    }
    }

    public async Task Authenticate(string playerName)
    {
    //this.playerName = playerName;
    InitializationOptions initializationOptions = new InitializationOptions();
    initializationOptions.SetProfile(playerName);

    await UnityServices.InitializeAsync(initializationOptions);

    AuthenticationService.Instance.SignedIn += () =>
    {
    _playerId = AuthenticationService.Instance.PlayerId;
    Debug.Log("Signed in " + AuthenticationService.Instance.PlayerId);
    };
    }

    private async Task<Lobby> QuickJoinLobby()
    {
    try
    {
    // Attempt to join a lobby in progress
    var lobby = await Lobbies.Instance.QuickJoinLobbyAsync();

    // If we found one, grab the relay allocation details
    var a = await RelayService.Instance.JoinAllocationAsync(lobby.Data[JoinCodeKey].Value);

    // Set the details to the transform
    SetTransformAsClient(a);

    // Join the game room as a client
    NetworkManager.Singleton.StartClient();
    return lobby;
    }
    catch(LobbyServiceException e)
    {
    Debug.Log($"No lobbies available via quick join");
    return null;
    }
    }

    private async Task<Lobby> CreateLobby()
    {
    try
    {
    const int maxPlayers = 10;

    // Create a relay allocation and generate a join code to share with the lobby
    var a = await RelayService.Instance.CreateAllocationAsync(maxPlayers);
    var joinCode = await RelayService.Instance.GetJoinCodeAsync(a.AllocationId);
    lobbyCode = joinCode;
    lobbyCodeText.text = "Server code: " + joinCode;

    // Create a lobby, adding the relay join code to the lobby data
    var options = new CreateLobbyOptions
    {
    Data = new Dictionary<string, DataObject> { { JoinCodeKey, new DataObject(DataObject.VisibilityOptions.Public, joinCode) } }
    };
    var lobby = await Lobbies.Instance.CreateLobbyAsync("Useless Lobby Name", maxPlayers, options);

    // Send a heartbeat every 15 seconds to keep the room alive
    StartCoroutine(HeartbeatLobby(lobby.Id, 15));

    // Set the game room to use the relay allocation
    _transport.SetHostRelayData(a.RelayServer.IpV4, (ushort)a.RelayServer.Port, a.AllocationIdBytes, a.Key, a.ConnectionData);

    // Start the game. i'm doing this immediately, but maybe you want to wait for the lobby to fill up
    NetworkManager.Singleton.StartHost();
    return lobby;
    }
    catch(Exception e)
    {
    Debug.LogFormat("Failed creating a lobby " + e);
    return null;
    }
    }

    private void SetTransformAsClient(JoinAllocation a)
    {
    _transport.SetClientRelayData(a.RelayServer.IpV4, (ushort)a.RelayServer.Port, a.AllocationIdBytes, a.Key, a.ConnectionData, a.HostConnectionData);
    }

    private static IEnumerator HeartbeatLobby(string lobbyId, float waitTimeSeconds)
    {
    var delay = new WaitForSecondsRealtime(waitTimeSeconds);
    while (true)
    {
    Lobbies.Instance.SendHeartbeatPingAsync(lobbyId);
    yield return delay;
    }
    }

    private void OnDestroy()
    {
    try
    {
    StopAllCoroutines();
    // todo: Add a check to see if you're host
    if(_connectedLobby != null)
    {
    if (_connectedLobby.HostId == _playerId) Lobbies.Instance.DeleteLobbyAsync(_connectedLobby.Id);
    else Lobbies.Instance.RemovePlayerAsync(_connectedLobby.Id, _playerId);
    }
    }
    catch (LobbyServiceException e)
    {
    Debug.Log($"Error shutting down lobby: {e}");
    }
    }
    }
     
  2. PersonInAGauntlet

    PersonInAGauntlet

    Joined:
    May 25, 2020
    Posts:
    3
    Apparently its because this isn't working and is not creating the profile based on the name of the player
    public async Task Authenticate(string playerName)
    {
    InitializationOptions initializationOptions = new InitializationOptions();
    initializationOptions.SetProfile(playerName);

    await UnityServices.InitializeAsync(initializationOptions);

    AuthenticationService.Instance.SignedIn += () =>
    {
    _playerId = AuthenticationService.Instance.PlayerId;
    Debug.Log("Signed in " + AuthenticationService.Instance.PlayerId);
    };
    }
     
    andreyshade likes this.
  3. ArthurAtUnity

    ArthurAtUnity

    Unity Technologies

    Joined:
    Aug 9, 2022
    Posts:
    9
    Hi Mellenbull,
    Thank you for providing your code !

    I would like to ask for a few more details: What do you mean by "not working" ? Do you have any error/exception or stacktrace ?
     
  4. andreyshade

    andreyshade

    Joined:
    Aug 8, 2017
    Posts:
    10
  5. andreyshade

    andreyshade

    Joined:
    Aug 8, 2017
    Posts:
    10
    When you try to test lobby using two opened editors, just to simulate two different users. But
    AuthenticationService auth this two fake users as same one, so in result both of then received the same updates from LobbyService