using System; using System.Collections; using Unity.BossRoom.ConnectionManagement; using Unity.BossRoom.Gameplay.GameplayObjects; using Unity.BossRoom.Infrastructure; using Unity.Multiplayer.Samples.BossRoom; using Unity.Multiplayer.Samples.Utilities; using Unity.Netcode; using UnityEngine; using VContainer; namespace Unity.BossRoom.Gameplay.GameState { /// /// Server specialization of Character Select game state. /// [RequireComponent(typeof(NetcodeHooks), typeof(NetworkCharSelection))] public class ServerCharSelectState : GameStateBehaviour { [SerializeField] NetcodeHooks m_NetcodeHooks; public override GameState ActiveState => GameState.CharSelect; public NetworkCharSelection networkCharSelection { get; private set; } Coroutine m_WaitToEndLobbyCoroutine; [Inject] ConnectionManager m_ConnectionManager; protected override void Awake() { base.Awake(); networkCharSelection = GetComponent(); m_NetcodeHooks.OnNetworkSpawnHook += OnNetworkSpawn; m_NetcodeHooks.OnNetworkDespawnHook += OnNetworkDespawn; } protected override void OnDestroy() { base.OnDestroy(); if (m_NetcodeHooks) { m_NetcodeHooks.OnNetworkSpawnHook -= OnNetworkSpawn; m_NetcodeHooks.OnNetworkDespawnHook -= OnNetworkDespawn; } } void OnClientChangedSeat(ulong clientId, int newSeatIdx, bool lockedIn) { int idx = FindLobbyPlayerIdx(clientId); if (idx == -1) { throw new Exception($"OnClientChangedSeat: client ID {clientId} is not a lobby player and cannot change seats! Shouldn't be here!"); } if (networkCharSelection.IsLobbyClosed.Value) { // The user tried to change their class after everything was locked in... too late! Discard this choice return; } // Allow multiple players to select the same character without restrictions networkCharSelection.LobbyPlayers[idx] = new NetworkCharSelection.LobbyPlayerState(clientId, networkCharSelection.LobbyPlayers[idx].PlayerName, networkCharSelection.LobbyPlayers[idx].PlayerNumber, lockedIn ? NetworkCharSelection.SeatState.LockedIn : NetworkCharSelection.SeatState.Active, newSeatIdx, Time.time); Debug.Log("CloseLobbyIfReady"); CloseLobbyIfReady(); } /// /// Returns the index of a client in the master LobbyPlayer list, or -1 if not found /// int FindLobbyPlayerIdx(ulong clientId) { for (int i = 0; i < networkCharSelection.LobbyPlayers.Count; ++i) { if (networkCharSelection.LobbyPlayers[i].ClientId == clientId) return i; } return -1; } public void StartGame() { networkCharSelection.IsLobbyClosed.Value = true; SaveLobbyResults(); m_WaitToEndLobbyCoroutine = StartCoroutine(WaitToEndLobby()); } void CloseLobbyIfReady() { startButtonScipt.InteractableTruer(false); foreach (NetworkCharSelection.LobbyPlayerState playerInfo in networkCharSelection.LobbyPlayers) { if (playerInfo.SeatState != NetworkCharSelection.SeatState.LockedIn) return; // at least one player isn't locked in yet! } startButtonScipt.InteractableTruer(true); } public StartButtonScipt startButtonScipt; void CancelCloseLobby() { if (m_WaitToEndLobbyCoroutine != null) { StopCoroutine(m_WaitToEndLobbyCoroutine); } networkCharSelection.IsLobbyClosed.Value = false; } void SaveLobbyResults() { int numofPlayersInLobby = networkCharSelection.LobbyPlayers.Count; PlayerPrefs.SetInt("NumberOfLobbyPlayers", numofPlayersInLobby); Debug.Log("Number of Players in lobby are: " + numofPlayersInLobby); foreach (NetworkCharSelection.LobbyPlayerState playerInfo in networkCharSelection.LobbyPlayers) { var playerNetworkObject = NetworkManager.Singleton.SpawnManager.GetPlayerNetworkObject(playerInfo.ClientId); if (playerNetworkObject && playerNetworkObject.TryGetComponent(out PersistentPlayer persistentPlayer)) { persistentPlayer.NetworkAvatarGuidState.AvatarGuid.Value = networkCharSelection.AvatarConfiguration[playerInfo.SeatIdx].Guid.ToNetworkGuid(); } } } IEnumerator WaitToEndLobby() { yield return new WaitForSeconds(3); SceneLoaderWrapper.Instance.LoadScene("BossRoom", useNetworkSceneManager: true); } public void OnNetworkDespawn() { if (NetworkManager.Singleton) { NetworkManager.Singleton.OnClientDisconnectCallback -= OnClientDisconnectCallback; NetworkManager.Singleton.SceneManager.OnSceneEvent -= OnSceneEvent; } if (networkCharSelection) { networkCharSelection.OnClientChangedSeat -= OnClientChangedSeat; } } public void OnNetworkSpawn() { if (!NetworkManager.Singleton.IsServer) { enabled = false; } else { NetworkManager.Singleton.OnClientDisconnectCallback += OnClientDisconnectCallback; networkCharSelection.OnClientChangedSeat += OnClientChangedSeat; NetworkManager.Singleton.SceneManager.OnSceneEvent += OnSceneEvent; } } void OnSceneEvent(SceneEvent sceneEvent) { if (sceneEvent.SceneEventType != SceneEventType.LoadComplete) return; SeatNewPlayer(sceneEvent.ClientId); } int GetAvailablePlayerNumber() { for (int possiblePlayerNumber = 0; possiblePlayerNumber < m_ConnectionManager.MaxConnectedPlayers; ++possiblePlayerNumber) { if (IsPlayerNumberAvailable(possiblePlayerNumber)) { return possiblePlayerNumber; } } return -1; } bool IsPlayerNumberAvailable(int playerNumber) { foreach (NetworkCharSelection.LobbyPlayerState playerState in networkCharSelection.LobbyPlayers) { if (playerState.PlayerNumber == playerNumber) { return false; } } return true; } void SeatNewPlayer(ulong clientId) { if (networkCharSelection.IsLobbyClosed.Value) { CancelCloseLobby(); } SessionPlayerData? sessionPlayerData = SessionManager.Instance.GetPlayerData(clientId); if (sessionPlayerData.HasValue) { var playerData = sessionPlayerData.Value; if (playerData.PlayerNumber == -1 || !IsPlayerNumberAvailable(playerData.PlayerNumber)) { playerData.PlayerNumber = GetAvailablePlayerNumber(); } if (playerData.PlayerNumber == -1) { throw new Exception($"No available player number for client ID {clientId}"); } networkCharSelection.LobbyPlayers.Add(new NetworkCharSelection.LobbyPlayerState(clientId, playerData.PlayerName, playerData.PlayerNumber, NetworkCharSelection.SeatState.Inactive)); SessionManager.Instance.SetPlayerData(clientId, playerData); } } void OnClientDisconnectCallback(ulong clientId) { for (int i = 0; i < networkCharSelection.LobbyPlayers.Count; ++i) { if (networkCharSelection.LobbyPlayers[i].ClientId == clientId) { networkCharSelection.LobbyPlayers.RemoveAt(i); break; } } if (!networkCharSelection.IsLobbyClosed.Value) { CloseLobbyIfReady(); } } } } //using System; //using System.Collections; //using Unity.BossRoom.ConnectionManagement; //using Unity.BossRoom.Gameplay.GameplayObjects; //using Unity.BossRoom.Infrastructure; //using Unity.Multiplayer.Samples.BossRoom; //using Unity.Multiplayer.Samples.Utilities; //using Unity.Netcode; //using UnityEngine; //using VContainer; //namespace Unity.BossRoom.Gameplay.GameState //{ // /// // /// Server specialization of Character Select game state. // /// // [RequireComponent(typeof(NetcodeHooks), typeof(NetworkCharSelection))] // public class ServerCharSelectState : GameStateBehaviour // { // [SerializeField] // NetcodeHooks m_NetcodeHooks; // public override GameState ActiveState => GameState.CharSelect; // public NetworkCharSelection networkCharSelection { get; private set; } // //public static int numOfPlayers; // Coroutine m_WaitToEndLobbyCoroutine; // [Inject] // ConnectionManager m_ConnectionManager; // protected override void Awake() // { // base.Awake(); // networkCharSelection = GetComponent(); // m_NetcodeHooks.OnNetworkSpawnHook += OnNetworkSpawn; // m_NetcodeHooks.OnNetworkDespawnHook += OnNetworkDespawn; // } // protected override void OnDestroy() // { // base.OnDestroy(); // if (m_NetcodeHooks) // { // m_NetcodeHooks.OnNetworkSpawnHook -= OnNetworkSpawn; // m_NetcodeHooks.OnNetworkDespawnHook -= OnNetworkDespawn; // } // } // void OnClientChangedSeat(ulong clientId, int newSeatIdx, bool lockedIn) // { // int idx = FindLobbyPlayerIdx(clientId); // if (idx == -1) // { // throw new Exception($"OnClientChangedSeat: client ID {clientId} is not a lobby player and cannot change seats! Shouldn't be here!"); // } // if (networkCharSelection.IsLobbyClosed.Value) // { // // The user tried to change their class after everything was locked in... too late! Discard this choice // return; // } // if (newSeatIdx == -1) // { // // we can't lock in with no seat // lockedIn = false; // } // else // { // // see if someone has already locked-in that seat! If so, too late... discard this choice // foreach (NetworkCharSelection.LobbyPlayerState playerInfo in networkCharSelection.LobbyPlayers) // { // if (playerInfo.ClientId != clientId && playerInfo.SeatIdx == newSeatIdx && playerInfo.SeatState == NetworkCharSelection.SeatState.LockedIn) // { // // somebody already locked this choice in. Stop! // // Instead of granting lock request, change this player to Inactive state. // networkCharSelection.LobbyPlayers[idx] = new NetworkCharSelection.LobbyPlayerState(clientId, // networkCharSelection.LobbyPlayers[idx].PlayerName, // networkCharSelection.LobbyPlayers[idx].PlayerNumber, // NetworkCharSelection.SeatState.Inactive); // // then early out // return; // } // } // } // networkCharSelection.LobbyPlayers[idx] = new NetworkCharSelection.LobbyPlayerState(clientId, // networkCharSelection.LobbyPlayers[idx].PlayerName, // networkCharSelection.LobbyPlayers[idx].PlayerNumber, // lockedIn ? NetworkCharSelection.SeatState.LockedIn : NetworkCharSelection.SeatState.Active, // newSeatIdx, // Time.time); // if (lockedIn) // { // // to help the clients visually keep track of who's in what seat, we'll "kick out" any other players // // who were also in that seat. (Those players didn't click "Ready!" fast enough, somebody else took their seat!) // for (int i = 0; i < networkCharSelection.LobbyPlayers.Count; ++i) // { // if (networkCharSelection.LobbyPlayers[i].SeatIdx == newSeatIdx && i != idx) // { // // change this player to Inactive state. // networkCharSelection.LobbyPlayers[i] = new NetworkCharSelection.LobbyPlayerState( // networkCharSelection.LobbyPlayers[i].ClientId, // networkCharSelection.LobbyPlayers[i].PlayerName, // networkCharSelection.LobbyPlayers[i].PlayerNumber, // NetworkCharSelection.SeatState.Inactive); // } // } // } // CloseLobbyIfReady(); // } // /// // /// Returns the index of a client in the master LobbyPlayer list, or -1 if not found // /// // int FindLobbyPlayerIdx(ulong clientId) // { // for (int i = 0; i < networkCharSelection.LobbyPlayers.Count; ++i) // { // if (networkCharSelection.LobbyPlayers[i].ClientId == clientId) // return i; // } // return -1; // } // /// // /// Looks through all our connections and sees if everyone has locked in their choice; // /// if so, we lock in the whole lobby, save state, and begin the transition to gameplay // /// // void CloseLobbyIfReady() // { // foreach (NetworkCharSelection.LobbyPlayerState playerInfo in networkCharSelection.LobbyPlayers) // { // if (playerInfo.SeatState != NetworkCharSelection.SeatState.LockedIn) // return; // nope, at least one player isn't locked in yet! // } // // everybody's ready at the same time! Lock it down! // networkCharSelection.IsLobbyClosed.Value = true; // // remember our choices so the next scene can use the info // SaveLobbyResults(); // // Delay a few seconds to give the UI time to react, then switch scenes // m_WaitToEndLobbyCoroutine = StartCoroutine(WaitToEndLobby()); // } // /// // /// Cancels the process of closing the lobby, so that if a new player joins, they are able to chose a character. // /// // void CancelCloseLobby() // { // if (m_WaitToEndLobbyCoroutine != null) // { // StopCoroutine(m_WaitToEndLobbyCoroutine); // } // networkCharSelection.IsLobbyClosed.Value = false; // } // void SaveLobbyResults() // { // int numofPlayersInLobby = networkCharSelection.LobbyPlayers.Count; // //numOfPlayers = numofPlayersInLobby; // PlayerPrefs.SetInt("NumberOfLobbyPlayers", numofPlayersInLobby); // Debug.Log("Number of Players in lobby are: " + numofPlayersInLobby); // foreach (NetworkCharSelection.LobbyPlayerState playerInfo in networkCharSelection.LobbyPlayers) // { // var playerNetworkObject = NetworkManager.Singleton.SpawnManager.GetPlayerNetworkObject(playerInfo.ClientId); // if (playerNetworkObject && playerNetworkObject.TryGetComponent(out PersistentPlayer persistentPlayer)) // { // // pass avatar GUID to PersistentPlayer // // it'd be great to simplify this with something like a NetworkScriptableObjects :( // persistentPlayer.NetworkAvatarGuidState.AvatarGuid.Value = // networkCharSelection.AvatarConfiguration[playerInfo.SeatIdx].Guid.ToNetworkGuid(); // } // } // } // IEnumerator WaitToEndLobby() // { // yield return new WaitForSeconds(3); // SceneLoaderWrapper.Instance.LoadScene("BossRoom", useNetworkSceneManager: true); // } // public void OnNetworkDespawn() // { // if (NetworkManager.Singleton) // { // NetworkManager.Singleton.OnClientDisconnectCallback -= OnClientDisconnectCallback; // NetworkManager.Singleton.SceneManager.OnSceneEvent -= OnSceneEvent; // } // if (networkCharSelection) // { // networkCharSelection.OnClientChangedSeat -= OnClientChangedSeat; // } // } // public void OnNetworkSpawn() // { // if (!NetworkManager.Singleton.IsServer) // { // enabled = false; // } // else // { // NetworkManager.Singleton.OnClientDisconnectCallback += OnClientDisconnectCallback; // networkCharSelection.OnClientChangedSeat += OnClientChangedSeat; // NetworkManager.Singleton.SceneManager.OnSceneEvent += OnSceneEvent; // } // } // void OnSceneEvent(SceneEvent sceneEvent) // { // // We need to filter out the event that are not a client has finished loading the scene // if (sceneEvent.SceneEventType != SceneEventType.LoadComplete) return; // // When the client finishes loading the Lobby Map, we will need to Seat it // SeatNewPlayer(sceneEvent.ClientId); // } // int GetAvailablePlayerNumber() // { // for (int possiblePlayerNumber = 0; possiblePlayerNumber < m_ConnectionManager.MaxConnectedPlayers; ++possiblePlayerNumber) // { // if (IsPlayerNumberAvailable(possiblePlayerNumber)) // { // return possiblePlayerNumber; // } // } // // we couldn't get a Player# for this person... which means the lobby is full! // return -1; // } // bool IsPlayerNumberAvailable(int playerNumber) // { // bool found = false; // foreach (NetworkCharSelection.LobbyPlayerState playerState in networkCharSelection.LobbyPlayers) // { // if (playerState.PlayerNumber == playerNumber) // { // found = true; // break; // } // } // return !found; // } // void SeatNewPlayer(ulong clientId) // { // // If lobby is closing and waiting to start the game, cancel to allow that new player to select a character // if (networkCharSelection.IsLobbyClosed.Value) // { // CancelCloseLobby(); // } // SessionPlayerData? sessionPlayerData = SessionManager.Instance.GetPlayerData(clientId); // if (sessionPlayerData.HasValue) // { // var playerData = sessionPlayerData.Value; // if (playerData.PlayerNumber == -1 || !IsPlayerNumberAvailable(playerData.PlayerNumber)) // { // // If no player num already assigned or if player num is no longer available, get an available one. // playerData.PlayerNumber = GetAvailablePlayerNumber(); // } // if (playerData.PlayerNumber == -1) // { // // Sanity check. We ran out of seats... there was no room! // throw new Exception($"we shouldn't be here, connection approval should have refused this connection already for client ID {clientId} and player num {playerData.PlayerNumber}"); // } // networkCharSelection.LobbyPlayers.Add(new NetworkCharSelection.LobbyPlayerState(clientId, playerData.PlayerName, playerData.PlayerNumber, NetworkCharSelection.SeatState.Inactive)); // SessionManager.Instance.SetPlayerData(clientId, playerData); // } // } // void OnClientDisconnectCallback(ulong clientId) // { // // clear this client's PlayerNumber and any associated visuals (so other players know they're gone). // for (int i = 0; i < networkCharSelection.LobbyPlayers.Count; ++i) // { // if (networkCharSelection.LobbyPlayers[i].ClientId == clientId) // { // networkCharSelection.LobbyPlayers.RemoveAt(i); // break; // } // } // if (!networkCharSelection.IsLobbyClosed.Value) // { // // If the lobby is not already closing, close if the remaining players are all ready // CloseLobbyIfReady(); // } // } // } //}