using System; using System.Collections.Generic; 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; using UnityEngine.SceneManagement; using Random = UnityEngine.Random; public class KitchenGameLobby : MonoBehaviour { public Lobby JoinedLobby { get; private set; } public static KitchenGameLobby Instance { get; private set; } private float heartbeatTimer; private const float heartbeatTimerMax = 15f; public event EventHandler OnCreateLobbyStarted; public event EventHandler OnCreateLobbyFailed; public event EventHandler OnJoinStarted; public event EventHandler OnQuickJoinFailed; public event EventHandler OnJoinFailed; public event EventHandler OnLobbyListChanged; public class LobbyListChangedEventArgs : EventArgs { public List LobbyList; } private void Awake() { Instance = this; DontDestroyOnLoad(gameObject); InitializeUnityAuthentitcation(); } private void Update() { HandleHeartbeat(); HandlePeriodicListLobbies(); } private float listLobbiesTimer; private const float listLobbiesTimerMax = 3f; private void HandlePeriodicListLobbies() { if (JoinedLobby == null && AuthenticationService.Instance.IsSignedIn && SceneManager.GetActiveScene().name == Loader.Scene.LobbyScene.ToString()) { listLobbiesTimer -= Time.deltaTime; if (listLobbiesTimer <= 0f) { listLobbiesTimer = listLobbiesTimerMax; ListLobbies(); } } } private void HandleHeartbeat() { if (IsLobbyHost()) { heartbeatTimer -= Time.deltaTime; if (heartbeatTimer <= 0f) { heartbeatTimer = heartbeatTimerMax; LobbyService.Instance.SendHeartbeatPingAsync(JoinedLobby.Id); } } } private bool IsLobbyHost() => JoinedLobby != null && JoinedLobby.HostId == AuthenticationService.Instance.PlayerId; private async void ListLobbies() { try { QueryLobbiesOptions queryLobbiesOptions = new() { Filters = new() { new(QueryFilter.FieldOptions.AvailableSlots, "0", QueryFilter.OpOptions.GT) } }; QueryResponse queryResponse = await LobbyService.Instance.QueryLobbiesAsync(queryLobbiesOptions); OnLobbyListChanged?.Invoke(this, new() { LobbyList = queryResponse.Results }); } catch (LobbyServiceException e) { Debug.Log(e); } } private async void InitializeUnityAuthentitcation() { if (UnityServices.State != ServicesInitializationState.Initialized) await UnityServices.InitializeAsync(); InitializationOptions initializationOptions = new(); // initializationOptions.SetProfile(Random.Range(0, 10000).ToString()); await AuthenticationService.Instance.SignInAnonymouslyAsync(); } private const string keyRelayJoinCode = "RelayJoinCode"; public async void CreateLobby(string lobbyName, bool isPrivate) { OnCreateLobbyStarted?.Invoke(this, EventArgs.Empty); try { JoinedLobby = await LobbyService.Instance.CreateLobbyAsync(lobbyName, KitchenGameMultiplayer.MaxPlayerAmount, new() { IsPrivate = isPrivate }); Allocation allocation = await AllocateRelay(); NetworkManager.Singleton.GetComponent().SetRelayServerData(new(allocation, "dtls")); string relayJoinCode = await GetRelayJoinCode(allocation); await LobbyService.Instance.UpdateLobbyAsync(JoinedLobby.Id, new() { Data = new() { { keyRelayJoinCode, new(DataObject.VisibilityOptions.Member, relayJoinCode) } } }); KitchenGameMultiplayer.Instance.StartHost(); Loader.LoadNetwork(Loader.Scene.CharacterSelectScene); } catch (LobbyServiceException e) { Debug.Log(e); OnCreateLobbyFailed?.Invoke(this, EventArgs.Empty); } } private async Task AllocateRelay() { try { return await RelayService.Instance.CreateAllocationAsync(KitchenGameMultiplayer.MaxPlayerAmount - 1); } catch (RelayServiceException e) { Debug.Log(e); return default; } } private async Task GetRelayJoinCode(Allocation allocation) { try { return await RelayService.Instance.GetJoinCodeAsync(allocation.AllocationId); } catch (RelayServiceException e) { Debug.Log(e); return default; } } private async Task JoinRelay(string joinCode) { try { return await RelayService.Instance.JoinAllocationAsync(joinCode); } catch (RelayServiceException e) { Debug.Log(e); return default; } } public async void QuickJoin() { OnJoinStarted?.Invoke(this, EventArgs.Empty); try { JoinedLobby = await LobbyService.Instance.QuickJoinLobbyAsync(); JoinAllocation joinAllocation = await JoinRelay(JoinedLobby.Data[keyRelayJoinCode].Value); NetworkManager.Singleton.GetComponent().SetRelayServerData(new(joinAllocation, "dtls")); KitchenGameMultiplayer.Instance.StartClient(); } catch (LobbyServiceException e) { Debug.Log(e); OnQuickJoinFailed?.Invoke(this, EventArgs.Empty); } } public async void DeleteLobby() { if (JoinedLobby != null) { try { await LobbyService.Instance.DeleteLobbyAsync(JoinedLobby.Id); JoinedLobby = null; } 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; } 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 async void JoinWithCode(string lobbyCode) { OnJoinStarted?.Invoke(this, EventArgs.Empty); try { JoinedLobby = await LobbyService.Instance.JoinLobbyByCodeAsync(lobbyCode); JoinAllocation joinAllocation = await JoinRelay(JoinedLobby.Data[keyRelayJoinCode].Value); NetworkManager.Singleton.GetComponent().SetRelayServerData(new(joinAllocation, "dtls")); KitchenGameMultiplayer.Instance.StartClient(); } catch (LobbyServiceException e) { Debug.Log(e); OnJoinFailed?.Invoke(this, EventArgs.Empty); } } public async void JoinWithId(string lobbyId) { OnJoinStarted?.Invoke(this, EventArgs.Empty); try { JoinedLobby = await LobbyService.Instance.JoinLobbyByIdAsync(lobbyId); JoinAllocation joinAllocation = await JoinRelay(JoinedLobby.Data[keyRelayJoinCode].Value); NetworkManager.Singleton.GetComponent().SetRelayServerData(new(joinAllocation, "dtls")); KitchenGameMultiplayer.Instance.StartClient(); } catch (LobbyServiceException e) { Debug.Log(e); OnJoinFailed?.Invoke(this, EventArgs.Empty); } } }