using OWML.Common; using OWML.Utils; using QSB.Animation.Player; using QSB.Animation.Player.Thrusters; using QSB.ClientServerStateSync; using QSB.DeathSync; using QSB.Events; using QSB.Instruments; using QSB.OrbSync.TransformSync; using QSB.Patches; using QSB.Player; using QSB.Player.TransformSync; using QSB.PoolSync; using QSB.ShipSync.TransformSync; using QSB.Anglerfish.TransformSync; using QSB.MeteorSync.TransformSync; using QSB.TimeSync; using QSB.Tools.ProbeTool.TransformSync; using QSB.Utility; using QSB.WorldSync; using QuantumUNET; using QuantumUNET.Components; using System; using System.Linq; using UnityEngine; using UnityEngine.Networking; namespace QSB { public class QSBNetworkManager : QNetworkManager { public static QSBNetworkManager Instance { get; private set; } public event Action OnNetworkManagerReady; public event Action OnClientConnected; public event Action OnClientDisconnected; public event Action OnClientErrorThrown; public bool IsReady { get; private set; } public GameObject OrbPrefab { get; private set; } public GameObject ShipPrefab { get; private set; } public GameObject AnglerPrefab { get; private set; } public GameObject MeteorPrefab { get; private set; } public string PlayerName { get; private set; } private const int MaxConnections = 128; private const int MaxBufferedPackets = 64; private AssetBundle _assetBundle; private GameObject _probePrefab; private bool _everConnected; public new void Awake() { base.Awake(); Instance = this; PlayerName = GetPlayerName(); _assetBundle = QSBCore.NetworkAssetBundle; playerPrefab = _assetBundle.LoadAsset("assets/NETWORK_Player_Body.prefab"); SetupNetworkId(playerPrefab, 1); SetupNetworkTransform(playerPrefab); playerPrefab.AddComponent(); playerPrefab.AddComponent(); playerPrefab.AddComponent(); playerPrefab.AddComponent(); playerPrefab.AddComponent(); playerPrefab.AddComponent(); ShipPrefab = _assetBundle.LoadAsset("assets/networkship.prefab"); SetupNetworkId(ShipPrefab, 2); SetupNetworkTransform(ShipPrefab); ShipPrefab.AddComponent(); spawnPrefabs.Add(ShipPrefab); _probePrefab = _assetBundle.LoadAsset("assets/networkprobe.prefab"); SetupNetworkId(_probePrefab, 3); SetupNetworkTransform(_probePrefab); _probePrefab.AddComponent(); spawnPrefabs.Add(_probePrefab); OrbPrefab = _assetBundle.LoadAsset("assets/networkorb.prefab"); SetupNetworkId(OrbPrefab, 4); SetupNetworkTransform(OrbPrefab); OrbPrefab.AddComponent(); spawnPrefabs.Add(OrbPrefab); AnglerPrefab = _assetBundle.LoadAsset("assets/networkangler.prefab"); SetupNetworkId(AnglerPrefab, 5); SetupNetworkTransform(AnglerPrefab); AnglerPrefab.AddComponent(); spawnPrefabs.Add(AnglerPrefab); MeteorPrefab = _assetBundle.LoadAsset("assets/networkmeteor.prefab"); SetupNetworkId(MeteorPrefab, 6); SetupNetworkTransform(MeteorPrefab); MeteorPrefab.AddComponent(); spawnPrefabs.Add(MeteorPrefab); ConfigureNetworkManager(); } private string GetPlayerName() { try { var profileManager = StandaloneProfileManager.SharedInstance; profileManager.Initialize(); var profile = profileManager._currentProfile; var profileName = profile.profileName; return profileName; } catch (Exception ex) { DebugLog.ToConsole($"Error - Exception when getting player name : {ex}", MessageType.Error); return "Player"; } } private void SetupNetworkId(GameObject go, int assetId) { var ident = go.AddComponent(); ident.LocalPlayerAuthority = true; var networkIdentity = go.GetComponent(); ident.SetValue("m_AssetId", assetId); ident.SetValue("m_SceneId", networkIdentity.GetComponent().sceneId); } private void SetupNetworkTransform(GameObject go) { foreach (var item in go.GetComponents()) { var child = go.AddComponent(); child.Target = item.target; child.m_ChildIndex = item.childIndex; Destroy(item); } Destroy(go.GetComponent()); Destroy(go.GetComponent()); } private void ConfigureNetworkManager() { networkAddress = QSBCore.DefaultServerIP; networkPort = QSBCore.Port; maxConnections = MaxConnections; customConfig = true; connectionConfig.AddChannel(QosType.Reliable); connectionConfig.AddChannel(QosType.Unreliable); this.SetValue("m_MaxBufferedPackets", MaxBufferedPackets); channels.Add(QosType.Reliable); channels.Add(QosType.Unreliable); DebugLog.DebugWrite("Network Manager ready.", MessageType.Success); } public override void OnStartServer() { DebugLog.DebugWrite("OnStartServer", MessageType.Info); if (QSBWorldSync.OldDialogueTrees.Count == 0 && QSBSceneManager.IsInUniverse) { QSBWorldSync.OldDialogueTrees = QSBWorldSync.GetUnityObjects().ToList(); } } public override void OnServerAddPlayer(QNetworkConnection connection, short playerControllerId) // Called on the server when a client joins { DebugLog.DebugWrite($"OnServerAddPlayer {playerControllerId}", MessageType.Info); base.OnServerAddPlayer(connection, playerControllerId); QNetworkServer.SpawnWithClientAuthority(Instantiate(_probePrefab), connection); } public override void OnStartClient(QNetworkClient _) { var config = QSBCore.Helper.Config; config.SetSettingsValue("defaultServerIP", networkAddress); QSBCore.Helper.Storage.Save(config, Constants.ModConfigFileName); } public override void OnClientError(QNetworkConnection conn, int errorCode) => OnClientErrorThrown?.SafeInvoke((NetworkError)errorCode); public override void OnClientConnect(QNetworkConnection connection) // Called on the client when connecting to a server { DebugLog.DebugWrite("OnClientConnect", MessageType.Info); base.OnClientConnect(connection); OnClientConnected?.SafeInvoke(); QSBEventManager.Init(); gameObject.AddComponent(); gameObject.AddComponent(); gameObject.AddComponent(); if (QSBSceneManager.IsInUniverse) { WorldObjectManager.Rebuild(QSBSceneManager.CurrentScene); } var specificType = QNetworkServer.active ? QSBPatchTypes.OnServerClientConnect : QSBPatchTypes.OnNonServerClientConnect; QSBPatchManager.DoPatchType(specificType); QSBPatchManager.DoPatchType(QSBPatchTypes.OnClientConnect); OnNetworkManagerReady?.SafeInvoke(); IsReady = true; QSBCore.UnityEvents.RunWhen(() => QSBEventManager.Ready && PlayerTransformSync.LocalInstance != null, () => QSBEventManager.FireEvent(EventNames.QSBPlayerJoin, PlayerName)); if (!QSBCore.IsHost) { QSBCore.UnityEvents.RunWhen(() => QSBEventManager.Ready && PlayerTransformSync.LocalInstance != null, () => QSBEventManager.FireEvent(EventNames.QSBRequestStateResync)); } _everConnected = true; } public override void OnStopClient() // Called on the client when closing connection { DebugLog.DebugWrite("OnStopClient", MessageType.Info); DebugLog.ToConsole("Disconnecting from server...", MessageType.Info); Destroy(GetComponent()); Destroy(GetComponent()); Destroy(GetComponent()); QSBEventManager.Reset(); QSBPlayerManager.PlayerList.ForEach(player => player.HudMarker?.Remove()); RemoveWorldObjects(); NomaiOrbTransformSync.OrbTransformSyncs.Clear(); QSBWorldSync.OldDialogueTrees.Clear(); if (WakeUpSync.LocalInstance != null) { WakeUpSync.LocalInstance.OnDisconnect(); } if (_everConnected) { var specificType = QNetworkServer.active ? QSBPatchTypes.OnServerClientConnect : QSBPatchTypes.OnNonServerClientConnect; QSBPatchManager.DoUnpatchType(specificType); QSBPatchManager.DoUnpatchType(QSBPatchTypes.OnClientConnect); } IsReady = false; _everConnected = false; } public override void OnClientDisconnect(QNetworkConnection conn) { base.OnClientDisconnect(conn); OnClientDisconnected?.SafeInvoke(conn.LastError); } public override void OnServerDisconnect(QNetworkConnection connection) // Called on the server when any client disconnects { base.OnServerDisconnect(connection); DebugLog.DebugWrite("OnServerDisconnect", MessageType.Info); foreach (var item in NomaiOrbTransformSync.OrbTransformSyncs) { if (item is null) { continue; } var identity = item.GetComponent(); if (identity.ClientAuthorityOwner == connection) { identity.RemoveClientAuthority(connection); } } } public override void OnStopServer() { DebugLog.DebugWrite("OnStopServer", MessageType.Info); Destroy(GetComponent()); QSBEventManager.Reset(); DebugLog.ToConsole("Server stopped!", MessageType.Info); QSBPlayerManager.PlayerList.ForEach(player => player.HudMarker?.Remove()); base.OnStopServer(); } private void RemoveWorldObjects() { QSBWorldSync.RemoveWorldObjects(); QSBWorldSync.RemoveWorldObjects(); foreach (var platform in QSBWorldSync.GetUnityObjects()) { Destroy(platform); } foreach (var camera in QSBWorldSync.GetUnityObjects()) { Destroy(camera); } foreach (var streaming in QSBWorldSync.GetUnityObjects()) { Destroy(streaming); } WorldObjectManager.SetNotReady(); } } }