You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

515 lines
18 KiB
C#

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<GameLobby, Lobby> OnLobbyCreated;
public static event Action<GameLobby, Lobby> OnLobbyChanged;
public static event Action<GameLobby, Lobby> OnBasicLobbyChanged;
public static event Action<GameLobby, Lobby> OnLobbyFailedToJoin;
public static event Action<GameLobby, Lobby> OnLobbyLeaved;
public static event Action<GameLobby, Lobby> OnHostLeftLobby;
public static event Action<GameLobby, Lobby> OnLobbyFailedToLeave;
public static event Action<Lobby> 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<bool> 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<string, PlayerDataObject>
{
{ "PlayerName", new PlayerDataObject(PlayerDataObject.VisibilityOptions.Member, "BRO") }
}
};
// private Player GetPlayer2() => new() {
// Data = new Dictionary<string, PlayerDataObject>
// {
// { "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<string, DataObject> {
{"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<string, DataObject>
{
{"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<string, DataObject>
{
{"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<string, DataObject>
{
{"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<string, DataObject>
{
{"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;
}
}