I can't get the VR multiplayer template working

As the title says i’m having issues with the multiplayer template. With the original template, no changes, the host cannot make a lobby so the client also cannot join. I’ve been changing it for a while to get it working. Right now my client still cannot join, this is my guess for what the issue is: The host makes a lobby, the host starts as client (it had to do with DA but i’m still confused about it), lobby is initialized and host is properly in there. Client tries to join, also thinks they are host, from here it branches into 2 problems im switching between, either client joins as host and cannot see the other host but lobby code is the same OR the issue i’m having right now: the client fails to join presses back to lobby and the hosts lobby also gets shutdown, but only if the client presses the back to lobby button. I have these logs (there are these $ in front of it but it makes the letters look weird and unreadable):

Debug.Log("{k_DebugPrepend}Network CONNECTED!");
Debug.Log("{k_DebugPrepend}  IsHost: {NetworkManager.Singleton.IsHost}");
Debug.Log("{k_DebugPrepend}  IsClient: {NetworkManager.Singleton.IsClient}");
Debug.Log("{k_DebugPrepend}  IsServer: {NetworkManager.Singleton.IsServer}");
Debug.Log("{k_DebugPrepend}  IsSessionOwner: {isSessionOwner}");

the problem is that these logs are the same for the client and host because they give:
isHost = false, isClient = true, isServer = false, isSessionOwner = true.
Right now i think the issue lies in the SessionManager, but I honestly also have no idea, under here i will give the sessionManager in a code block but the other code that is connected to it in files since they are all pretty damn big. I really hope someone can help me!
LobbyListSlotUI.cs (3.7 KB)
LobbyUI.cs (20.0 KB)
SessionManager.cs (23.3 KB)
XRINetworkGameManager.cs (26.2 KB)

using System.Collections.Generic;
using UnityEngine;
using System.Threading.Tasks;
using System;
using Unity.XR.CoreUtils.Bindings.Variables;
using UnityEngine.SceneManagement;
using Unity.Services.Multiplayer;
using System.Collections;
using Unity.Netcode.Transports.UTP;
using Unity.Netcode;
namespace XRMultiplayer
{
    public enum SessionType
    {
        DistributedAuthority,
        LocalOnly,
    }
   public class SessionManager : MonoBehaviour
    {
        // Lobby data keys
        public const string k_JoinCodeKeyIdentifier = "j";
        public const string k_RegionKeyIdentifier = "r";
        public const string k_BuildIdKeyIdentifier = "b";
        public const string k_SceneKeyIdentifier = "s";
        public const string k_EditorKeyIdentifier = "e";

        public SessionType sessionType => m_SessionType;

        [SerializeField]
        SessionType m_SessionType = SessionType.DistributedAuthority;

        public bool hideEditorFromLobby = false;

        public Action<string> OnSessionFailed;

        public ISession currentSession => m_CurrentSession;
        ISession m_CurrentSession;

        public static IReadOnlyBindableVariable<string> status => m_Status;
        static readonly BindableVariable<string> m_Status = new("");

        const string k_DebugPrepend = "<color=#EC0CFA>[SessionManager]</color> ";

        // Session state
        bool ngoStarted = false;

        private void Awake()
        {
            if (!Application.isEditor)
                hideEditorFromLobby = false;
        }

        private void Start()
        {
            // Subscribe to network events
            if (NetworkManager.Singleton != null)
            {
                NetworkManager.Singleton.OnClientConnectedCallback += OnClientConnected;
                NetworkManager.Singleton.OnClientDisconnectCallback += OnClientDisconnected;
            }
        }

        private void OnClientConnected(ulong clientId)
        {
            Debug.Log($"{k_DebugPrepend}Client connected: {clientId}");
        }

        private void OnClientDisconnected(ulong clientId)
        {
            Debug.Log($"{k_DebugPrepend}Client disconnected: {clientId}");

            if (clientId == NetworkManager.Singleton.LocalClientId)
            {
                Debug.LogError($"{k_DebugPrepend}LOCAL PLAYER DISCONNECTED!");
            }
        }

        //
        // QUICK JOIN
        //
        public async Task<ISession> QuickJoinLobby()
        {
            m_Status.Value = "Searching for sessions…";

            try
            {
                var queryOptions = new QuerySessionsOptions();
                QuerySessionsResults results = await MultiplayerService.Instance.QuerySessionsAsync(queryOptions);

                Debug.Log($"{k_DebugPrepend}Found {results.Sessions.Count} sessions in QuickJoin");

                foreach (var session in results.Sessions)
                {
                    if (session.AvailableSlots > 0)
                    {
                        m_Status.Value = $"Joining {session.Name}...";

                        Debug.Log($"{k_DebugPrepend}Attempting to join session: {session.Name} (ID: {session.Id})");

                        m_CurrentSession = await MultiplayerService.Instance.JoinSessionByIdAsync(session.Id);

                        if (m_CurrentSession != null)
                        {
                            Debug.Log($"{k_DebugPrepend}Successfully joined session in QuickJoin");
                            await ConnectedToSessionAsync(m_CurrentSession);
                            return m_CurrentSession;
                        }
                        else
                        {
                            Debug.LogError($"{k_DebugPrepend}JoinSessionByIdAsync returned null in QuickJoin");
                        }
                    }
                }

                m_Status.Value = "No available sessions found.";
                OnSessionFailed?.Invoke("No available sessions to join");
                return null;
            }
            catch (Exception e)
            {
                Debug.LogException(e);
                OnSessionFailed?.Invoke($"Failed to quick join: {e.Message}");
                return null;
            }
        }

        //
        // JOIN SESSION
        //
        public async Task<ISession> JoinLobby(ISessionInfo sessionInfo = null, string roomCode = null)
        {
            try
            {
                StopAllCoroutines();
                StartCoroutine(PlayConnectionMessage());

                if (sessionInfo != null)
                {
                    m_Status.Value = "Connecting To Room: " + sessionInfo.Name;

                    Debug.Log($"{k_DebugPrepend}Attempting to join session by ID:");
                    Debug.Log($"{k_DebugPrepend}  Name: {sessionInfo.Name}");
                    Debug.Log($"{k_DebugPrepend}  ID: {sessionInfo.Id}");
                    Debug.Log($"{k_DebugPrepend}  AvailableSlots: {sessionInfo.AvailableSlots}");

                    m_CurrentSession = await MultiplayerService.Instance.JoinSessionByIdAsync(sessionInfo.Id);

                    if (m_CurrentSession == null)
                    {
                        Debug.LogError($"{k_DebugPrepend}JoinSessionByIdAsync returned NULL!");
                        OnSessionFailed?.Invoke("Session object is null on join");
                        return null;
                    }
                    else
                    {
                        Debug.Log($"{k_DebugPrepend}Successfully joined session by ID: {m_CurrentSession.Name}");
                    }
                }
                else if (!string.IsNullOrEmpty(roomCode))
                {
                    m_Status.Value = "Connecting To Room Code: " + roomCode;

                    Debug.Log($"{k_DebugPrepend}Attempting to join by code: {roomCode}");

                    m_CurrentSession = await MultiplayerService.Instance.JoinSessionByCodeAsync(roomCode);

                    if (m_CurrentSession == null)
                    {
                        Debug.LogError($"{k_DebugPrepend}JoinSessionByCodeAsync returned NULL!");
                        OnSessionFailed?.Invoke("Session object is null on join by code");
                        return null;
                    }
                    else
                    {
                        Debug.Log($"{k_DebugPrepend}Successfully joined session by code: {m_CurrentSession.Name}");
                    }
                }
                else
                {
                    return await QuickJoinLobby();
                }

                await ConnectedToSessionAsync(m_CurrentSession);
                return m_CurrentSession;
            }
            catch (Exception e)
            {
                Debug.LogError($"{k_DebugPrepend}Exception in JoinLobby: {e.Message}");
                Debug.LogError($"{k_DebugPrepend}Stack Trace: {e.StackTrace}");
                OnSessionFailed?.Invoke($"Failed to Join Lobby: {e.Message}");
                return null;
            }
        }

        //
        // CREATE SESSION - WITH DistributedAuthorityNetwork (REQUIRED for internet)
        //
        public async Task<ISession> CreateSession(string roomName = null, bool isPrivate = false, int maxPlayers = XRINetworkGameManager.maxPlayers)
        {
            string sessionName = string.IsNullOrEmpty(roomName)
                ? $"{XRINetworkGameManager.LocalPlayerName.Value}'s Room"
                : roomName;

            try
            {
                m_Status.Value = $"Creating {sessionName}…";
                StartCoroutine(PlayConnectionMessage());

                // Create session options WITH DistributedAuthorityNetwork (REQUIRED for internet)
                SessionOptions options = GetSessionOptions(sessionName, isPrivate, maxPlayers);

                // IMPORTANT: This is REQUIRED for Unity 6 Multiplayer Services over internet
                options = options.WithDistributedAuthorityNetwork();

                Guid sessionId = Guid.NewGuid();
                Debug.Log($"{k_DebugPrepend}Creating session with ID: {sessionId}, Name: {sessionName}");

                m_CurrentSession = await MultiplayerService.Instance.CreateOrJoinSessionAsync(sessionId.ToString(), options);

                if (m_CurrentSession == null)
                {
                    Debug.LogError($"{k_DebugPrepend}CreateSession returned null!");
                    OnSessionFailed?.Invoke("Failed to create session - null returned");
                    return null;
                }

                Debug.Log($"HOST: Session created successfully!");
                Debug.Log($"  ID: {m_CurrentSession.Id}");
                Debug.Log($"  Code: {m_CurrentSession.Code}");
                Debug.Log($"  Name: {m_CurrentSession.Name}");
                Debug.Log($"  MaxPlayers: {m_CurrentSession.MaxPlayers}");

                await ConnectedToSessionAsync(m_CurrentSession);
                return m_CurrentSession;
            }
            catch (Exception e)
            {
                Utils.Log($"{k_DebugPrepend}Create Failed: {e}", 1);
                OnSessionFailed?.Invoke($"Failed to create session: {e.Message}");
                return null;
            }
        }

        SessionOptions GetSessionOptions(string lobbyName, bool isPrivate, int maxPlayers)
        {
            return new SessionOptions()
            {
                Name = lobbyName,
                MaxPlayers = maxPlayers,
                IsPrivate = isPrivate,

                SessionProperties = new Dictionary<string, SessionProperty>
                {
                    { k_BuildIdKeyIdentifier, new SessionProperty(Application.version) },
                    { k_SceneKeyIdentifier, new SessionProperty(SceneManager.GetActiveScene().name) },
                    { k_EditorKeyIdentifier, new SessionProperty(hideEditorFromLobby.ToString()) }
                }
            };
        }

        IEnumerator PlayConnectionMessage()
        {
            yield return new WaitForSeconds(2);
            m_Status.Value = "Systems functional…";
            yield return new WaitForSeconds(1);
            m_Status.Value = "Connecting…";
        }

        //
        // AFTER CONNECTION - DISTRIBUTED AUTHORITY VERSION
        //
        private async Task ConnectedToSessionAsync(ISession newSession)
        {
            if (newSession == null)
            {
                Debug.LogError($"{k_DebugPrepend}ConnectedToSessionAsync received null session!");
                return;
            }

            m_CurrentSession = newSession;

            Debug.Log($"{k_DebugPrepend}ConnectedToSessionAsync STARTED");

            // Wait for CurrentPlayer to be set
            float timeout = 15f;
            float timer = 0f;

            while (m_CurrentSession.CurrentPlayer == null && timer < timeout)
            {
                await Task.Yield();
                timer += Time.deltaTime;
                Debug.Log($"{k_DebugPrepend}Waiting for CurrentPlayer... {timer:F1}s");
            }

            if (m_CurrentSession.CurrentPlayer == null)
            {
                Debug.LogError($"{k_DebugPrepend}CurrentPlayer not set after timeout!");
                return;
            }

            Debug.Log($"{k_DebugPrepend}CurrentPlayer set: ID={m_CurrentSession.CurrentPlayer.Id}");
            Debug.Log($"{k_DebugPrepend}Session Host ID: {m_CurrentSession.Host}");

            // SIMPLE AND RELIABLE HOST DETECTION LOGIC
            bool isSessionOwner = false;

            // For Unity Editor/MPPM testing: Use player count to determine host/client#if UNITY_EDITOR
            Debug.Log($"{k_DebugPrepend}Unity Editor detected - using player count logic");

            // First player to connect is the host, subsequent players are clients
            // This works reliably for both MPPM and regular editor testing
            if (m_CurrentSession.PlayerCount == 1)
            {
                // First player in session = Host
                isSessionOwner = true;
                Debug.Log($"{k_DebugPrepend}First player in session = Host");
            }
            else
            {
                // Additional players = Clients
                isSessionOwner = false;
                Debug.Log($"{k_DebugPrepend}Player #{m_CurrentSession.PlayerCount} = Client");
            } #else
    // Regular build logic (non-editor)
    Debug.Log($"{k_DebugPrepend}Build detected - using property-based logic");
    
    // Check session properties for original creator
    if (m_CurrentSession.Properties != null &&
        m_CurrentSession.Properties.TryGetValue("session_creator_id", out var creatorProp))
    {
        isSessionOwner = m_CurrentSession.CurrentPlayer.Id == creatorProp.Value;
        Debug.Log($"{k_DebugPrepend}Found session creator in properties: {creatorProp.Value}");
    }
    else if (!string.IsNullOrEmpty(m_CurrentSession.Host))
    {
        isSessionOwner = m_CurrentSession.CurrentPlayer.Id == m_CurrentSession.Host;
        Debug.Log($"{k_DebugPrepend}Using Host comparison");
    }
    else
    {
        // Fallback: If we can't determine, assume client for safety
        isSessionOwner = false;
        Debug.LogWarning($"{k_DebugPrepend}Could not determine session ownership - defaulting to client");
    }#endif

            Debug.Log($"{k_DebugPrepend}Is Session Owner: {isSessionOwner} (FINAL)");
            Debug.Log($"{k_DebugPrepend}Current Player ID: {m_CurrentSession.CurrentPlayer.Id}");
            Debug.Log($"{k_DebugPrepend}Session Host ID: {m_CurrentSession.Host}");
            Debug.Log($"{k_DebugPrepend}Player Count: {m_CurrentSession.PlayerCount}");

            // For Distributed Authority, Unity handles ALL network connections
            Debug.Log($"{k_DebugPrepend}Waiting for Distributed Authority network connection...");

            timeout = 30f;
            timer = 0f;

            while (!NetworkManager.Singleton.IsListening && timer < timeout)
            {
                await Task.Delay(1000);
                timer += 1f;
                Debug.Log($"{k_DebugPrepend}Waiting for network... {timer}s");
            }

            if (NetworkManager.Singleton.IsListening)
            {
                Debug.Log($"{k_DebugPrepend}Network CONNECTED!");
                Debug.Log($"{k_DebugPrepend}  IsHost: {NetworkManager.Singleton.IsHost}");
                Debug.Log($"{k_DebugPrepend}  IsClient: {NetworkManager.Singleton.IsClient}");
                Debug.Log($"{k_DebugPrepend}  IsServer: {NetworkManager.Singleton.IsServer}");
                Debug.Log($"{k_DebugPrepend}  IsSessionOwner: {isSessionOwner}");

                // For Distributed Authority, you might need to handle host differently
                if (isSessionOwner)
                {
                    // We created the session, so we're responsible for spawning initial objects
                    Debug.Log($"{k_DebugPrepend}  ROLE: SESSION OWNER (Host-equivalent)");

                    // Trigger any host-specific initialization here
                    OnBecameSessionOwner();
                }
                else
                {
                    Debug.Log($"{k_DebugPrepend}  ROLE: CLIENT");

                    // Client-specific initialization
                    OnJoinedAsClient();
                }

                // Subscribe to session property changes
                try
                {
                    m_CurrentSession.SessionPropertiesChanged += OnSessionPropertiesChanged;
                    OnSessionPropertiesChanged();

                    Debug.Log($"{k_DebugPrepend}ConnectedToSessionAsync COMPLETED successfully");
                    ngoStarted = true;
                }
                catch (Exception e)
                {
                    Debug.LogError($"{k_DebugPrepend}Error subscribing to session events: {e.Message}");
                }
            }
            else
            {
                Debug.LogError($"{k_DebugPrepend}Network failed to connect after {timeout} seconds!");
            }
        }

        // Add this method for client-specific initialization
        private void OnJoinedAsClient()
        {
            Debug.Log($"{k_DebugPrepend}Client joined session - waiting for host initialization...");

            // Clients should wait for host to spawn objects
            // Add any client-specific setup here
        }

        // Add this method to handle host-specific initialization
        // Add this method to handle host-specific initialization
        private async void OnBecameSessionOwner()
        {
            Debug.Log($"{k_DebugPrepend}Performing session owner initialization...");

            if (m_CurrentSession is IHostSession hostSession)
            {
                try
                {
                    // 1. Set the session property using the correct host API
                    hostSession.SetProperty("session_owner_id", new SessionProperty(m_CurrentSession.CurrentPlayer.Id));

                    // 2. Save all pending property changes
                    await hostSession.SavePropertiesAsync();

                    Debug.Log($"{k_DebugPrepend}Updated session properties with owner info");
                }
                catch (Exception e)
                {
                    Debug.LogError($"{k_DebugPrepend}Failed to update session properties: {e.Message}");
                }
            }
        }

        void OnSessionPropertiesChanged()
        {
            if (m_CurrentSession != null)
            {
                XRINetworkGameManager.ConnectedRoomCode = m_CurrentSession.Code;
                XRINetworkGameManager.ConnectedRoomName.Value = m_CurrentSession.Name;

                Debug.Log($"{k_DebugPrepend}Session properties updated: Code={m_CurrentSession.Code}, Name={m_CurrentSession.Name}");
            }
        }

        public async Task LeaveSession()
        {
            if (m_CurrentSession != null)
            {
                try
                {
                    m_CurrentSession.SessionPropertiesChanged -= OnSessionPropertiesChanged;
                    await m_CurrentSession.LeaveAsync();
                }
                catch (Exception e)
                {
                    Debug.LogError($"{k_DebugPrepend}Error leaving session: {e.Message}");
                }

                m_CurrentSession = null;
                ngoStarted = false;
            }
            else
            {
                Utils.Log($"{k_DebugPrepend}No active session.");
            }
        }

        public static bool CanJoinLobby(ISessionInfo session)
        {
            return XRINetworkGameManager.Instance.sessionManager.currentSession == null ||
                session.Id != XRINetworkGameManager.Instance.sessionManager.currentSession.Id;
        }

        // ----------------------------------------------------------------------
        // REQUIRED LEGACY METHODS FOR VRMP UI
        // ----------------------------------------------------------------------

        public static QuerySessionsOptions GetQuickJoinFilterOptions()
        {
            return new QuerySessionsOptions();
        }

        public static bool CheckForSessionFilter(ISessionInfo sessionInfo)
        {
            return false;
        }

        public static bool CheckForIncompatibilityFilter(ISessionInfo sessionInfo)
        {
            return false;
        }

        // -----------------------------------------------------------
        // ROOM SETTINGS
        // -----------------------------------------------------------

        public async void UpdateRoomPrivacy(bool privateRoom)
        {
            if (m_CurrentSession == null)
            {
                Utils.Log($"{k_DebugPrepend}No active session.");
                return;
            }

            try
            {
                if (m_CurrentSession is IHostSession host)
                {
                    host.IsPrivate = privateRoom;
                    await host.SavePropertiesAsync();
                }
            }
            catch (Exception e)
            {
                Utils.Log($"{k_DebugPrepend}{e}");
            }
        }

        public async void UpdateLobbyName(string lobbyName)
        {
            if (m_CurrentSession == null)
            {
                Utils.Log($"{k_DebugPrepend}No active session.");
                return;
            }

            try
            {
                if (m_CurrentSession is IHostSession host)
                {
                    host.Name = lobbyName;
                    await host.SavePropertiesAsync();

                    XRINetworkGameManager.ConnectedRoomName.Value = lobbyName;
                }
            }
            catch (Exception e)
            {
                Utils.Log($"{k_DebugPrepend}{e}");
            }
        }

        private void OnDestroy()
        {
            if (NetworkManager.Singleton != null)
            {
                NetworkManager.Singleton.OnClientConnectedCallback -= OnClientConnected;
                NetworkManager.Singleton.OnClientDisconnectCallback -= OnClientDisconnected;
            }

            if (m_CurrentSession != null)
            {
                m_CurrentSession.SessionPropertiesChanged -= OnSessionPropertiesChanged;
            }
        }

        public void SetForceClient(bool value)
        {
            // Not used in Distributed Authority
        }

        public async Task<ISession> CreateOrQuickJoin(string roomName = null, bool isPrivate = false)
        {
            var session = await QuickJoinLobby();

            if (session == null)
            {
                m_Status.Value = "Creating new session...";
                session = await CreateSession(roomName, isPrivate, XRINetworkGameManager.maxPlayers);
            }

            return session;
        }

        public async Task CheckUnityServicesStatus()
        {
            try
            {
                Debug.Log($"{k_DebugPrepend}=== Unity Services Status ===");
                Debug.Log($"{k_DebugPrepend}MultiplayerService Instance: {MultiplayerService.Instance != null}");
                Debug.Log($"{k_DebugPrepend}Is Signed In: {Unity.Services.Core.UnityServices.State == Unity.Services.Core.ServicesInitializationState.Initialized}");

                var queryOptions = new QuerySessionsOptions();
                var results = await MultiplayerService.Instance.QuerySessionsAsync(queryOptions);
                Debug.Log($"{k_DebugPrepend}Available sessions: {results.Sessions.Count}");

                foreach (var sessionInfo in results.Sessions)
                {
                    Debug.Log($"{k_DebugPrepend}  - {sessionInfo.Name} (ID: {sessionInfo.Id}, Slots: {sessionInfo.AvailableSlots}/{sessionInfo.MaxPlayers})");
                }

                Debug.Log($"{k_DebugPrepend}===========================");
            }
            catch (Exception e)
            {
                Debug.LogError($"{k_DebugPrepend}Unity Services check failed: {e.Message}");
            }
        }
    }

I solved it! the issue was in the authentication manager that the players were getting the same playerID! i added something so it adds a random number at the end and it’s fixed!