using System; using System.Collections; using System.Collections.Generic; using System.Text; using MLAPI; using MLAPI.SceneManagement; using MLAPI.Spawning; using UnityEngine; using UnityEngine.SceneManagement; public class ServerGameNetPortal : MonoBehaviour { [Header("Settings")] [SerializeField] private int maxPlayers = 3; public static ServerGameNetPortal Instance => instance; private static ServerGameNetPortal instance; public Dictionary clientData; public Dictionary clientIdToGuid; private Dictionary clientSceneMap; private bool gameInProgress; private const int MaxConnectionPayload = 1024; private GameNetPortal gameNetPortal; private int runnersQuitEarly = 0; private void Awake() { if (instance != null && instance != this) { Destroy(gameObject); return; } instance = this; DontDestroyOnLoad(gameObject); } private void Start() { gameNetPortal = GetComponent(); gameNetPortal.OnNetworkReadied += HandleNetworkReadied; NetworkManager.Singleton.ConnectionApprovalCallback += ApprovalCheck; NetworkManager.Singleton.OnServerStarted += HandleServerStarted; clientData = new Dictionary(); clientIdToGuid = new Dictionary(); clientSceneMap = new Dictionary(); } private void OnDestroy() { if (gameNetPortal == null) { return; } gameNetPortal.OnNetworkReadied -= HandleNetworkReadied; if (NetworkManager.Singleton == null) { return; } NetworkManager.Singleton.ConnectionApprovalCallback -= ApprovalCheck; NetworkManager.Singleton.OnServerStarted -= HandleServerStarted; } public PlayerData? GetPlayerData(ulong clientId) { if (clientIdToGuid.TryGetValue(clientId, out string clientGuid)) { if (clientData.TryGetValue(clientGuid, out PlayerData playerData)) { return playerData; } else { Debug.LogWarning($"No player data found for client id: {clientId}"); } } else { Debug.LogWarning($"No client guid found for client id: {clientId}"); } return null; } public void StartGame() { gameInProgress = true; runnersQuitEarly = 0; // Load the mountain level. Can add code to swap which level here NetworkSceneManager.SwitchScene("Game-Mountain"); } public void EndRound() { gameInProgress = false; // Remove any networked objects that might persist back to the Lobby //ObjectCleanup.Instance.DestroyPersistingNetObjectsServerRPC(); NetworkSceneManager.SwitchScene("Lobby"); } private void HandleNetworkReadied() { if (!NetworkManager.Singleton.IsServer) { return; } gameNetPortal.OnUserDisconnectRequested += HandleUserDisconnectRequested; NetworkManager.Singleton.OnClientDisconnectCallback += HandleClientDisconnect; gameNetPortal.OnClientSceneChanged += HandleClientSceneChanged; NetworkSceneManager.SwitchScene("Lobby"); if (NetworkManager.Singleton.IsHost) { clientSceneMap[NetworkManager.Singleton.LocalClientId] = SceneManager.GetActiveScene().buildIndex; } } private void HandleClientDisconnect(ulong clientId) { bool kingLeft = false; clientSceneMap.Remove(clientId); if (clientIdToGuid.TryGetValue(clientId, out string guid)) { clientIdToGuid.Remove(clientId); if (clientData[guid].ClientId == clientId) { // Check for who specifically discornnected (only if in game) if (gameInProgress == true) { // King left game early if (clientData[guid].IsKing) { kingLeft = true; } else { // Runner left early runnersQuitEarly++; } } clientData.Remove(guid); } } if (clientId == NetworkManager.Singleton.LocalClientId) { gameNetPortal.OnUserDisconnectRequested -= HandleUserDisconnectRequested; NetworkManager.Singleton.OnClientDisconnectCallback -= HandleClientDisconnect; gameNetPortal.OnClientSceneChanged -= HandleClientSceneChanged; } // Players Left Early and the game must be stopped early // - If the king leaves or if both runners leave if (kingLeft || runnersQuitEarly == 2) { EndRound(); } } private void HandleClientSceneChanged(ulong clientId, int sceneIndex) { clientSceneMap[clientId] = sceneIndex; } private void HandleUserDisconnectRequested() { HandleClientDisconnect(NetworkManager.Singleton.LocalClientId); NetworkManager.Singleton.StopHost(); ClearData(); SceneManager.LoadScene("TitleScene"); } private void HandleServerStarted() { if (!NetworkManager.Singleton.IsHost) { return; } string clientGuid = Guid.NewGuid().ToString(); string playerName = PlayerPrefs.GetString("PlayerName", "Missing Name"); clientData.Add(clientGuid, new PlayerData(playerName, NetworkManager.Singleton.LocalClientId)); clientIdToGuid.Add(NetworkManager.Singleton.LocalClientId, clientGuid); } private void ClearData() { clientData.Clear(); clientIdToGuid.Clear(); clientSceneMap.Clear(); gameInProgress = false; } private void ApprovalCheck(byte[] connectionData, ulong clientId, NetworkManager.ConnectionApprovedDelegate callback) { if (connectionData.Length > MaxConnectionPayload) { callback(false, 0, false, null, null); return; } string payload = Encoding.UTF8.GetString(connectionData); var connectionPayload = JsonUtility.FromJson(payload); ConnectStatus gameReturnStatus = ConnectStatus.Success; // This stops us from running multiple standalone builds since // they disconnect eachother when trying to join // // if (clientData.ContainsKey(connectionPayload.clientGUID)) // { // ulong oldClientId = clientData[connectionPayload.clientGUID].ClientId; // StartCoroutine(WaitToDisconnectClient(oldClientId, ConnectStatus.LoggedInAgain)); // } if (gameInProgress) { gameReturnStatus = ConnectStatus.GameInProgress; } else if (clientData.Count >= maxPlayers) { gameReturnStatus = ConnectStatus.ServerFull; } if (gameReturnStatus == ConnectStatus.Success) { clientSceneMap[clientId] = connectionPayload.clientScene; clientIdToGuid[clientId] = connectionPayload.clientGUID; clientData[connectionPayload.clientGUID] = new PlayerData(connectionPayload.playerName, clientId); } callback(false, 0, true, null, null); gameNetPortal.ServerToClientConnectResult(clientId, gameReturnStatus); if (gameReturnStatus != ConnectStatus.Success) { StartCoroutine(WaitToDisconnectClient(clientId, gameReturnStatus)); } } private IEnumerator WaitToDisconnectClient(ulong clientId, ConnectStatus reason) { gameNetPortal.ServerToClientSetDisconnectReason(clientId, reason); yield return new WaitForSeconds(0); KickClient(clientId); } private void KickClient(ulong clientId) { NetworkObject networkObject = NetworkSpawnManager.GetPlayerNetworkObject(clientId); if (networkObject != null) { networkObject.Despawn(true); } NetworkManager.Singleton.DisconnectClient(clientId); } }