400 lines
11 KiB
C#
400 lines
11 KiB
C#
using System;
|
|
using System.Linq;
|
|
using FishNet.Managing;
|
|
using FishNet.Object;
|
|
using FishNet.Transporting;
|
|
using FishNet.Transporting.Tugboat;
|
|
using FishySteamworks;
|
|
using Michsky.LSS;
|
|
using Michsky.UI.Heat;
|
|
using Obvious.Soap;
|
|
using QFSW.QC;
|
|
using UnityEngine;
|
|
using UnityEngine.SceneManagement;
|
|
|
|
public class MultiplayerManager : MonoBehaviour
|
|
{
|
|
public enum State
|
|
{
|
|
Offline = 0,
|
|
EnteringMap = 1,
|
|
EnteringSession = 2,
|
|
InSession = 3,
|
|
HostChange = 4,
|
|
LeavingSession = 5
|
|
}
|
|
|
|
[SerializeField]
|
|
private ScriptableLobbyDataVariable scriptable_variable_LobbyData;
|
|
|
|
[SerializeField]
|
|
private ScriptableEventNoParam onLobbyReadyClick;
|
|
|
|
[SerializeField]
|
|
private ScriptableEventNoParam onServerDisconnectClick;
|
|
|
|
[SerializeField]
|
|
private ScriptableEventNoParam OnConnectionLost;
|
|
|
|
[SerializeField]
|
|
private ScriptableEventNoParam OnConnectionTimeout;
|
|
|
|
[SerializeField]
|
|
private ScriptableEventNoParam OnLeavingServer;
|
|
|
|
[Tooltip("Time (in seconds) allowed for connection attempts before showing a warning popup")]
|
|
[SerializeField]
|
|
private float connectAttemptWarning = 10f;
|
|
|
|
[Tooltip("Time (in seconds) allowed for connection attempts before closing multiplayer and kicking the player")]
|
|
[SerializeField]
|
|
private float connectAttemptKick = 15f;
|
|
|
|
private State state;
|
|
|
|
private LocalConnectionState serverConnectionState;
|
|
|
|
private LocalConnectionState clientConnectionState;
|
|
|
|
private float connectAttemptTimer;
|
|
|
|
private bool connectAttemptWarningDisplayed;
|
|
|
|
private bool isDebugHostRunning;
|
|
|
|
private bool isDebugClientRunning;
|
|
|
|
private NetworkManager NetworkManager => NetworkManager.Instances[0];
|
|
|
|
public static event Action OnOffline;
|
|
|
|
public static event Action OnEnteringMap;
|
|
|
|
public static event Action OnEnteringSession;
|
|
|
|
public static event Action OnInSession;
|
|
|
|
public static event Action OnHostChange;
|
|
|
|
public static event Action OnLeaveingSession;
|
|
|
|
private void OnLobbyReadyClick_OnRaised()
|
|
{
|
|
if (state != State.Offline || !scriptable_variable_LobbyData.Value.IsValid)
|
|
{
|
|
return;
|
|
}
|
|
ChapterManager chapterManager = UnityEngine.Object.FindFirstObjectByType<ChapterManager>(FindObjectsInactive.Include);
|
|
if (!chapterManager)
|
|
{
|
|
return;
|
|
}
|
|
if (scriptable_variable_LobbyData.Value.GetMetadata().TryGetValue("Map", out var mapName) && !string.IsNullOrEmpty(mapName))
|
|
{
|
|
ChangeState(State.EnteringMap);
|
|
chapterManager.chapters.FirstOrDefault((ChapterManager.ChapterItem c) => c.chapterID == mapName)?.onPlay?.Invoke();
|
|
}
|
|
}
|
|
|
|
private void OnServerDisconnectClick_OnRaised()
|
|
{
|
|
if (state != State.Offline && state != State.LeavingSession)
|
|
{
|
|
if (scriptable_variable_LobbyData.Value.IsValid)
|
|
{
|
|
scriptable_variable_LobbyData.Value.Leave();
|
|
}
|
|
OnLeavingServer.Raise();
|
|
ChangeState(State.LeavingSession);
|
|
}
|
|
}
|
|
|
|
private void ServerManager_OnServerConnectionState(ServerConnectionStateArgs state)
|
|
{
|
|
serverConnectionState = state.ConnectionState;
|
|
}
|
|
|
|
private void ClientManager_OnClientConnectionState(ClientConnectionStateArgs state)
|
|
{
|
|
clientConnectionState = state.ConnectionState;
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
onLobbyReadyClick.OnRaised += OnLobbyReadyClick_OnRaised;
|
|
onServerDisconnectClick.OnRaised += OnServerDisconnectClick_OnRaised;
|
|
NetworkManager.ServerManager.OnServerConnectionState += ServerManager_OnServerConnectionState;
|
|
NetworkManager.ClientManager.OnClientConnectionState += ClientManager_OnClientConnectionState;
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
onLobbyReadyClick.OnRaised -= OnLobbyReadyClick_OnRaised;
|
|
onServerDisconnectClick.OnRaised -= OnServerDisconnectClick_OnRaised;
|
|
NetworkManager.ServerManager.OnServerConnectionState -= ServerManager_OnServerConnectionState;
|
|
NetworkManager.ClientManager.OnClientConnectionState -= ClientManager_OnClientConnectionState;
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
TransitionTo_Offline();
|
|
TransitionTo_EnteringSession();
|
|
SetHostAndClientConnection();
|
|
TransitionTo_InSession();
|
|
TransitionTo_HostChange();
|
|
TransitionTo_LeavingSession();
|
|
}
|
|
|
|
[Command("start-host", "Used to test multiplayer in two editor instances on the same machine.", Platform.AllPlatforms, MonoTargetType.Single)]
|
|
public void StartHost()
|
|
{
|
|
if (state == State.Offline && IsMapSceneLoaded() && IsServerFullyDisconnected() && IsClientFullyDisconnected() && !isDebugHostRunning && !isDebugClientRunning)
|
|
{
|
|
isDebugHostRunning = true;
|
|
global::FishySteamworks.FishySteamworks component = NetworkManager.GetComponent<global::FishySteamworks.FishySteamworks>();
|
|
Tugboat component2 = NetworkManager.GetComponent<Tugboat>();
|
|
component.enabled = false;
|
|
component2.enabled = true;
|
|
NetworkManager.TransportManager.Transport = component2;
|
|
NetworkManager.ServerManager.StartConnection();
|
|
NetworkManager.ClientManager.StartConnection();
|
|
ChangeState(State.EnteringSession);
|
|
}
|
|
}
|
|
|
|
[Command("start-client", "Used to test multiplayer in two editor instances on the same machine.", Platform.AllPlatforms, MonoTargetType.Single)]
|
|
public void StartClient()
|
|
{
|
|
if (state == State.Offline && IsMapSceneLoaded() && IsServerFullyDisconnected() && IsClientFullyDisconnected() && !isDebugHostRunning && !isDebugClientRunning)
|
|
{
|
|
isDebugClientRunning = true;
|
|
global::FishySteamworks.FishySteamworks component = NetworkManager.GetComponent<global::FishySteamworks.FishySteamworks>();
|
|
Tugboat component2 = NetworkManager.GetComponent<Tugboat>();
|
|
component.enabled = false;
|
|
component2.enabled = true;
|
|
NetworkManager.TransportManager.Transport = component2;
|
|
NetworkManager.ClientManager.StartConnection();
|
|
ChangeState(State.EnteringSession);
|
|
}
|
|
}
|
|
|
|
[Command("stop-host-client", "Used to test multiplayer in two editor instances on the same machine.", Platform.AllPlatforms, MonoTargetType.Single)]
|
|
public void StopHostClient()
|
|
{
|
|
if (state == State.InSession && (isDebugHostRunning || isDebugClientRunning))
|
|
{
|
|
isDebugHostRunning = false;
|
|
isDebugClientRunning = false;
|
|
if (NetworkManager.IsServerStarted)
|
|
{
|
|
NetworkManager.ServerManager.StopConnection(sendDisconnectMessage: true);
|
|
}
|
|
if (NetworkManager.IsClientStarted)
|
|
{
|
|
NetworkManager.ClientManager.StopConnection();
|
|
}
|
|
ChangeState(State.Offline);
|
|
}
|
|
}
|
|
|
|
private void TransitionTo_Offline()
|
|
{
|
|
if (state == State.LeavingSession)
|
|
{
|
|
StopFishNet();
|
|
if (scriptable_variable_LobbyData.Value.IsValid)
|
|
{
|
|
scriptable_variable_LobbyData.Value.Leave();
|
|
}
|
|
connectAttemptTimer = 0f;
|
|
connectAttemptWarningDisplayed = false;
|
|
ChangeState(State.Offline);
|
|
}
|
|
}
|
|
|
|
private void TransitionTo_EnteringSession()
|
|
{
|
|
if (state == State.EnteringMap && IsMapSceneLoaded())
|
|
{
|
|
ChangeState(State.EnteringSession);
|
|
}
|
|
}
|
|
|
|
private void SetHostAndClientConnection()
|
|
{
|
|
if ((state == State.EnteringSession || state == State.HostChange) && IsMapSceneLoaded() && scriptable_variable_LobbyData.Value.IsValid && IsServerFullyDisconnected() && IsClientFullyDisconnected())
|
|
{
|
|
NetworkManager.GetComponent<global::FishySteamworks.FishySteamworks>().SetClientAddress(scriptable_variable_LobbyData.Value.Owner.user.id.ToString());
|
|
Debug.Log("(MultiplayerManager) Starting connection to host ID: " + scriptable_variable_LobbyData.Value.Owner.user.id.ToString());
|
|
if (scriptable_variable_LobbyData.Value.IsOwner)
|
|
{
|
|
NetworkManager.ServerManager.StartConnection();
|
|
NetworkManager.ClientManager.StartConnection();
|
|
}
|
|
else
|
|
{
|
|
NetworkManager.ClientManager.StartConnection();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void TransitionTo_InSession()
|
|
{
|
|
if ((state != State.EnteringSession && state != State.HostChange) || !IsMapSceneLoaded())
|
|
{
|
|
return;
|
|
}
|
|
bool num = NetworkManager.IsClientStarted && IsClientFullyConnected();
|
|
bool flag = !NetworkManager.IsServerStarted || IsServerFullyConnected();
|
|
if (num && flag)
|
|
{
|
|
NetworkObject firstObject = NetworkManager.ClientManager.Connection.FirstObject;
|
|
if (firstObject != null && firstObject.TryGetComponent<FishNetSpawnerProxy>(out var component) && component.ClientStarted)
|
|
{
|
|
ChangeState(State.InSession);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void TransitionTo_HostChange()
|
|
{
|
|
if (state == State.InSession && !IsClientFullyConnected())
|
|
{
|
|
StopFishNet();
|
|
ChangeState(State.HostChange);
|
|
}
|
|
}
|
|
|
|
private void TransitionTo_LeavingSession()
|
|
{
|
|
if (state == State.EnteringSession || state == State.HostChange)
|
|
{
|
|
connectAttemptTimer += Time.deltaTime;
|
|
}
|
|
else
|
|
{
|
|
connectAttemptTimer = 0f;
|
|
}
|
|
if (state == State.EnteringSession || state == State.InSession || state == State.HostChange)
|
|
{
|
|
if (connectAttemptTimer >= connectAttemptWarning && !connectAttemptWarningDisplayed)
|
|
{
|
|
OnConnectionTimeout.Raise();
|
|
connectAttemptWarningDisplayed = true;
|
|
}
|
|
else if (connectAttemptTimer >= connectAttemptKick)
|
|
{
|
|
OnConnectionLost.Raise();
|
|
ChangeState(State.LeavingSession);
|
|
}
|
|
else if (!IsMapSceneLoaded() || IsLobbyInvalid())
|
|
{
|
|
OnConnectionLost.Raise();
|
|
ChangeState(State.LeavingSession);
|
|
}
|
|
}
|
|
bool IsLobbyInvalid()
|
|
{
|
|
if (!scriptable_variable_LobbyData.Value.IsValid && !isDebugHostRunning)
|
|
{
|
|
return !isDebugClientRunning;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private bool IsMapSceneLoaded()
|
|
{
|
|
bool result = false;
|
|
Scene activeScene = SceneManager.GetActiveScene();
|
|
if (LSS_LoadingScreen.instance == null && activeScene.isLoaded && !activeScene.name.Equals("Main Menu") && !activeScene.name.Equals("Gameplay"))
|
|
{
|
|
result = true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private bool IsServerFullyDisconnected()
|
|
{
|
|
if (!NetworkManager.IsServerStarted)
|
|
{
|
|
if (serverConnectionState != 0)
|
|
{
|
|
return serverConnectionState == LocalConnectionState.Stopped;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool IsServerFullyConnected()
|
|
{
|
|
if (NetworkManager.IsServerStarted)
|
|
{
|
|
return serverConnectionState == LocalConnectionState.Started;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool IsClientFullyDisconnected()
|
|
{
|
|
if (!NetworkManager.IsClientStarted)
|
|
{
|
|
if (clientConnectionState != 0)
|
|
{
|
|
return clientConnectionState == LocalConnectionState.Stopped;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool IsClientFullyConnected()
|
|
{
|
|
if (NetworkManager.IsClientStarted)
|
|
{
|
|
return clientConnectionState == LocalConnectionState.Started;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private void StopFishNet()
|
|
{
|
|
if (NetworkManager.IsClientStarted)
|
|
{
|
|
NetworkManager.ClientManager.StopConnection();
|
|
}
|
|
if (NetworkManager.IsServerStarted)
|
|
{
|
|
NetworkManager.ServerManager.StopConnection(sendDisconnectMessage: true);
|
|
}
|
|
}
|
|
|
|
private void ChangeState(State newState)
|
|
{
|
|
Debug.Log($"(MultiplayerManager) Change state to {newState}");
|
|
state = newState;
|
|
switch (state)
|
|
{
|
|
case State.Offline:
|
|
MultiplayerManager.OnOffline?.Invoke();
|
|
break;
|
|
case State.EnteringMap:
|
|
MultiplayerManager.OnEnteringMap?.Invoke();
|
|
break;
|
|
case State.EnteringSession:
|
|
MultiplayerManager.OnEnteringSession?.Invoke();
|
|
break;
|
|
case State.InSession:
|
|
MultiplayerManager.OnInSession?.Invoke();
|
|
break;
|
|
case State.HostChange:
|
|
MultiplayerManager.OnHostChange?.Invoke();
|
|
break;
|
|
case State.LeavingSession:
|
|
MultiplayerManager.OnLeaveingSession?.Invoke();
|
|
break;
|
|
}
|
|
}
|
|
}
|