981 lines
25 KiB
C#
981 lines
25 KiB
C#
#define ENABLE_LOGS
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Threading.Tasks;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.InputSystem;
|
|
using UnityEngine.SceneManagement;
|
|
using Fusion;
|
|
using Fusion.Plugin;
|
|
using Fusion.Sockets;
|
|
|
|
using UnityScene = UnityEngine.SceneManagement.Scene;
|
|
|
|
namespace TPSBR
|
|
{
|
|
public struct SessionRequest
|
|
{
|
|
public string UserID;
|
|
public GameMode GameMode;
|
|
public string DisplayName;
|
|
public string SessionName;
|
|
public string ScenePath;
|
|
public EGameplayType GameplayType;
|
|
public int MaxPlayers;
|
|
public int ExtraPeers;
|
|
public string CustomLobby;
|
|
public string IPAddress;
|
|
public ushort Port;
|
|
}
|
|
|
|
// This Networking class is complex due to handling of reconnection of extra peers that we used for initial batch testing.
|
|
// We suggest to use standard approach (inheriting from NetworkSceneManagerBase for custom loading
|
|
// functionality and directly starting via NetworkRunner) for your game unless such functionality is needed.
|
|
public class Networking : MonoBehaviour
|
|
{
|
|
// CONSTANTS
|
|
|
|
public const string DISPLAY_NAME_KEY = "name";
|
|
public const string MAP_KEY = "map";
|
|
public const string TYPE_KEY = "type";
|
|
public const string MODE_KEY = "mode";
|
|
public const string STATUS_SERVER_CLOSED = "Server Closed";
|
|
|
|
// PUBLIC MEMBERS
|
|
|
|
public string Status { get; private set; }
|
|
public string StatusDescription { get; private set; }
|
|
public string ErrorStatus { get; private set; }
|
|
|
|
public bool HasSession => _pendingSession != null || _currentSession != null;
|
|
public bool IsConnecting => _pendingSession != null || _currentSession.IsConnected == false;
|
|
public bool IsConnected => _currentSession != null && _pendingSession == null && _currentSession.IsConnected == true;
|
|
|
|
public int PeerCount => _currentSession != null ? _currentSession.GamePeers.SafeCount() : 0;
|
|
|
|
// PRIVATE MEMBERS
|
|
|
|
private Session _pendingSession;
|
|
private Session _currentSession;
|
|
private bool _stopGameOnDisconnect;
|
|
private string _loadingScene;
|
|
private Coroutine _coroutine;
|
|
|
|
// PUBLIC METHODS
|
|
|
|
public void StartGame(SessionRequest request)
|
|
{
|
|
var session = new Session();
|
|
|
|
if (request.ExtraPeers > 0 && NetworkProjectConfig.Global.PeerMode == NetworkProjectConfig.PeerModes.Single)
|
|
{
|
|
Debug.LogError("Cannot start with multiple peers. PeerMode is set to Single.");
|
|
request.ExtraPeers = 0;
|
|
}
|
|
|
|
SceneRef sceneRef = default;
|
|
|
|
int sceneIndex = SceneUtility.GetBuildIndexByScenePath(request.ScenePath);
|
|
if (sceneIndex >= 0)
|
|
{
|
|
sceneRef = SceneRef.FromIndex(sceneIndex);
|
|
}
|
|
|
|
int totalPeers = 1 + request.ExtraPeers;
|
|
session.GamePeers = new GamePeer[totalPeers];
|
|
|
|
NetworkSceneInfo sceneInfo = new NetworkSceneInfo();
|
|
|
|
if (sceneRef.IsValid == true)
|
|
{
|
|
sceneInfo.AddSceneRef(sceneRef, LoadSceneMode.Additive, LocalPhysicsMode.None, true);
|
|
}
|
|
|
|
for (int i = 0; i < totalPeers; i++)
|
|
{
|
|
session.GamePeers[i] = new GamePeer(i)
|
|
{
|
|
UserID = i == 0 ? request.UserID : $"{request.UserID}.{i}",
|
|
Scene = sceneInfo,
|
|
GameMode = i == 0 ? request.GameMode : GameMode.Client,
|
|
Request = request,
|
|
};
|
|
}
|
|
|
|
session.ConnectionRequested = true;
|
|
|
|
_pendingSession = session;
|
|
_stopGameOnDisconnect = false;
|
|
|
|
ErrorStatus = null;
|
|
|
|
Log($"StartGame() UserID:{request.UserID} GameMode:{request.GameMode} DisplayName:{request.DisplayName} SessionName:{request.SessionName} ScenePath:{request.ScenePath} GameplayType:{request.GameplayType} MaxPlayers:{request.MaxPlayers} ExtraPeers:{request.ExtraPeers} CustomLobby:{request.CustomLobby}");
|
|
}
|
|
|
|
public void StopGame(string errorStatus = null)
|
|
{
|
|
Log($"StopGame()");
|
|
|
|
_pendingSession = null;
|
|
_stopGameOnDisconnect = false;
|
|
|
|
if (_currentSession != null)
|
|
{
|
|
_currentSession.ConnectionRequested = false;
|
|
}
|
|
|
|
ErrorStatus = errorStatus;
|
|
}
|
|
|
|
public void StopGameOnDisconnect()
|
|
{
|
|
Log($"StopGameOnDisconnect()");
|
|
|
|
_stopGameOnDisconnect = true;
|
|
}
|
|
|
|
public void ClearErrorStatus()
|
|
{
|
|
ErrorStatus = null;
|
|
}
|
|
|
|
// MONOBEHAVIOUR
|
|
|
|
protected void Awake()
|
|
{
|
|
_loadingScene = Global.Settings.LoadingScene;
|
|
}
|
|
|
|
protected void Update()
|
|
{
|
|
if (_pendingSession != null)
|
|
{
|
|
if (_currentSession == null)
|
|
{
|
|
_currentSession = _pendingSession;
|
|
_pendingSession = null;
|
|
}
|
|
else
|
|
{
|
|
// Request end of current session
|
|
_currentSession.ConnectionRequested = false;
|
|
}
|
|
}
|
|
|
|
UpdateCurrentSession();
|
|
|
|
// Check if current session finished
|
|
if (_coroutine == null && _currentSession != null && _currentSession.IsConnected == false)
|
|
{
|
|
if (_pendingSession == null)
|
|
{
|
|
Log($"Starting LoadMenuCoroutine()");
|
|
// Current session is finished and there is no pending session, let's go to menu
|
|
_coroutine = StartCoroutine(LoadMenuCoroutine());
|
|
}
|
|
|
|
_currentSession = null;
|
|
}
|
|
}
|
|
|
|
// PRIVATE MEMBERS
|
|
|
|
public void UpdateCurrentSession()
|
|
{
|
|
if (_currentSession == null)
|
|
{
|
|
Status = string.Empty;
|
|
StatusDescription = string.Empty;
|
|
return;
|
|
}
|
|
|
|
if (_coroutine != null)
|
|
return;
|
|
|
|
var peers = _currentSession.GamePeers;
|
|
|
|
if (_stopGameOnDisconnect == true)
|
|
{
|
|
for (int i = 0; i < peers.Length; i++)
|
|
{
|
|
if (_currentSession.ConnectionRequested == true && peers[i].IsConnected == false)
|
|
{
|
|
Log($"Stopping game after disconnect");
|
|
_stopGameOnDisconnect = false;
|
|
StopGame();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < peers.Length; i++)
|
|
{
|
|
var peer = peers[i];
|
|
bool isConnected = peer.IsConnected;
|
|
|
|
if (_currentSession.ConnectionRequested == true && peer.Loaded == false && isConnected == false && peer.CanConnect == true)
|
|
{
|
|
// First connect or reconnect after failed connect
|
|
|
|
Status = peer.WasConnected == false ? "Starting" : "Reconnecting";
|
|
Log($"Starting ConnectPeerCoroutine() - {Status} - Peer {peer.ID}");
|
|
_coroutine = StartCoroutine(ConnectPeerCoroutine(peer));
|
|
return;
|
|
}
|
|
else if (_currentSession.ConnectionRequested == false && (isConnected == true || peer.Loaded == true))
|
|
{
|
|
// Disconnect requested
|
|
|
|
Status = "Quitting";
|
|
Log($"Starting DisconnectPeerCoroutine() - {Status} - Peer {peer.ID}");
|
|
_coroutine = StartCoroutine(DisconnectPeerCoroutine(peer));
|
|
return;
|
|
}
|
|
else if (peer.Loaded == true && isConnected == false)
|
|
{
|
|
// Connection lost
|
|
|
|
Status = "Connection Lost";
|
|
Log($"Starting DisconnectPeerCoroutine() - {Status} - Peer {peer.ID}");
|
|
_coroutine = StartCoroutine(DisconnectPeerCoroutine(peer));
|
|
return;
|
|
}
|
|
}
|
|
|
|
UpdatePeerSwitch(_currentSession.GamePeers);
|
|
ValidateMultiPeers(_currentSession.GamePeers);
|
|
}
|
|
|
|
private IEnumerator ConnectPeerCoroutine(GamePeer peer, float connectionTimeout = 10f, float loadTimeout = 45f)
|
|
{
|
|
peer.Loaded = true;
|
|
|
|
if (peer.WasConnected == true)
|
|
{
|
|
peer.ReconnectionTries--;
|
|
}
|
|
else
|
|
{
|
|
peer.ConnectionTries--;
|
|
}
|
|
|
|
StatusDescription = "Unloading current scene";
|
|
|
|
UnityScene activeScene = SceneManager.GetActiveScene();
|
|
|
|
if (IsSameScene(activeScene.path, peer.Request.ScenePath) == false && activeScene.name != _loadingScene)
|
|
{
|
|
Log($"Show loading scene");
|
|
yield return ShowLoadingSceneCoroutine(true);
|
|
|
|
bool unloadScene = true;
|
|
|
|
for (int i = 0; i < _currentSession.GamePeers.Length; ++i)
|
|
{
|
|
if (activeScene == _currentSession.GamePeers[i].LoadedScene)
|
|
{
|
|
unloadScene = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (unloadScene == true)
|
|
{
|
|
Scene currentScene = activeScene.GetComponent<Scene>();
|
|
if (currentScene != null)
|
|
{
|
|
Log($"Deinitializing Scene");
|
|
currentScene.Deinitialize();
|
|
}
|
|
|
|
Log($"Unloading scene {activeScene.name}");
|
|
yield return SceneManager.UnloadSceneAsync(activeScene);
|
|
yield return null;
|
|
}
|
|
}
|
|
|
|
float baseTime = Time.realtimeSinceStartup;
|
|
float limitTime = baseTime + connectionTimeout;
|
|
string peerName = $"{peer.GameMode}#{peer.ID}";
|
|
|
|
Debug.LogWarning($"Starting {peerName} ...");
|
|
StatusDescription = "Starting network connection";
|
|
|
|
yield return null;
|
|
|
|
NetworkObjectPool pool = new NetworkObjectPool();
|
|
|
|
NetworkRunner runner = Instantiate(Global.Settings.RunnerPrefab);
|
|
runner.name = peerName;
|
|
|
|
runner.EnableVisibilityExtension();
|
|
|
|
peer.Runner = runner;
|
|
peer.SceneManager = runner.GetComponent<NetworkSceneManager>();
|
|
peer.LoadedScene = default;
|
|
|
|
StartGameArgs startGameArgs = new StartGameArgs();
|
|
startGameArgs.GameMode = peer.GameMode;
|
|
startGameArgs.SessionName = peer.Request.SessionName;
|
|
startGameArgs.Scene = peer.Scene;
|
|
startGameArgs.OnGameStarted = OnGamePeerInitialized;
|
|
startGameArgs.ObjectProvider = pool;
|
|
startGameArgs.CustomLobbyName = peer.Request.CustomLobby;
|
|
startGameArgs.SceneManager = peer.SceneManager;
|
|
startGameArgs.EnableClientSessionCreation = false;
|
|
|
|
if (peer.Request.MaxPlayers > 0)
|
|
{
|
|
startGameArgs.PlayerCount = peer.Request.MaxPlayers;
|
|
}
|
|
|
|
if (peer.GameMode == GameMode.Server || peer.GameMode == GameMode.Host)
|
|
{
|
|
startGameArgs.SessionProperties = CreateSessionProperties(peer.Request);
|
|
}
|
|
|
|
if (peer.Request.IPAddress.HasValue() == true)
|
|
{
|
|
startGameArgs.Address = NetAddress.CreateFromIpPort(peer.Request.IPAddress, peer.Request.Port);
|
|
}
|
|
else if (peer.Request.Port > 0)
|
|
{
|
|
startGameArgs.Address = NetAddress.Any(peer.Request.Port);
|
|
}
|
|
|
|
Log($"NetworkRunner.StartGame()");
|
|
var startGameTask = runner.StartGame(startGameArgs);
|
|
|
|
while (startGameTask.IsCompleted == false)
|
|
{
|
|
yield return null;
|
|
|
|
if (Time.realtimeSinceStartup >= limitTime)
|
|
{
|
|
Debug.LogError($"{peerName} start timeout! IsCompleted: {startGameTask.IsCompleted} IsCanceled: {startGameTask.IsCanceled} IsFaulted: {startGameTask.IsFaulted}");
|
|
break;
|
|
}
|
|
|
|
if (_currentSession.ConnectionRequested == false)
|
|
{
|
|
Log($"Stopping coroutine (requested by user)");
|
|
// Stop requested by user
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (startGameTask.IsCanceled == true || startGameTask.IsFaulted == true || startGameTask.IsCompleted == false)
|
|
{
|
|
Debug.LogError($"{peerName} failed to start!");
|
|
|
|
Log($"Starting DisconnectPeerCoroutine() - Peer {peer.ID}");
|
|
yield return DisconnectPeerCoroutine(peer);
|
|
|
|
_coroutine = null;
|
|
yield break;
|
|
}
|
|
|
|
var result = startGameTask.Result;
|
|
|
|
Log($"StartGame() Result: {result.ToString()} - Peer {peer.ID}");
|
|
|
|
if (result.Ok == false)
|
|
{
|
|
Debug.LogError($"{peerName} failed to start! Result: {result}");
|
|
|
|
// Probably incorrect start game parameters, go back to menu immediately
|
|
if (Application.isBatchMode == false)
|
|
{
|
|
StopGame();
|
|
}
|
|
|
|
if (peer.WasConnected == true && result.ShutdownReason == ShutdownReason.GameNotFound)
|
|
{
|
|
ErrorStatus = STATUS_SERVER_CLOSED;
|
|
}
|
|
else
|
|
{
|
|
ErrorStatus = StringToLabel(result.ShutdownReason.ToString());
|
|
}
|
|
|
|
Log($"Starting DisconnectPeerCoroutine() - Peer {peer.ID}");
|
|
yield return DisconnectPeerCoroutine(peer);
|
|
|
|
_coroutine = null;
|
|
yield break;
|
|
}
|
|
|
|
limitTime += loadTimeout;
|
|
|
|
Log($"Waiting for connection - Peer {peer.ID}");
|
|
StatusDescription = "Waiting for server connection";
|
|
|
|
while (peer.IsConnected == false)
|
|
{
|
|
yield return null;
|
|
|
|
if (Time.realtimeSinceStartup >= limitTime)
|
|
{
|
|
Debug.LogError($"{peerName} start timeout! IsCloudReady: {runner.IsCloudReady} IsRunning: {runner.IsRunning}");
|
|
|
|
Log($"Starting DisconnectPeerCoroutine() - Peer {peer.ID}");
|
|
yield return DisconnectPeerCoroutine(peer);
|
|
|
|
_coroutine = null;
|
|
yield break;
|
|
}
|
|
}
|
|
|
|
Log($"Loading gameplay scene - Peer {peer.ID}");
|
|
StatusDescription = "Loading gameplay scene";
|
|
|
|
while (runner.SimulationUnityScene.IsValid() == false || runner.SimulationUnityScene.isLoaded == false)
|
|
{
|
|
Log($"Waiting for NetworkRunner.SimulationUnityScene - Peer {peer.ID}");
|
|
yield return null;
|
|
|
|
if (Time.realtimeSinceStartup >= limitTime)
|
|
{
|
|
Debug.LogError($"{peerName} scene load timeout!");
|
|
|
|
Log($"Starting DisconnectPeerCoroutine() - Peer {peer.ID}");
|
|
yield return DisconnectPeerCoroutine(peer);
|
|
|
|
_coroutine = null;
|
|
yield break;
|
|
}
|
|
}
|
|
|
|
Debug.LogWarning($"{peerName} started in {(Time.realtimeSinceStartup - baseTime):0.00}s");
|
|
|
|
peer.LoadedScene = runner.SimulationUnityScene;
|
|
|
|
if (peer.ID == 0)
|
|
{
|
|
SceneManager.SetActiveScene(peer.LoadedScene);
|
|
}
|
|
|
|
StatusDescription = "Waiting for gameplay scene load";
|
|
|
|
var scene = peer.SceneManager.GameplayScene;
|
|
while (scene == null)
|
|
{
|
|
Log($"Waiting for GameplayScene - Peer {peer.ID}");
|
|
|
|
yield return null;
|
|
|
|
scene = peer.SceneManager.GameplayScene;
|
|
|
|
if (Time.realtimeSinceStartup >= limitTime)
|
|
{
|
|
Debug.LogError($"{peerName} GameplayScene query timeout!");
|
|
|
|
Log($"Starting DisconnectPeerCoroutine() - Peer {peer.ID}");
|
|
yield return DisconnectPeerCoroutine(peer);
|
|
|
|
_coroutine = null;
|
|
yield break;
|
|
}
|
|
}
|
|
|
|
Log($"Scene.PrepareContext() - Peer {peer.ID}");
|
|
scene.PrepareContext();
|
|
|
|
var sceneContext = scene.Context;
|
|
sceneContext.IsVisible = peer.ID == 0;
|
|
sceneContext.HasInput = peer.ID == 0;
|
|
sceneContext.Runner = peer.Runner;
|
|
sceneContext.PeerUserID = peer.UserID;
|
|
|
|
peer.Context = sceneContext;
|
|
pool.Context = sceneContext;
|
|
|
|
StatusDescription = "Waiting for networked game";
|
|
|
|
var networkGame = scene.GetComponentInChildren<NetworkGame>(true);
|
|
|
|
while (networkGame.Object == null)
|
|
{
|
|
Log($"Waiting for NetworkGame - Peer {peer.ID}");
|
|
|
|
yield return null;
|
|
|
|
if (Time.realtimeSinceStartup >= limitTime)
|
|
{
|
|
Debug.LogError($"{peerName} start timeout! Network game not started properly.");
|
|
|
|
Log($"Starting DisconnectPeerCoroutine() - Peer {peer.ID}");
|
|
yield return DisconnectPeerCoroutine(peer);
|
|
|
|
_coroutine = null;
|
|
yield break;
|
|
}
|
|
|
|
if (_currentSession.ConnectionRequested == false)
|
|
{
|
|
// Stop requested by user
|
|
Log($"Starting DisconnectPeerCoroutine() - Connection is not requested anymore - Peer {peer.ID}");
|
|
yield return DisconnectPeerCoroutine(peer);
|
|
|
|
_coroutine = null;
|
|
yield break;
|
|
}
|
|
}
|
|
|
|
StatusDescription = "Waiting for gameplay load";
|
|
|
|
Log($"NetworkGame.Initialize() - Peer {peer.ID}");
|
|
networkGame.Initialize(peer.Request.GameplayType);
|
|
|
|
while (scene.Context.GameplayMode == null)
|
|
{
|
|
Log($"Waiting for GameplayMode - Peer {peer.ID}");
|
|
|
|
yield return null;
|
|
|
|
if (Time.realtimeSinceStartup >= limitTime)
|
|
{
|
|
Debug.LogError($"{peerName} start timeout! Gameplay mode not started properly.");
|
|
|
|
Log($"Starting DisconnectPeerCoroutine() - Peer {peer.ID}");
|
|
yield return DisconnectPeerCoroutine(peer);
|
|
|
|
_coroutine = null;
|
|
yield break;
|
|
}
|
|
}
|
|
|
|
StatusDescription = "Activating scene";
|
|
|
|
Log($"Scene.Initialize() - Peer {peer.ID}");
|
|
scene.Initialize();
|
|
|
|
Log($"Scene.Activate() - Peer {peer.ID}");
|
|
yield return scene.Activate();
|
|
|
|
StatusDescription = "Activating network game";
|
|
|
|
Log($"NetworkGame.Activate() - Peer {peer.ID}");
|
|
networkGame.Activate();
|
|
|
|
if (SceneManager.GetSceneByName(_loadingScene).IsValid() == true)
|
|
{
|
|
// Wait a little bit for scene activation before showing it
|
|
yield return new WaitForSeconds(1f);
|
|
|
|
Log($"Hide loading scene");
|
|
yield return ShowLoadingSceneCoroutine(false);
|
|
}
|
|
|
|
if (peer.WasConnected == true)
|
|
{
|
|
peer.ReconnectionTries++;
|
|
}
|
|
|
|
peer.WasConnected = true;
|
|
|
|
_coroutine = null;
|
|
|
|
Log($"ConnectPeerCoroutine() finished");
|
|
}
|
|
|
|
private IEnumerator DisconnectPeerCoroutine(GamePeer peer)
|
|
{
|
|
StatusDescription = "Disconnecting from server";
|
|
|
|
UnityScene gameplayScene = default;
|
|
|
|
try
|
|
{
|
|
if (peer.Runner != null)
|
|
{
|
|
// Possible exception when runner tries to read config
|
|
gameplayScene = peer.Runner.SimulationUnityScene;
|
|
|
|
// Close and hide the room
|
|
if (peer.Runner.IsServer == true && peer.Runner.SessionInfo != null)
|
|
{
|
|
Log($"Closing the room");
|
|
peer.Runner.SessionInfo.IsOpen = false;
|
|
peer.Runner.SessionInfo.IsVisible = false;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
Debug.LogException(exception);
|
|
}
|
|
|
|
if (gameplayScene.IsValid() == false)
|
|
{
|
|
gameplayScene = peer.LoadedScene;
|
|
}
|
|
|
|
if (gameplayScene.IsValid() == true)
|
|
{
|
|
Scene scene = gameplayScene.GetComponent<Scene>(true);
|
|
if (scene != null)
|
|
{
|
|
try
|
|
{
|
|
Log($"Deinitializing Scene");
|
|
scene.Deinitialize();
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
Debug.LogException(exception);
|
|
}
|
|
}
|
|
}
|
|
|
|
Task shutdownTask = null;
|
|
|
|
if (peer.Runner != null)
|
|
{
|
|
Debug.LogWarning($"Shutdown {peer.Runner.name} ...");
|
|
|
|
try
|
|
{
|
|
shutdownTask = peer.Runner.Shutdown(true);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
Debug.LogException(exception);
|
|
}
|
|
}
|
|
|
|
Log($"Show loading scene");
|
|
yield return ShowLoadingSceneCoroutine(true);
|
|
|
|
if (shutdownTask != null)
|
|
{
|
|
float operationTimeout = 10.0f;
|
|
while (operationTimeout > 0.0f && shutdownTask.IsCompleted == false)
|
|
{
|
|
yield return null;
|
|
operationTimeout -= Time.unscaledDeltaTime;
|
|
}
|
|
}
|
|
|
|
StatusDescription = "Unloading gameplay scene";
|
|
|
|
yield return null;
|
|
|
|
if (gameplayScene.IsValid() == true)
|
|
{
|
|
Debug.LogWarning($"Unloading scene {gameplayScene.name}");
|
|
|
|
yield return SceneManager.UnloadSceneAsync(gameplayScene);
|
|
yield return null;
|
|
}
|
|
|
|
peer.Loaded = default;
|
|
peer.Runner = default;
|
|
peer.SceneManager = default;
|
|
peer.LoadedScene = default;
|
|
|
|
_coroutine = null;
|
|
|
|
Log($"DisconnectPeerCoroutine() finished");
|
|
}
|
|
|
|
private IEnumerator ShowLoadingSceneCoroutine(bool show, float additionalTime = 1f)
|
|
{
|
|
var loadingScene = SceneManager.GetSceneByName(_loadingScene);
|
|
|
|
if (loadingScene.IsValid() == false)
|
|
{
|
|
yield return SceneManager.LoadSceneAsync(_loadingScene, LoadSceneMode.Additive);
|
|
loadingScene = SceneManager.GetSceneByName(_loadingScene);
|
|
}
|
|
|
|
if (show == false && additionalTime > 0f)
|
|
{
|
|
// Wait additional time till fade out starts
|
|
yield return new WaitForSeconds(additionalTime);
|
|
}
|
|
|
|
yield return null;
|
|
|
|
var loadingSceneObject = loadingScene.GetComponent<LoadingScene>();
|
|
if (loadingSceneObject != null)
|
|
{
|
|
if (show == true)
|
|
{
|
|
loadingSceneObject.FadeIn();
|
|
}
|
|
else
|
|
{
|
|
loadingSceneObject.FadeOut();
|
|
}
|
|
|
|
while (loadingSceneObject.IsFading == true)
|
|
yield return null;
|
|
}
|
|
|
|
if (show == true && additionalTime > 0f)
|
|
{
|
|
// Wait additional time after fade in
|
|
yield return new WaitForSeconds(additionalTime);
|
|
}
|
|
|
|
if (show == false)
|
|
{
|
|
yield return SceneManager.UnloadSceneAsync(loadingScene);
|
|
}
|
|
}
|
|
|
|
private IEnumerator LoadMenuCoroutine()
|
|
{
|
|
string menuSceneName = Global.Settings.MenuScene;
|
|
|
|
if (SceneManager.sceneCount == 1 && SceneManager.GetSceneAt(0).name == menuSceneName)
|
|
{
|
|
_coroutine = null;
|
|
yield break;
|
|
}
|
|
|
|
StatusDescription = "Unloading gameplay scenes";
|
|
|
|
yield return ShowLoadingSceneCoroutine(true);
|
|
|
|
for (int i = SceneManager.sceneCount - 1; i >= 0; --i)
|
|
{
|
|
var scene = SceneManager.GetSceneAt(i);
|
|
|
|
if (scene.name != _loadingScene)
|
|
{
|
|
yield return SceneManager.UnloadSceneAsync(scene);
|
|
}
|
|
}
|
|
|
|
StatusDescription = "Loading menu scene";
|
|
yield return null;
|
|
|
|
yield return SceneManager.LoadSceneAsync(menuSceneName, LoadSceneMode.Additive);
|
|
yield return ShowLoadingSceneCoroutine(false);
|
|
|
|
SceneManager.SetActiveScene(SceneManager.GetSceneByName(menuSceneName));
|
|
|
|
_coroutine = null;
|
|
}
|
|
|
|
private void OnGamePeerInitialized(NetworkRunner runner)
|
|
{
|
|
if (NetworkProjectConfig.Global.PeerMode != NetworkProjectConfig.PeerModes.Multiple)
|
|
return;
|
|
|
|
Camera camera = runner.SimulationUnityScene.FindMainCamera();
|
|
if (camera != null)
|
|
{
|
|
camera.gameObject.SetActive(false);
|
|
}
|
|
|
|
EventSystem eventSystem = runner.SimulationUnityScene.GetComponent<EventSystem>(true);
|
|
if (eventSystem != null)
|
|
{
|
|
eventSystem.gameObject.SetActive(false);
|
|
}
|
|
}
|
|
|
|
private void UpdatePeerSwitch(GamePeer[] peers)
|
|
{
|
|
int newID = -1;
|
|
bool showOthers = false;
|
|
|
|
bool canSwitchPeer = Application.isEditor == true ? true : Keyboard.current.leftCtrlKey.isPressed == true && Keyboard.current.leftShiftKey.isPressed == true;
|
|
if (canSwitchPeer == true)
|
|
{
|
|
if (Keyboard.current.numpad1Key.wasPressedThisFrame == true)
|
|
{
|
|
newID = 0;
|
|
}
|
|
else if (Keyboard.current.numpad2Key.wasPressedThisFrame == true)
|
|
{
|
|
newID = 1;
|
|
}
|
|
else if (Keyboard.current.numpad3Key.wasPressedThisFrame == true)
|
|
{
|
|
newID = 2;
|
|
}
|
|
else if (Keyboard.current.numpad4Key.wasPressedThisFrame == true)
|
|
{
|
|
newID = 0;
|
|
showOthers = true;
|
|
}
|
|
else if (Keyboard.current.numpad5Key.wasPressedThisFrame == true)
|
|
{
|
|
newID = 1;
|
|
showOthers = true;
|
|
}
|
|
else if (Keyboard.current.numpad6Key.wasPressedThisFrame == true)
|
|
{
|
|
newID = 2;
|
|
showOthers = true;
|
|
}
|
|
}
|
|
|
|
if (newID >= 0 && newID < peers.Length)
|
|
{
|
|
for (int i = 0; i < peers.Length; i++)
|
|
{
|
|
GamePeer peer = peers[i];
|
|
|
|
peer.Context.HasInput = peer.ID == newID;
|
|
peer.Context.IsVisible = peer.ID == newID || showOthers == true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ValidateMultiPeers(GamePeer[] peers)
|
|
{
|
|
if (peers.SafeCount() <= 0)
|
|
return;
|
|
|
|
int inputPeer = -1;
|
|
int visibilityPeer = -1;
|
|
|
|
for (int i = 0; i < peers.Length; i++)
|
|
{
|
|
GamePeer peer = peers[i];
|
|
|
|
if (peer.Context == null)
|
|
continue;
|
|
|
|
if (peer.Context.HasInput)
|
|
{
|
|
if (inputPeer >= 0)
|
|
{
|
|
Debug.Log($"Multiple peers with input is not allowed, turning off input for peer {peer.ID}");
|
|
peer.Context.HasInput = false;
|
|
}
|
|
else
|
|
{
|
|
inputPeer = peer.ID;
|
|
}
|
|
}
|
|
|
|
if (peer.Context.IsVisible == true && visibilityPeer < 0)
|
|
{
|
|
visibilityPeer = peer.ID;
|
|
}
|
|
}
|
|
|
|
if (peers[0].Context != null)
|
|
{
|
|
if (inputPeer < 0)
|
|
{
|
|
Debug.Log($"No input peer, turning on input for peer {peers[0].ID}");
|
|
peers[0].Context.HasInput = true;
|
|
}
|
|
|
|
if (visibilityPeer < 0)
|
|
{
|
|
Debug.Log($"No visible peer, turning on visibility for peer {peers[0].ID}");
|
|
peers[0].Context.IsVisible = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private Dictionary<string, SessionProperty> CreateSessionProperties(SessionRequest request)
|
|
{
|
|
var dictionary = new Dictionary<string, SessionProperty>();
|
|
|
|
dictionary[DISPLAY_NAME_KEY] = request.DisplayName;
|
|
dictionary[MAP_KEY] = Global.Settings.Map.GetMapIndexFromScenePath(request.ScenePath);
|
|
dictionary[TYPE_KEY] = (int)request.GameplayType;
|
|
dictionary[MODE_KEY] = (int)request.GameMode;
|
|
|
|
return dictionary;
|
|
}
|
|
|
|
[System.Diagnostics.Conditional("ENABLE_LOGS")]
|
|
private void Log(string message)
|
|
{
|
|
Debug.Log($"[{Time.realtimeSinceStartup:F3}][{Time.frameCount}] Networking({GetInstanceID()}): {message}");
|
|
}
|
|
|
|
private static string StringToLabel(string myString)
|
|
{
|
|
var label = System.Text.RegularExpressions.Regex.Replace(myString, "(?<=[A-Z])(?=[A-Z][a-z])", " ");
|
|
label = System.Text.RegularExpressions.Regex.Replace(label, "(?<=[^A-Z])(?=[A-Z])", " ");
|
|
|
|
return label;
|
|
}
|
|
|
|
private static bool IsSameScene(string assetPath, string scenePath)
|
|
{
|
|
return assetPath == $"Assets/{scenePath}.unity";
|
|
}
|
|
|
|
// HELPERS
|
|
|
|
private sealed class GamePeer
|
|
{
|
|
public int ID;
|
|
public NetworkSceneInfo Scene;
|
|
public SceneContext Context;
|
|
public GameMode GameMode;
|
|
public NetworkRunner Runner;
|
|
public NetworkSceneManager SceneManager;
|
|
public UnityScene LoadedScene;
|
|
public string UserID;
|
|
public SessionRequest Request;
|
|
public int ConnectionTries = 3;
|
|
public int ReconnectionTries = 1;
|
|
|
|
public bool Loaded;
|
|
public bool WasConnected;
|
|
public bool CanConnect => WasConnected == true ? ReconnectionTries > 0 : ConnectionTries > 0;
|
|
|
|
public bool IsConnected
|
|
{
|
|
get
|
|
{
|
|
if (Runner == null)
|
|
return false;
|
|
|
|
if (Request.GameMode == GameMode.Single)
|
|
return true;
|
|
|
|
if (Runner.IsCloudReady == false || Runner.IsRunning == false)
|
|
return false;
|
|
|
|
return GameMode == GameMode.Client ? Runner.IsConnectedToServer : true;
|
|
}
|
|
}
|
|
|
|
public GamePeer(int id)
|
|
{
|
|
ID = id;
|
|
}
|
|
}
|
|
|
|
private class Session
|
|
{
|
|
public bool ConnectionRequested;
|
|
public GamePeer[] GamePeers;
|
|
|
|
public bool IsConnected
|
|
{
|
|
get
|
|
{
|
|
if (GamePeers.SafeCount() == 0)
|
|
return false;
|
|
|
|
for (int i = 0; i < GamePeers.Length; i++)
|
|
{
|
|
if (GamePeers[i].IsConnected == false)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|