using System; using System.Collections.Generic; using System.Threading.Tasks; using Unity.Services.Authentication; using Unity.Services.Lobbies; using Unity.Services.Lobbies.Models; using Unity.Services.Relay; using UnityEngine; namespace LobbyScripts { public class GameLobby : MonoBehaviour { [SerializeField] private float _lobbyRefreshTime; private float _heartBeatTimer; private float _lobbyPollTimer; public static event Action OnLobbyCreated; public static event Action OnLobbyChanged; public static event Action OnBasicLobbyChanged; public static event Action OnLobbyFailedToJoin; public static event Action OnLobbyLeaved; public static event Action OnHostLeftLobby; public static event Action OnLobbyFailedToLeave; public static event Action OnWaitingForPLayers; public static event Action OnRelayStarted; public static event Action OnWaitingToJoinRelay; public static event Action OnRelayCompleted; public static event Action OnRelayFailed; public static event Action OnUpdateLobby; public static event Action OnLobbyUpdated; public static event Action OnUpdateLobbyFailed; public static event Action OnGameStarted; private string _playerId; private bool _lobbyFound = true; private bool _gameIsStarted; private bool _gameJoined; private bool _isLeavingLobby; private bool _leavingLobbyFailed; private bool _hostHasLeftLobby; public string LobbyCode { private set; get;} public Lobby LobbyInstance { set; get; } public static GameLobby GameLobbyInstance { private set; get;} public bool IsCreatedThroughRelay { set; get; } [SerializeField] private LoginSessions _loginSessionManager; private void Awake() { if (GameLobbyInstance != null && GameLobbyInstance != this) { Destroy(GameLobbyInstance); return; } GameLobbyInstance = this; } private void OnEnable() { LobbyUI.DoHostLobby += CreateLobbyInstance; LobbyUI.DoJoinPrivateLobbyByCode += JoinLobbyThroughCode; LobbyUI.DoJoinPrivateLobbyByID += JoinLobbyThroughID; LobbyUI.DoStartTheGame += StartGame; LobbyUI.DoLeaveLobby += LeaveLobby; } private void OnDisable() { LobbyUI.DoHostLobby -= CreateLobbyInstance; LobbyUI.DoJoinPrivateLobbyByCode -= JoinLobbyThroughCode; LobbyUI.DoJoinPrivateLobbyByID -= JoinLobbyThroughID; LobbyUI.DoStartTheGame -= StartGame; LobbyUI.DoLeaveLobby -= LeaveLobby; } private void Update() { HandleLobbyHeartBeat(); HandleLobbyPolling(); } private async void HandleLobbyHeartBeat() { if(LobbyInstance == null) return; if(!IsLobbyHost()) return; _heartBeatTimer -= Time.deltaTime; if (!(_heartBeatTimer < 0)) return; _heartBeatTimer = _lobbyRefreshTime; await LobbyService.Instance.SendHeartbeatPingAsync(LobbyInstance.Id); } private async void HandleLobbyPolling() { if (LobbyInstance == null) return; if(_hostHasLeftLobby) return; if(_isLeavingLobby) return; _lobbyPollTimer -= Time.deltaTime; if (!(_lobbyPollTimer < 0f)) return; var lobbyPollTimerMax = 1.1f; _lobbyPollTimer = lobbyPollTimerMax; try { var lobby = await LobbyService.Instance.GetLobbyAsync(LobbyInstance.Id); LobbyInstance = lobby; } catch (LobbyServiceException e) { if(e.Reason == LobbyExceptionReason.Forbidden) { LobbyInstance = null; if(!IsCreatedThroughRelay) OnLobbyLeaved?.Invoke(this, null); return; } } if (await CheckIfHostHasLeft()) return; if (CheckIfPlayerIsKicked()) return; if (IsCreatedThroughRelay) { OnBasicLobbyChanged?.Invoke(this, LobbyInstance); return; } await CheckWhetherGameHasStarted(); } private async Task CheckWhetherGameHasStarted() { if (!_gameIsStarted) OnLobbyChanged?.Invoke(this, LobbyInstance); if (LobbyInstance.Data["START_GAME"].Value == "0") return; if (!IsLobbyHost() && !_gameJoined) { Debug.Log("LC: "+ LobbyInstance.Data["START_GAME"].Value); OnWaitingToJoinRelay?.Invoke(); await GameRelay.Instance.JoinRelay(LobbyInstance.Data["START_GAME"].Value); OnRelayCompleted?.Invoke(); _gameJoined = true; Debug.Log("End of if"); } OnWaitingForPLayers?.Invoke(LobbyInstance); Debug.Log("OnWaitingForPLayers"); // if (LobbyInstance.Data["PLAYER_COUNT"].Value != LobbyInstance.Players.Count.ToString()) return; // Debug.Log("PLAYER_COUNT"); OnGameStarted?.Invoke(); Debug.Log("OnGameStarted"); // GameManager.Ins.isHost = IsLobbyHost(); LobbyInstance = null; Debug.Log("LobbyInstance"); } private async Task CheckIfHostHasLeft() { if (LobbyInstance.Data["HOST_LIFE"].Value == "Dead") { if (LobbyInstance.Players.Count == 0) { await Lobbies.Instance.DeleteLobbyAsync(LobbyInstance.Id); } LeaveLobby(); return true; } return false; } private bool CheckIfPlayerIsKicked() { var currentPlayer = AuthenticationService.Instance.PlayerId; if (LobbyInstance.Data["PLAYER_TO_KICK"].Value != currentPlayer) return false; OnLobbyLeaved?.Invoke(this, LobbyInstance); LobbyInstance = null; return true; } public bool IsLobbyHost() => LobbyInstance != null && LobbyInstance.HostId == AuthenticationService.Instance.PlayerId; private Player GetPlayer() => new() { Data = new Dictionary { { "PlayerName", new PlayerDataObject(PlayerDataObject.VisibilityOptions.Member, "BRO") } } }; // private Player GetPlayer2() => new() { // Data = new Dictionary // { // { "PlayerName", new PlayerDataObject(PlayerDataObject.VisibilityOptions.Member, $"SecondPlayer") } // } // }; public async void CreateLobbyInstance(LobbyData lobbyData, bool shouldUpdateUI) => await CreateLobby(lobbyData, shouldUpdateUI); public async Task CreateLobby(LobbyData lobbyData, bool shouldUpdateUI) { await _loginSessionManager.LoginUnityServices(); try { var player = GetPlayer(); bool shouldUpdateLobbyName = String.IsNullOrWhiteSpace(lobbyData.LobbyName); var options = new CreateLobbyOptions { Player = player, IsPrivate = lobbyData.IsPrivate, Data = new Dictionary { {"LOBBY_NAME", new DataObject(DataObject.VisibilityOptions.Member,shouldUpdateLobbyName?"GameLobby":lobbyData.LobbyName)}, {"START_GAME", new DataObject(DataObject.VisibilityOptions.Member, "0")}, {"PLAYER_COUNT", new DataObject(DataObject.VisibilityOptions.Member, "0")}, {"HOST_LIFE", new DataObject(DataObject.VisibilityOptions.Member, "Alive")}, {"PLAYER_TO_KICK", new DataObject(DataObject.VisibilityOptions.Member, "None")}, {"HOST_ROOM_DATA", new DataObject(DataObject.VisibilityOptions.Member,"ROOM1")} } }; var lobby = await LobbyService.Instance.CreateLobbyAsync(lobbyData.LobbyName, lobbyData.MaxPlayers, options); //Updating lobby name to lobby code that was generated after the creation of the lobby if (shouldUpdateLobbyName) UpdateLobbyNameToLobbyCode(); LobbyCode = lobby.LobbyCode; /* if (GameVivoxManager.Instance.ChannelSession?.Parent.State != LoginState.LoggedIn) { GameVivoxManager.Instance.Login(player.Data["PlayerName"].Value); GameVivoxManager.Instance.ChannelName = lobby.Data["LOBBY_NAME"].Value; }*/ Debug.Log(LobbyCode); IsCreatedThroughRelay = !shouldUpdateUI; LobbyInstance = lobby; if (shouldUpdateUI) { OnLobbyCreated?.Invoke(this, lobby); OnLobbyChanged?.Invoke(this, lobby); } Debug.Log("Created Lobby " + lobby.Name); async void UpdateLobbyNameToLobbyCode() { try { var updatedLobby = await Lobbies.Instance.UpdateLobbyAsync(lobby.Id, new UpdateLobbyOptions { Name = lobby.LobbyCode }); lobby = updatedLobby; } catch (Exception e) { Debug.Log($"Failed updating lobby: {e}"); } } } catch (LobbyServiceException e) { Debug.Log(e); } } private async void JoinLobbyThroughID(string lobbyCode, bool shouldUpdateUI) => await JoinPrivateLobbyByID(lobbyCode, shouldUpdateUI); public async Task JoinPrivateLobbyByID(string lobbyCode, bool shouldUpdateUI) { await _loginSessionManager.LoginUnityServices(); var player = GetPlayer(); try { LobbyInstance = await LobbyService.Instance.JoinLobbyByIdAsync(lobbyCode, new JoinLobbyByIdOptions() { Player = player }); /*if (GameVivoxManager.Instance.ChannelSession?.Parent.State != LoginState.LoggedIn) { GameVivoxManager.Instance.Login(player.Data["PlayerName"].Value); GameVivoxManager.Instance.ChannelName = LobbyInstance.Data["LOBBY_NAME"].Value; }*/ IsCreatedThroughRelay = !shouldUpdateUI; if (shouldUpdateUI) { OnLobbyChanged?.Invoke(this, LobbyInstance); } } catch (LobbyServiceException e) { OnLobbyFailedToJoin?.Invoke(this, null); Debug.Log(e); } } private async void JoinLobbyThroughCode(string lobbyCode, bool shouldUpdateUI) => await JoinPrivateLobbyByCode(lobbyCode, shouldUpdateUI); public async Task JoinPrivateLobbyByCode(string lobbyCode, bool shouldUpdateUI) { await _loginSessionManager.LoginUnityServices(); var player = GetPlayer(); try { LobbyInstance = await LobbyService.Instance.JoinLobbyByCodeAsync(lobbyCode, new JoinLobbyByCodeOptions() { Player = player }); /*if (GameVivoxManager.Instance.ChannelSession?.Parent.State != LoginState.LoggedIn) { GameVivoxManager.Instance.Login(player.Data["PlayerName"].Value); GameVivoxManager.Instance.ChannelName = LobbyInstance.Data["LOBBY_NAME"].Value; }*/ IsCreatedThroughRelay = !shouldUpdateUI; if (shouldUpdateUI) { OnLobbyChanged?.Invoke(this, LobbyInstance); } } catch (LobbyServiceException e) { OnLobbyFailedToJoin?.Invoke(this, null); Debug.Log(e); } } public async void StartGame() { if(!IsLobbyHost()) return; string relayCode = default; try { _gameIsStarted = true; OnRelayStarted?.Invoke(); relayCode = await GameRelay.Instance.CreateRelay(LobbyInstance.Players.Count); OnRelayCompleted?.Invoke(); } catch (RelayServiceException e) { _gameIsStarted = false; OnRelayFailed?.Invoke(); Debug.Log(e); } try { OnUpdateLobby?.Invoke(); var lobby = await Lobbies.Instance.UpdateLobbyAsync(LobbyInstance.Id, new UpdateLobbyOptions { Data = new Dictionary { {"START_GAME", new DataObject(DataObject.VisibilityOptions.Member, relayCode)}, {"PLAYER_COUNT", new DataObject(DataObject.VisibilityOptions.Member, LobbyInstance.Players.Count.ToString())} } }); LobbyInstance = lobby; OnLobbyUpdated?.Invoke(); } catch (LobbyServiceException e) { _gameIsStarted = false; OnUpdateLobbyFailed?.Invoke(); Console.WriteLine(e); } } public async void LeaveLobby() { try { if (IsLobbyHost()) _hostHasLeftLobby = true; _isLeavingLobby = true; var playerId = AuthenticationService.Instance.PlayerId; if (_hostHasLeftLobby) { OnHostLeftLobby?.Invoke(this, LobbyInstance); var lobby = await Lobbies.Instance.UpdateLobbyAsync(LobbyInstance.Id, new UpdateLobbyOptions { Data = new Dictionary { {"HOST_LIFE", new DataObject(DataObject.VisibilityOptions.Member, "Dead")} } }); LobbyInstance = lobby; } else { OnLobbyLeaved?.Invoke(this, LobbyInstance); } await LobbyService.Instance.RemovePlayerAsync(LobbyInstance.Id, playerId); /* GameVivoxManager.Instance.LeaveChannel(GameVivoxManager.Instance.ChannelSession?.Channel); VivoxUI.LogOutUser?.Invoke(); */ LobbyInstance = null; _hostHasLeftLobby = false; _isLeavingLobby = false; } catch (LobbyServiceException e) { if (IsLobbyHost()) _hostHasLeftLobby = false; _isLeavingLobby = false; OnLobbyFailedToLeave?.Invoke(this , LobbyInstance); Debug.Log(e); } } public async void KickAPlayer(Player player) { try { var lobby = await LobbyService.Instance.UpdateLobbyAsync(LobbyInstance.Id, new UpdateLobbyOptions { Data = new Dictionary { {"PLAYER_TO_KICK", new DataObject(DataObject.VisibilityOptions.Member, $"{player.Id}")} } }); LobbyInstance = lobby; } catch (LobbyServiceException e) { Console.WriteLine(e); throw; } try { await LobbyService.Instance.RemovePlayerAsync(LobbyInstance.Id, player.Id); OnLobbyChanged?.Invoke(this, LobbyInstance); } catch (LobbyServiceException e) { Console.WriteLine(e); throw; } try { var lobby = await LobbyService.Instance.UpdateLobbyAsync(LobbyInstance.Id, new UpdateLobbyOptions { Data = new Dictionary { {"PLAYER_TO_KICK", new DataObject(DataObject.VisibilityOptions.Member, $"None")} } }); LobbyInstance = lobby; } catch (LobbyServiceException e) { Console.WriteLine(e); throw; } } public async void DestroyLobby() { try { await LobbyService.Instance.DeleteLobbyAsync(LobbyInstance.Id); } catch (LobbyServiceException e) { Debug.Log(e); } } } public struct LobbyData { public string LobbyName; public int MaxPlayers; public bool IsPrivate; } }