TheKingsRace/Assets/Scripts/Network/ServerGameNetPortal.cs

266 lines
7.9 KiB
C#

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<string, PlayerData> clientData;
public Dictionary<ulong, string> clientIdToGuid;
private Dictionary<ulong, int> 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>();
gameNetPortal.OnNetworkReadied += HandleNetworkReadied;
NetworkManager.Singleton.ConnectionApprovalCallback += ApprovalCheck;
NetworkManager.Singleton.OnServerStarted += HandleServerStarted;
clientData = new Dictionary<string, PlayerData>();
clientIdToGuid = new Dictionary<ulong, string>();
clientSceneMap = new Dictionary<ulong, int>();
}
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<ConnectionPayload>(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);
}
}