diff --git a/QSB/Anglerfish/Patches/AnglerPatches.cs b/QSB/Anglerfish/Patches/AnglerPatches.cs index 439bb7a8..b7e174d4 100644 --- a/QSB/Anglerfish/Patches/AnglerPatches.cs +++ b/QSB/Anglerfish/Patches/AnglerPatches.cs @@ -191,54 +191,54 @@ namespace QSB.Anglerfish.Patches __instance.ApplyDrag(1f); return false; case AnglerfishController.AnglerState.Investigating: - { - var targetPos = __instance._brambleBody.transform.TransformPoint(__instance._localDisturbancePos); - __instance.RotateTowardsTarget(targetPos, __instance._turnSpeed, __instance._turnSpeed); - if (!__instance._turningInPlace) { - __instance.MoveTowardsTarget(targetPos, __instance._investigateSpeed, __instance._acceleration); - return false; + var targetPos = __instance._brambleBody.transform.TransformPoint(__instance._localDisturbancePos); + __instance.RotateTowardsTarget(targetPos, __instance._turnSpeed, __instance._turnSpeed); + if (!__instance._turningInPlace) + { + __instance.MoveTowardsTarget(targetPos, __instance._investigateSpeed, __instance._acceleration); + return false; + } + break; } - break; - } case AnglerfishController.AnglerState.Chasing: - { - var velocity = qsbAngler.TargetVelocity; - var normalized = velocity.normalized; - var from = __instance._anglerBody.GetPosition() + __instance.transform.TransformDirection(__instance._mouthOffset) - qsbAngler.TargetTransform.position; - var magnitude = velocity.magnitude; - var num = Vector3.Angle(from, normalized); - var num2 = magnitude * 2f; - var d = num2; - if (num < 90f) { - var magnitude2 = from.magnitude; - var num3 = magnitude2 * Mathf.Sin(num * 0.017453292f); - var num4 = magnitude2 * Mathf.Cos(num * 0.017453292f); - var magnitude3 = __instance._anglerBody.GetVelocity().magnitude; - var num5 = num4 / Mathf.Max(magnitude, 0.0001f); - var num6 = num3 / Mathf.Max(magnitude3, 0.0001f); - var num7 = num5 / num6; - if (num7 <= 1f) + var velocity = qsbAngler.TargetVelocity; + var normalized = velocity.normalized; + var from = __instance._anglerBody.GetPosition() + __instance.transform.TransformDirection(__instance._mouthOffset) - qsbAngler.TargetTransform.position; + var magnitude = velocity.magnitude; + var num = Vector3.Angle(from, normalized); + var num2 = magnitude * 2f; + var d = num2; + if (num < 90f) { - var t = Mathf.Clamp01(num7); - d = Mathf.Lerp(num2, num4, t); + var magnitude2 = from.magnitude; + var num3 = magnitude2 * Mathf.Sin(num * 0.017453292f); + var num4 = magnitude2 * Mathf.Cos(num * 0.017453292f); + var magnitude3 = __instance._anglerBody.GetVelocity().magnitude; + var num5 = num4 / Mathf.Max(magnitude, 0.0001f); + var num6 = num3 / Mathf.Max(magnitude3, 0.0001f); + var num7 = num5 / num6; + if (num7 <= 1f) + { + var t = Mathf.Clamp01(num7); + d = Mathf.Lerp(num2, num4, t); + } + else + { + var num8 = Mathf.InverseLerp(1f, 4f, num7); + d = Mathf.Lerp(num4, 0f, num8 * num8); + } } - else + __instance._targetPos = qsbAngler.TargetTransform.position + normalized * d; + __instance.RotateTowardsTarget(__instance._targetPos, __instance._turnSpeed, __instance._quickTurnSpeed); + if (!__instance._turningInPlace) { - var num8 = Mathf.InverseLerp(1f, 4f, num7); - d = Mathf.Lerp(num4, 0f, num8 * num8); + __instance.MoveTowardsTarget(__instance._targetPos, __instance._chaseSpeed, __instance._acceleration); + return false; } + break; } - __instance._targetPos = qsbAngler.TargetTransform.position + normalized * d; - __instance.RotateTowardsTarget(__instance._targetPos, __instance._turnSpeed, __instance._quickTurnSpeed); - if (!__instance._turningInPlace) - { - __instance.MoveTowardsTarget(__instance._targetPos, __instance._chaseSpeed, __instance._acceleration); - return false; - } - break; - } case AnglerfishController.AnglerState.Consuming: __instance.ApplyDrag(1f); return false; diff --git a/QSB/Anglerfish/TransformSync/AnglerTransformSync.cs b/QSB/Anglerfish/TransformSync/AnglerTransformSync.cs index 3df7db00..01d874e0 100644 --- a/QSB/Anglerfish/TransformSync/AnglerTransformSync.cs +++ b/QSB/Anglerfish/TransformSync/AnglerTransformSync.cs @@ -11,6 +11,7 @@ namespace QSB.Anglerfish.TransformSync { public override bool IsReady => WorldObjectManager.AllObjectsAdded; public override bool UseInterpolation => false; + public override bool IsPlayerObject => false; private QSBAngler _qsbAngler; private static readonly List _instances = new(); diff --git a/QSB/Animation/Player/AnimationSync.cs b/QSB/Animation/Player/AnimationSync.cs index d6ef7908..806b80df 100644 --- a/QSB/Animation/Player/AnimationSync.cs +++ b/QSB/Animation/Player/AnimationSync.cs @@ -187,19 +187,45 @@ namespace QSB.Animation.Player DebugLog.ToConsole($"Error - Suited controller is null. ({PlayerId})", MessageType.Error); } + if (_unsuitedGraphics == null) + { + DebugLog.ToConsole($"Warning - _unsuitedGraphics is null! ({PlayerId})", MessageType.Warning); + } + + if (_suitedGraphics == null) + { + DebugLog.ToConsole($"Warning - _suitedGraphics is null! ({PlayerId})", MessageType.Warning); + } + RuntimeAnimatorController controller = default; switch (type) { case AnimationType.PlayerSuited: controller = _suitedAnimController; - _unsuitedGraphics?.SetActive(false); - _suitedGraphics?.SetActive(true); + if (_unsuitedGraphics != null) + { + _unsuitedGraphics?.SetActive(false); + } + + if (_suitedGraphics != null) + { + _suitedGraphics?.SetActive(true); + } + break; case AnimationType.PlayerUnsuited: controller = _unsuitedAnimController; - _unsuitedGraphics?.SetActive(true); - _suitedGraphics?.SetActive(false); + if (_unsuitedGraphics != null) + { + _unsuitedGraphics?.SetActive(true); + } + + if (_suitedGraphics != null) + { + _suitedGraphics?.SetActive(false); + } + break; case AnimationType.Chert: @@ -223,25 +249,66 @@ namespace QSB.Animation.Player break; } - InvisibleAnimator.runtimeAnimatorController = controller; - VisibleAnimator.runtimeAnimatorController = controller; + if (InvisibleAnimator == null) + { + DebugLog.ToConsole($"Error - InvisibleAnimator is null. ({PlayerId})", MessageType.Error); + } + else + { + InvisibleAnimator.runtimeAnimatorController = controller; + } + + if (VisibleAnimator == null) + { + DebugLog.ToConsole($"Error - VisibleAnimator is null. ({PlayerId})", MessageType.Error); + } + else + { + VisibleAnimator.runtimeAnimatorController = controller; + } + if (type is not AnimationType.PlayerSuited and not AnimationType.PlayerUnsuited) { - VisibleAnimator.SetTrigger("Playing"); - InvisibleAnimator.SetTrigger("Playing"); + if (VisibleAnimator != null) + { + VisibleAnimator.SetTrigger("Playing"); + } + + if (InvisibleAnimator != null) + { + InvisibleAnimator.SetTrigger("Playing"); + } } else { // Avoids "jumping" when exiting instrument and putting on suit - VisibleAnimator.SetTrigger("Grounded"); - InvisibleAnimator.SetTrigger("Grounded"); + if (VisibleAnimator != null) + { + VisibleAnimator.SetTrigger("Grounded"); + } + + if (InvisibleAnimator != null) + { + InvisibleAnimator.SetTrigger("Grounded"); + } } - NetworkAnimator.animator = InvisibleAnimator; // Probably not needed. - Mirror.RebuildFloatParams(); - for (var i = 0; i < InvisibleAnimator.parameterCount; i++) + if (NetworkAnimator == null) { - NetworkAnimator.SetParameterAutoSend(i, true); + DebugLog.ToConsole($"Error - NetworkAnimator is null. ({PlayerId})", MessageType.Error); + } + else if (Mirror == null) + { + DebugLog.ToConsole($"Error - Mirror is null. ({PlayerId})", MessageType.Error); + } + else if (InvisibleAnimator != null) + { + NetworkAnimator.animator = InvisibleAnimator; // Probably not needed. + Mirror.RebuildFloatParams(); + for (var i = 0; i < InvisibleAnimator.parameterCount; i++) + { + NetworkAnimator.SetParameterAutoSend(i, true); + } } } } diff --git a/QSB/Animation/Player/Events/AnimationTriggerEvent.cs b/QSB/Animation/Player/Events/AnimationTriggerEvent.cs index ddc85992..4aac7220 100644 --- a/QSB/Animation/Player/Events/AnimationTriggerEvent.cs +++ b/QSB/Animation/Player/Events/AnimationTriggerEvent.cs @@ -1,6 +1,5 @@ using QSB.Events; using QSB.Player; -using QSB.WorldSync; namespace QSB.Animation.Player.Events { @@ -28,6 +27,11 @@ namespace QSB.Animation.Player.Events return; } + if (animationSync.VisibleAnimator == null) + { + return; + } + animationSync.VisibleAnimator.SetTrigger(message.Name); } } diff --git a/QSB/Animation/Player/Events/ChangeAnimTypeEvent.cs b/QSB/Animation/Player/Events/ChangeAnimTypeEvent.cs index d20ae6df..3fbf03fe 100644 --- a/QSB/Animation/Player/Events/ChangeAnimTypeEvent.cs +++ b/QSB/Animation/Player/Events/ChangeAnimTypeEvent.cs @@ -2,7 +2,6 @@ using QSB.Instruments; using QSB.Messaging; using QSB.Player; -using QSB.WorldSync; namespace QSB.Animation.Player.Events { diff --git a/QSB/Animation/Player/Events/PlayerSuitEvent.cs b/QSB/Animation/Player/Events/PlayerSuitEvent.cs index 1c67c151..329adb0a 100644 --- a/QSB/Animation/Player/Events/PlayerSuitEvent.cs +++ b/QSB/Animation/Player/Events/PlayerSuitEvent.cs @@ -1,7 +1,6 @@ using QSB.Events; using QSB.Messaging; using QSB.Player; -using QSB.WorldSync; namespace QSB.Animation.Player.Events { diff --git a/QSB/AssetBundles/network b/QSB/AssetBundles/network index dfbd89d6..e1bab2b2 100644 Binary files a/QSB/AssetBundles/network and b/QSB/AssetBundles/network differ diff --git a/QSB/AssetBundles/network.manifest b/QSB/AssetBundles/network.manifest index bbf0c969..a78ae406 100644 --- a/QSB/AssetBundles/network.manifest +++ b/QSB/AssetBundles/network.manifest @@ -1,12 +1,12 @@ ManifestFileVersion: 0 -CRC: 2906651145 +CRC: 1208921857 Hashes: AssetFileHash: serializedVersion: 2 - Hash: 0fa5758f55c8803c7ba77a52ab2c9488 + Hash: e6fb281706df372893c7d91213b2598a TypeTreeHash: serializedVersion: 2 - Hash: 87b5c87f221cc0cbe8c4eefab7d02419 + Hash: 91770441465e7cb2442c311d5ad605d6 HashAppended: 0 ClassTypes: - Class: 1 @@ -17,8 +17,6 @@ ClassTypes: Script: {fileID: -1208705890, guid: 8929207a0d76fd54a8a2a820c652720f, type: 3} - Class: 114 Script: {fileID: 1142416158, guid: 8929207a0d76fd54a8a2a820c652720f, type: 3} -- Class: 114 - Script: {fileID: 519208029, guid: 27687deae413b90448366870cb0de502, type: 3} - Class: 114 Script: {fileID: -790547616, guid: 27687deae413b90448366870cb0de502, type: 3} - Class: 114 @@ -29,18 +27,10 @@ ClassTypes: Script: {fileID: -1164351254, guid: 27687deae413b90448366870cb0de502, type: 3} - Class: 114 Script: {fileID: -1309757293, guid: 27687deae413b90448366870cb0de502, type: 3} -- Class: 114 - Script: {fileID: 316226861, guid: 27687deae413b90448366870cb0de502, type: 3} - Class: 114 Script: {fileID: 78926581, guid: 27687deae413b90448366870cb0de502, type: 3} -- Class: 114 - Script: {fileID: -1381557116, guid: 27687deae413b90448366870cb0de502, type: 3} -- Class: 114 - Script: {fileID: -1937550126, guid: 27687deae413b90448366870cb0de502, type: 3} - Class: 114 Script: {fileID: -749020886, guid: 27687deae413b90448366870cb0de502, type: 3} -- Class: 114 - Script: {fileID: -2281650, guid: 27687deae413b90448366870cb0de502, type: 3} - Class: 114 Script: {fileID: 677635994, guid: 27687deae413b90448366870cb0de502, type: 3} - Class: 114 @@ -49,10 +39,6 @@ ClassTypes: Script: {instanceID: 0} SerializeReferenceClassIdentifiers: [] Assets: -- Assets/Prefabs/NetworkJellyfish.prefab -- Assets/Prefabs/NetworkProbe.prefab +- Assets/Prefabs/NetworkObject.prefab - Assets/Prefabs/NETWORK_Player_Body.prefab -- Assets/Prefabs/NetworkOrb.prefab -- Assets/Prefabs/NetworkAngler.prefab -- Assets/Prefabs/NetworkShip.prefab Dependencies: [] diff --git a/QSB/AuthoritySync/AuthorityManager.cs b/QSB/AuthoritySync/AuthorityManager.cs index 1b93fbde..b2b3ce25 100644 --- a/QSB/AuthoritySync/AuthorityManager.cs +++ b/QSB/AuthoritySync/AuthorityManager.cs @@ -1,9 +1,9 @@ -using System.Collections.Generic; -using System.Linq; -using QSB.Events; +using QSB.Events; using QSB.Utility; using QuantumUNET; using QuantumUNET.Components; +using System.Collections.Generic; +using System.Linq; namespace QSB.AuthoritySync { @@ -72,7 +72,7 @@ namespace QSB.AuthoritySync } // DebugLog.DebugWrite($"{identity.NetId}:{identity.gameObject.name} - " - // + $"set authority to {id}"); + // + $"set authority to {id}"); } #endregion diff --git a/QSB/AuthoritySync/AuthorityQueueEvent.cs b/QSB/AuthoritySync/AuthorityQueueEvent.cs index d949dc6f..5d51bbaf 100644 --- a/QSB/AuthoritySync/AuthorityQueueEvent.cs +++ b/QSB/AuthoritySync/AuthorityQueueEvent.cs @@ -1,5 +1,4 @@ using QSB.Events; -using QSB.WorldSync; using QuantumUNET.Components; namespace QSB.AuthoritySync diff --git a/QSB/ElevatorSync/WorldObjects/QSBElevator.cs b/QSB/ElevatorSync/WorldObjects/QSBElevator.cs index f322af7a..e62e73dc 100644 --- a/QSB/ElevatorSync/WorldObjects/QSBElevator.cs +++ b/QSB/ElevatorSync/WorldObjects/QSBElevator.cs @@ -40,7 +40,7 @@ namespace QSB.ElevatorSync.WorldObjects if (_elevatorTrigger.IsTrackingObject(Locator.GetPlayerDetector())) { SetDirection(isGoingUp); - + AttachedObject._attachPoint.AttachPlayer(); if (Locator.GetPlayerSuit().IsWearingSuit(true) && Locator.GetPlayerSuit().IsTrainingSuit()) diff --git a/QSB/Events/EventNames.cs b/QSB/Events/EventNames.cs index b37d2b4e..8d63218c 100644 --- a/QSB/Events/EventNames.cs +++ b/QSB/Events/EventNames.cs @@ -102,5 +102,8 @@ public const string QSBSatelliteRepaired = nameof(QSBSatelliteRepairTick); public const string QSBAuthorityQueue = nameof(QSBAuthorityQueue); public const string QSBJellyfishRising = nameof(QSBJellyfishRising); + public const string QSBTornadoFormState = nameof(QSBTornadoFormState); + public const string QSBRequestGameDetails = nameof(QSBRequestGameDetails); + public const string QSBGameDetails = nameof(QSBGameDetails); } } diff --git a/QSB/Events/QSBEvent.cs b/QSB/Events/QSBEvent.cs index 9ccd5bdb..3b191888 100644 --- a/QSB/Events/QSBEvent.cs +++ b/QSB/Events/QSBEvent.cs @@ -1,4 +1,3 @@ -using System; using JetBrains.Annotations; using OWML.Utils; using QSB.Messaging; @@ -6,6 +5,7 @@ using QSB.Player; using QSB.Player.TransformSync; using QSB.WorldSync; using QuantumUNET.Transport; +using System; namespace QSB.Events { diff --git a/QSB/Events/QSBEventManager.cs b/QSB/Events/QSBEventManager.cs index 2ff00210..d6e3a817 100644 --- a/QSB/Events/QSBEventManager.cs +++ b/QSB/Events/QSBEventManager.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using OWML.Common; diff --git a/QSB/Inputs/QSBInputManager.cs b/QSB/Inputs/QSBInputManager.cs index 840ffeb0..dcf06a1b 100644 --- a/QSB/Inputs/QSBInputManager.cs +++ b/QSB/Inputs/QSBInputManager.cs @@ -58,7 +58,6 @@ namespace QSB.Inputs public void SetInputsEnabled(bool enabled) { - DebugLog.DebugWrite($"INPUTS ENABLED? : {enabled}"); InputsEnabled = enabled; } } diff --git a/QSB/JellyfishSync/TransformSync/JellyfishTransformSync.cs b/QSB/JellyfishSync/TransformSync/JellyfishTransformSync.cs index d343fea2..9114b439 100644 --- a/QSB/JellyfishSync/TransformSync/JellyfishTransformSync.cs +++ b/QSB/JellyfishSync/TransformSync/JellyfishTransformSync.cs @@ -1,10 +1,10 @@ -using System.Collections.Generic; -using QSB.JellyfishSync.WorldObjects; +using QSB.JellyfishSync.WorldObjects; using QSB.Syncs; using QSB.Syncs.Unsectored.Rigidbodies; using QSB.Utility; using QSB.WorldSync; using QuantumUNET.Transport; +using System.Collections.Generic; using UnityEngine; namespace QSB.JellyfishSync.TransformSync @@ -13,6 +13,7 @@ namespace QSB.JellyfishSync.TransformSync { public override bool IsReady => WorldObjectManager.AllObjectsAdded; public override bool UseInterpolation => false; + public override bool IsPlayerObject => false; private QSBJellyfish _qsbJellyfish; private static readonly List _instances = new(); @@ -93,11 +94,6 @@ namespace QSB.JellyfishSync.TransformSync var targetPos = ReferenceTransform.DecodePos(transform.position); var targetRot = ReferenceTransform.DecodeRot(transform.rotation); - if (targetPos == Vector3.zero || transform.position == Vector3.zero) - { - return false; - } - var positionToSet = targetPos; var rotationToSet = targetRot; diff --git a/QSB/Menus/IMenuAPI.cs b/QSB/Menus/IMenuAPI.cs index c30510f0..edb0a932 100644 --- a/QSB/Menus/IMenuAPI.cs +++ b/QSB/Menus/IMenuAPI.cs @@ -1,4 +1,5 @@ -using UnityEngine; +using System; +using UnityEngine; using UnityEngine.UI; namespace QSB.Menus @@ -6,9 +7,16 @@ namespace QSB.Menus public interface IMenuAPI { // Title screen + [Obsolete] GameObject TitleScreen_MakeMenuOpenButton(string name, Menu menuToOpen); + [Obsolete] GameObject TitleScreen_MakeSceneLoadButton(string name, SubmitActionLoadScene.LoadableScenes sceneToLoad, PopupMenu confirmPopup = null); + [Obsolete] Button TitleScreen_MakeSimpleButton(string name); + + GameObject TitleScreen_MakeMenuOpenButton(string name, int index, Menu menuToOpen); + GameObject TitleScreen_MakeSceneLoadButton(string name, int index, SubmitActionLoadScene.LoadableScenes sceneToLoad, PopupMenu confirmPopup = null); + Button TitleScreen_MakeSimpleButton(string name, int index); // Pause menu GameObject PauseMenu_MakeMenuOpenButton(string name, Menu menuToOpen, Menu customMenu = null); GameObject PauseMenu_MakeSceneLoadButton(string name, SubmitActionLoadScene.LoadableScenes sceneToLoad, PopupMenu confirmPopup = null, Menu customMenu = null); diff --git a/QSB/Menus/MenuManager.cs b/QSB/Menus/MenuManager.cs index 7ac927ca..647dfddd 100644 --- a/QSB/Menus/MenuManager.cs +++ b/QSB/Menus/MenuManager.cs @@ -1,5 +1,9 @@ -using QSB.Player; +using QSB.Events; +using QSB.Player; +using QSB.Player.TransformSync; +using QSB.Utility; using System.Linq; +using System.Text; using UnityEngine; using UnityEngine.Networking; using UnityEngine.UI; @@ -11,13 +15,27 @@ namespace QSB.Menus public static MenuManager Instance; private IMenuAPI MenuApi => QSBCore.MenuApi; - private PopupMenu PopupMenu; - private Button HostButton; - private GameObject ClientButton; - private Button DisconnectButton; + + private PopupMenu IPPopup; private PopupMenu InfoPopup; private bool _addedPauseLock; + // Pause menu only + private Button HostButton; + private GameObject QuitButton; + private GameObject DisconnectButton; + private PopupMenu DisconnectPopup; + private StringBuilder _nowLoadingSB; + protected Text _loadingText; + + // title screen only + private GameObject ResumeGameButton; + private GameObject NewGameButton; + private GameObject ClientButton; + + private const int _ClientButtonIndex = 2; + private const int _DisconnectIndex = 3; + public void Start() { Instance = this; @@ -42,6 +60,51 @@ namespace QSB.Menus } } + private void ResetStringBuilder() + { + if (_nowLoadingSB == null) + { + _nowLoadingSB = new StringBuilder(); + return; + } + _nowLoadingSB.Length = 0; + } + + private void Update() + { + if (QSBCore.IsInMultiplayer + && (LoadManager.GetLoadingScene() == OWScene.SolarSystem || LoadManager.GetLoadingScene() == OWScene.EyeOfTheUniverse) + && _loadingText != null) + { + var num = LoadManager.GetAsyncLoadProgress(); + num = num < 0.1f + ? Mathf.InverseLerp(0f, 0.1f, num) * 0.9f + : 0.9f + (Mathf.InverseLerp(0.1f, 1f, num) * 0.1f); + ResetStringBuilder(); + _nowLoadingSB.Append(UITextLibrary.GetString(UITextType.LoadingMessage)); + _nowLoadingSB.Append(num.ToString("P0")); + _loadingText.text = _nowLoadingSB.ToString(); + } + } + + public void JoinGame(bool inEye, bool inSolarSystem) + { + if (inEye) + { + LoadManager.LoadSceneAsync(OWScene.EyeOfTheUniverse, true, LoadManager.FadeType.ToBlack, 1f, false); + Locator.GetMenuInputModule().DisableInputs(); + } + else if (inSolarSystem) + { + LoadManager.LoadSceneAsync(OWScene.SolarSystem, true, LoadManager.FadeType.ToBlack, 1f, false); + Locator.GetMenuInputModule().DisableInputs(); + } + else + { + DebugLog.DebugWrite("tried to join game that wasnt in solar system or eye??"); + } + } + private void OpenInfoPopup(string message, string buttonText) { InfoPopup.SetUpPopup(message, InputLibrary.menuConfirm, InputLibrary.cancel, new ScreenPrompt(buttonText), null, true, false); @@ -70,72 +133,108 @@ namespace QSB.Menus OWTime.Unpause(OWTime.PauseType.System); OWInput.RestorePreviousInputs(); + + if (QSBSceneManager.IsInUniverse) + { + LoadManager.LoadScene(OWScene.TitleScreen, LoadManager.FadeType.ToBlack, 2f, true); + } } private void CreateCommonPopups() { - PopupMenu = MenuApi.MakeInputFieldPopup("IP Address", "IP Address", "Connect", "Cancel"); - PopupMenu.OnPopupConfirm += Connect; + IPPopup = MenuApi.MakeInputFieldPopup("IP Address", "IP Address", "Connect", "Cancel"); + IPPopup.OnPopupConfirm += Connect; InfoPopup = MenuApi.MakeInfoPopup("", ""); InfoPopup.OnDeactivateMenu += OnCloseInfoPopup; } + private void SetButtonActive(Button button, bool active) + => SetButtonActive(button?.gameObject, active); + + private void SetButtonActive(GameObject button, bool active) + { + if (button == null) + { + DebugLog.DebugWrite($"Warning - Tried to set button to {active}, but it was null.", OWML.Common.MessageType.Warning); + return; + } + + button.SetActive(active); + button.GetComponent().alpha = active ? 1 : 0; + } + private void InitPauseMenus() { CreateCommonPopups(); - HostButton = MenuApi.PauseMenu_MakeSimpleButton("MULTIPLAYER (HOST)"); + HostButton = MenuApi.PauseMenu_MakeSimpleButton("OPEN TO MULTIPLAYER"); HostButton.onClick.AddListener(Host); - ClientButton = MenuApi.PauseMenu_MakeMenuOpenButton("MULTIPLAYER (CONNECT)", PopupMenu); + DisconnectPopup = MenuApi.MakeTwoChoicePopup("Are you sure you want to disconnect?\r\nThis will send you back to the main menu.", "YES", "NO"); + DisconnectPopup.OnPopupConfirm += Disconnect; - DisconnectButton = MenuApi.PauseMenu_MakeSimpleButton("DISCONNECT"); - DisconnectButton.onClick.AddListener(Disconnect); + DisconnectButton = MenuApi.PauseMenu_MakeMenuOpenButton("DISCONNECT", DisconnectPopup); + + QuitButton = FindObjectOfType()._exitToMainMenuAction.gameObject; if (QSBCore.IsInMultiplayer) { - ClientButton.SetActive(false); - HostButton.gameObject.SetActive(false); - DisconnectButton.gameObject.SetActive(true); - DisconnectButton.GetComponent().alpha = 1f; + SetButtonActive(HostButton, false); + SetButtonActive(DisconnectButton, true); + SetButtonActive(QuitButton, false); } else { - DisconnectButton.gameObject.SetActive(false); - DisconnectButton.GetComponent().alpha = 1f; + SetButtonActive(HostButton, true); + SetButtonActive(DisconnectButton, false); + SetButtonActive(QuitButton, true); } - OnConnected(); + var text = QSBCore.IsHost + ? "STOP HOSTING" + : "DISCONNECT"; + DisconnectButton.transform.GetChild(0).GetChild(1).GetComponent().text = text; + + var popupText = QSBCore.IsHost + ? "Are you sure you want to stop hosting?\r\nThis will disconnect all clients and send everyone back to the main menu." + : "Are you sure you want to disconnect?\r\nThis will send you back to the main menu."; + DisconnectPopup._labelText.text = popupText; } private void MakeTitleMenus() { CreateCommonPopups(); - HostButton = MenuApi.TitleScreen_MakeSimpleButton("MULTIPLAYER (HOST)"); - HostButton.onClick.AddListener(Host); + ClientButton = MenuApi.TitleScreen_MakeMenuOpenButton("CONNECT TO MULTIPLAYER", _ClientButtonIndex, IPPopup); - ClientButton = MenuApi.TitleScreen_MakeMenuOpenButton("MULTIPLAYER (CONNECT)", PopupMenu); + _loadingText = ClientButton.transform.GetChild(0).GetChild(1).GetComponent(); - DisconnectButton = MenuApi.TitleScreen_MakeSimpleButton("DISCONNECT"); - DisconnectButton.onClick.AddListener(Disconnect); + ResumeGameButton = GameObject.Find("MainMenuLayoutGroup/Button-ResumeGame"); + NewGameButton = GameObject.Find("MainMenuLayoutGroup/Button-NewGame"); if (QSBCore.IsInMultiplayer) { - ClientButton.SetActive(false); - HostButton.gameObject.SetActive(false); - DisconnectButton.gameObject.SetActive(true); - DisconnectButton.GetComponent().alpha = 1f; + SetButtonActive(ClientButton, false); + + if (QSBCore.IsHost) + { + SetButtonActive(ResumeGameButton, StandaloneProfileManager.SharedInstance.currentProfileGameSave.loopCount > 1); + SetButtonActive(NewGameButton, true); + } + else + { + SetButtonActive(ResumeGameButton, false); + SetButtonActive(NewGameButton, false); + } } else { - DisconnectButton.gameObject.SetActive(false); - DisconnectButton.GetComponent().alpha = 1f; + SetButtonActive(ClientButton, true); + SetButtonActive(ResumeGameButton, StandaloneProfileManager.SharedInstance.currentProfileGameSave.loopCount > 1); + SetButtonActive(NewGameButton, true); } - OnConnected(); - if (QSBCore.SkipTitleScreen) { Application.runInBackground = true; @@ -159,37 +258,68 @@ namespace QSB.Menus private void Disconnect() { QSBNetworkManager.Instance.StopHost(); - DisconnectButton.gameObject.SetActive(false); - ClientButton.SetActive(true); - HostButton.gameObject.SetActive(true); + SetButtonActive(DisconnectButton.gameObject, false); + + Locator.GetSceneMenuManager().pauseMenu._pauseMenu.EnableMenu(false); + Locator.GetSceneMenuManager().pauseMenu._isPaused = false; + + OWInput.RestorePreviousInputs(); + + LoadManager.LoadScene(OWScene.TitleScreen, LoadManager.FadeType.ToBlack, 2f, true); } private void Host() { - QSBNetworkManager.Instance.StartHost(); - DisconnectButton.gameObject.SetActive(true); - DisconnectButton.GetComponent().alpha = 1f; - ClientButton.SetActive(false); - HostButton.gameObject.SetActive(false); - } + if (QSBNetworkManager.Instance.StartHost() != null) + { + SetButtonActive(DisconnectButton, true); + SetButtonActive(HostButton, false); + SetButtonActive(QuitButton, false); + } + else + { + OpenInfoPopup($"Failed to start server.", "OK"); + } - private void Connect() - { - QSBNetworkManager.Instance.networkAddress = string.Concat((PopupMenu as PopupInputMenu).GetInputText().Where(c => !char.IsWhiteSpace(c))); - QSBNetworkManager.Instance.StartClient(); - DisconnectButton.transform.GetChild(0).GetChild(1).GetComponent().text = "CONNECTING... (STOP)"; - DisconnectButton.gameObject.SetActive(true); - DisconnectButton.GetComponent().alpha = 1f; - ClientButton.SetActive(false); - HostButton.gameObject.SetActive(false); - } - - private void OnConnected() - { var text = QSBCore.IsHost ? "STOP HOSTING" : "DISCONNECT"; DisconnectButton.transform.GetChild(0).GetChild(1).GetComponent().text = text; + + var popupText = QSBCore.IsHost + ? "Are you sure you want to stop hosting?\r\nThis will disconnect all clients and send everyone back to the main menu." + : "Are you sure you want to disconnect?\r\nThis will send you back to the main menu."; + DisconnectPopup._labelText.text = popupText; + } + + private void Connect() + { + QSBNetworkManager.Instance.networkAddress = string.Concat((IPPopup as PopupInputMenu).GetInputText().Where(c => !char.IsWhiteSpace(c))); + QSBNetworkManager.Instance.StartClient(); + + if (QSBSceneManager.CurrentScene == OWScene.TitleScreen) + { + SetButtonActive(ResumeGameButton, false); + SetButtonActive(NewGameButton, false); + } + + if (QSBSceneManager.IsInUniverse) + { + SetButtonActive(QuitButton, false); + } + } + + private void OnConnected() + { + if (QSBCore.IsHost || !QSBCore.IsInMultiplayer) + { + return; + } + + QSBCore.UnityEvents.RunWhen(() => QSBEventManager.Ready && PlayerTransformSync.LocalInstance != null, () => + { + QSBEventManager.FireEvent(EventNames.QSBRequestGameDetails); + }); } public void OnKicked(KickReason reason) @@ -199,14 +329,16 @@ namespace QSB.Menus KickReason.QSBVersionNotMatching => "Server refused connection as QSB version does not match.", KickReason.GameVersionNotMatching => "Server refused connection as Outer Wilds version does not match.", KickReason.GamePlatformNotMatching => "Server refused connection as Outer Wilds platform does not match. (Steam/Epic)", + KickReason.DLCNotMatching => "Server refused connection as DLC installation state does not match.", KickReason.None => "Kicked from server. No reason given.", _ => $"Kicked from server. KickReason:{reason}", }; OpenInfoPopup(text, "OK"); - DisconnectButton.gameObject.SetActive(false); - ClientButton.SetActive(true); - HostButton.gameObject.SetActive(true); + SetButtonActive(DisconnectButton, false); + SetButtonActive(ClientButton, true); + SetButtonActive(HostButton, true); + SetButtonActive(QuitButton, true); } private void OnDisconnected(NetworkError error) @@ -223,9 +355,12 @@ namespace QSB.Menus }; OpenInfoPopup(text, "OK"); - DisconnectButton.gameObject.SetActive(false); - ClientButton.SetActive(true); - HostButton.gameObject.SetActive(true); + SetButtonActive(DisconnectButton, false); + SetButtonActive(ClientButton, true); + SetButtonActive(QuitButton, true); + SetButtonActive(HostButton, true); + SetButtonActive(ResumeGameButton, StandaloneProfileManager.SharedInstance.currentProfileGameSave.loopCount > 1); + SetButtonActive(NewGameButton, true); } private void OnClientError(NetworkError error) @@ -241,9 +376,13 @@ namespace QSB.Menus { case NetworkError.DNSFailure: text = "Internal QNet client error!\r\nDNS Faliure. Address was invalid or could not be resolved."; - DisconnectButton.gameObject.SetActive(false); - ClientButton.SetActive(true); - HostButton.gameObject.SetActive(true); + DebugLog.DebugWrite($"dns failure"); + SetButtonActive(DisconnectButton, false); + SetButtonActive(ClientButton, true); + SetButtonActive(HostButton, true); + SetButtonActive(ResumeGameButton, StandaloneProfileManager.SharedInstance.currentProfileGameSave.loopCount > 1); + SetButtonActive(NewGameButton, true); + SetButtonActive(QuitButton, true); break; default: text = $"Internal QNet client error!\n\nNetworkError:{error}"; diff --git a/QSB/Messaging/MessageHandler.cs b/QSB/Messaging/MessageHandler.cs new file mode 100644 index 00000000..5f282702 --- /dev/null +++ b/QSB/Messaging/MessageHandler.cs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/QSB/OrbSync/Events/OrbUserEvent.cs b/QSB/OrbSync/Events/OrbUserEvent.cs index 5770d85f..3d7ea18a 100644 --- a/QSB/OrbSync/Events/OrbUserEvent.cs +++ b/QSB/OrbSync/Events/OrbUserEvent.cs @@ -1,11 +1,11 @@ -using System.Linq; -using OWML.Common; +using OWML.Common; using QSB.AuthoritySync; using QSB.Events; using QSB.OrbSync.TransformSync; using QSB.Utility; using QSB.WorldSync; using QSB.WorldSync.Events; +using System.Linq; namespace QSB.OrbSync.Events { diff --git a/QSB/OrbSync/TransformSync/NomaiOrbTransformSync.cs b/QSB/OrbSync/TransformSync/NomaiOrbTransformSync.cs index aacb5eb8..600e6a8c 100644 --- a/QSB/OrbSync/TransformSync/NomaiOrbTransformSync.cs +++ b/QSB/OrbSync/TransformSync/NomaiOrbTransformSync.cs @@ -11,6 +11,8 @@ namespace QSB.OrbSync.TransformSync { public static List OrbTransformSyncs = new(); + public override bool IsPlayerObject => false; + private int _index => OrbTransformSyncs.IndexOf(this); public override void OnStartClient() => OrbTransformSyncs.Add(this); diff --git a/QSB/Player/Events/PlayerInformationEvent.cs b/QSB/Player/Events/PlayerInformationEvent.cs index 3ba248fb..5dda07bc 100644 --- a/QSB/Player/Events/PlayerInformationEvent.cs +++ b/QSB/Player/Events/PlayerInformationEvent.cs @@ -29,7 +29,7 @@ namespace QSB.Player.Events public override void OnReceiveRemote(bool server, PlayerInformationMessage message) { - DebugLog.DebugWrite($"Received playerstate of player ID {message.AboutId}", MessageType.Info); + RequestStateResyncEvent._waitingForEvent = false; if (QSBPlayerManager.PlayerExists(message.AboutId)) { QSBPlayerManager.HandleFullStateMessage(message); diff --git a/QSB/Player/Events/PlayerJoinEvent.cs b/QSB/Player/Events/PlayerJoinEvent.cs index f6f3b239..d0177a15 100644 --- a/QSB/Player/Events/PlayerJoinEvent.cs +++ b/QSB/Player/Events/PlayerJoinEvent.cs @@ -19,7 +19,8 @@ namespace QSB.Player.Events PlayerName = name, QSBVersion = QSBCore.QSBVersion, GameVersion = QSBCore.GameVersion, - Platform = QSBCore.Platform + Platform = QSBCore.Platform, + DlcInstalled = QSBCore.DLCInstalled }; public override void OnReceiveRemote(bool server, PlayerJoinMessage message) @@ -51,6 +52,15 @@ namespace QSB.Player.Events if (server) { DebugLog.ToConsole($"Error - Client {message.PlayerName} connecting with wrong game platform. (Client:{message.Platform}, Server:{QSBCore.Platform})", MessageType.Error); + QSBEventManager.FireEvent(EventNames.QSBPlayerKick, message.AboutId, KickReason.DLCNotMatching); + } + } + + if (message.DlcInstalled != QSBCore.DLCInstalled) + { + if (server) + { + DebugLog.ToConsole($"Error - Client {message.PlayerName} connecting with wrong DLC installation state. (Client:{message.DlcInstalled}, Server:{QSBCore.DLCInstalled})", MessageType.Error); QSBEventManager.FireEvent(EventNames.QSBPlayerKick, message.AboutId, KickReason.GamePlatformNotMatching); } } @@ -58,7 +68,7 @@ namespace QSB.Player.Events var player = QSBPlayerManager.GetPlayer(message.AboutId); player.Name = message.PlayerName; DebugLog.ToAll($"{player.Name} joined!", MessageType.Info); - DebugLog.DebugWrite($"{player.Name} joined. id:{player.PlayerId}, qsbVersion:{message.QSBVersion}, gameVersion:{message.GameVersion}, platform:{message.Platform}", MessageType.Info); + DebugLog.DebugWrite($"{player.Name} joined. id:{player.PlayerId}, qsbVersion:{message.QSBVersion}, gameVersion:{message.GameVersion}, platform:{message.Platform}. dlcInstalled:{message.DlcInstalled}", MessageType.Info); } public override void OnReceiveLocal(bool server, PlayerJoinMessage message) diff --git a/QSB/Player/Events/PlayerJoinMessage.cs b/QSB/Player/Events/PlayerJoinMessage.cs index a07f5f4d..a965cf20 100644 --- a/QSB/Player/Events/PlayerJoinMessage.cs +++ b/QSB/Player/Events/PlayerJoinMessage.cs @@ -9,6 +9,7 @@ namespace QSB.Player.Events public string QSBVersion { get; set; } public string GameVersion { get; set; } public GamePlatform Platform { get; set; } + public bool DlcInstalled { get; set; } public override void Deserialize(QNetworkReader reader) { @@ -17,6 +18,7 @@ namespace QSB.Player.Events QSBVersion = reader.ReadString(); GameVersion = reader.ReadString(); Platform = (GamePlatform)reader.ReadInt32(); + DlcInstalled = reader.ReadBoolean(); } public override void Serialize(QNetworkWriter writer) @@ -26,6 +28,7 @@ namespace QSB.Player.Events writer.Write(QSBVersion); writer.Write(GameVersion); writer.Write((int)Platform); + writer.Write(DlcInstalled); } } } \ No newline at end of file diff --git a/QSB/Player/Events/PlayerReadyEvent.cs b/QSB/Player/Events/PlayerReadyEvent.cs index 8da97c2c..2bc21ad5 100644 --- a/QSB/Player/Events/PlayerReadyEvent.cs +++ b/QSB/Player/Events/PlayerReadyEvent.cs @@ -34,14 +34,14 @@ namespace QSB.Player.Events private static void HandleServer(ToggleMessage message) { - DebugLog.DebugWrite($"[SERVER] Get ready event from {message.FromId}", MessageType.Success); + DebugLog.DebugWrite($"[SERVER] Get ready event from {message.FromId} (ready = {message.ToggleValue})", MessageType.Success); QSBPlayerManager.GetPlayer(message.AboutId).IsReady = message.ToggleValue; QSBEventManager.FireEvent(EventNames.QSBPlayerInformation); } private void HandleClient(ToggleMessage message) { - DebugLog.DebugWrite($"[CLIENT] Get ready event from {message.FromId}", MessageType.Success); + DebugLog.DebugWrite($"[CLIENT] Get ready event from {message.FromId} (ready = {message.ToggleValue})", MessageType.Success); if (!QSBPlayerManager.PlayerExists(message.FromId)) { DebugLog.ToConsole( @@ -49,6 +49,8 @@ namespace QSB.Player.Events MessageType.Error); return; } + + QSBPlayerManager.GetPlayer(message.AboutId).IsReady = message.ToggleValue; } } } \ No newline at end of file diff --git a/QSB/Player/Events/RequestStateResyncEvent.cs b/QSB/Player/Events/RequestStateResyncEvent.cs index fabfa614..a590ff98 100644 --- a/QSB/Player/Events/RequestStateResyncEvent.cs +++ b/QSB/Player/Events/RequestStateResyncEvent.cs @@ -1,4 +1,5 @@ -using OWML.Utils; +using System.Linq; +using OWML.Utils; using QSB.CampfireSync.WorldObjects; using QSB.ClientServerStateSync; using QSB.Events; @@ -7,27 +8,51 @@ using QSB.MeteorSync.WorldObjects; using QSB.QuantumSync; using QSB.Tools.TranslatorTool.TranslationSync; using QSB.Tools.TranslatorTool.TranslationSync.WorldObjects; +using QSB.TornadoSync; +using QSB.TornadoSync.WorldObjects; using QSB.Utility; using QSB.WorldSync; -using System.Linq; namespace QSB.Player.Events { // Can be sent by any client (including host) to signal they want latest worldobject, player, and server infomation public class RequestStateResyncEvent : QSBEvent { + public static bool _waitingForEvent; + public override bool RequireWorldObjectsReady => false; public override void SetupListener() => GlobalMessenger.AddListener(EventNames.QSBRequestStateResync, Handler); public override void CloseListener() => GlobalMessenger.RemoveListener(EventNames.QSBRequestStateResync, Handler); - private void Handler() => SendEvent(CreateMessage()); + private void Handler() + { + if (_waitingForEvent) + { + return; + } + + _waitingForEvent = true; + SendEvent(CreateMessage()); + } private PlayerMessage CreateMessage() => new() { AboutId = LocalPlayerId }; + public override void OnReceiveLocal(bool isHost, PlayerMessage message) + { + QSBCore.UnityEvents.FireInNUpdates(() => + { + if (_waitingForEvent) + { + DebugLog.ToConsole($"Did not receive PlayerInformationEvent in time. Setting _waitingForEvent to false.", OWML.Common.MessageType.Info); + _waitingForEvent = false; + } + }, 60); + } + public override void OnReceiveRemote(bool isHost, PlayerMessage message) { // send response only to the requesting client @@ -94,6 +119,9 @@ namespace QSB.Player.Events QSBWorldSync.GetWorldObjects().ForEach(fragment => QSBEventManager.FireEvent(EventNames.QSBFragmentResync, fragment)); + + QSBWorldSync.GetWorldObjects().ForEach(tornado + => QSBEventManager.FireEvent(EventNames.QSBTornadoFormState, tornado)); } } } diff --git a/QSB/Player/KickReason.cs b/QSB/Player/KickReason.cs index d02b4159..5b44b066 100644 --- a/QSB/Player/KickReason.cs +++ b/QSB/Player/KickReason.cs @@ -5,6 +5,7 @@ None, QSBVersionNotMatching, GameVersionNotMatching, - GamePlatformNotMatching + GamePlatformNotMatching, + DLCNotMatching } } diff --git a/QSB/Player/PlayerHUDMarker.cs b/QSB/Player/PlayerHUDMarker.cs index 30f4d5a1..0e411f5f 100644 --- a/QSB/Player/PlayerHUDMarker.cs +++ b/QSB/Player/PlayerHUDMarker.cs @@ -21,7 +21,6 @@ namespace QSB.Player public void Init(PlayerInfo player) { - DebugLog.DebugWrite($"Init {player.PlayerId} name:{player.Name}"); _player = player; _player.HudMarker = this; _needsInitializing = true; diff --git a/QSB/Player/PlayerInfo.cs b/QSB/Player/PlayerInfo.cs index 1b51d7a8..da426922 100644 --- a/QSB/Player/PlayerInfo.cs +++ b/QSB/Player/PlayerInfo.cs @@ -142,7 +142,7 @@ namespace QSB.Player return null; } - return CameraBody.transform.Find("ProbeLauncher").GetComponent(); + return CameraBody?.transform.Find("ProbeLauncher").GetComponent(); } } @@ -170,7 +170,7 @@ namespace QSB.Player return null; } - return CameraBody.transform.Find("Signalscope").GetComponent(); + return CameraBody?.transform.Find("Signalscope").GetComponent(); } } @@ -184,7 +184,7 @@ namespace QSB.Player return null; } - return CameraBody.transform.Find("NomaiTranslatorProp").GetComponent(); + return CameraBody?.transform.Find("NomaiTranslatorProp").GetComponent(); } } @@ -197,6 +197,12 @@ namespace QSB.Player public void UpdateObjectsFromStates() { if (OWInput.GetInputMode() == InputMode.None) + { + // ? why is this here lmao + return; + } + + if (CameraBody == null) { return; } @@ -225,7 +231,30 @@ namespace QSB.Player QSBEventManager.FireEvent(EventNames.QSBPlayerInformation); } - private QSBTool GetToolByType(ToolType type) => CameraBody?.GetComponentsInChildren() - .FirstOrDefault(x => x.Type == type); + private QSBTool GetToolByType(ToolType type) + { + if (CameraBody == null) + { + DebugLog.ToConsole($"Warning - Tried to GetToolByType({type}) on player {PlayerId}, but CameraBody was null.", MessageType.Warning); + return null; + } + + var tools = CameraBody.GetComponentsInChildren(); + + if (tools == null || tools.Length == 0) + { + DebugLog.ToConsole($"Warning - Couldn't find any QSBTools for player {PlayerId}.", MessageType.Warning); + return null; + } + + var tool = tools.FirstOrDefault(x => x.Type == type); + + if (tool == null) + { + DebugLog.ToConsole($"Warning - No tool found on player {PlayerId} matching ToolType {type}.", MessageType.Warning); + } + + return tool; + } } } \ No newline at end of file diff --git a/QSB/Player/QSBPlayerManager.cs b/QSB/Player/QSBPlayerManager.cs index 35fb95fb..6895a1da 100644 --- a/QSB/Player/QSBPlayerManager.cs +++ b/QSB/Player/QSBPlayerManager.cs @@ -89,7 +89,7 @@ namespace QSB.Player player.SignalscopeEquipped = message.SignalscopeEquipped; player.TranslatorEquipped = message.TranslatorEquipped; player.ProbeActive = message.ProbeActive; - if (LocalPlayer.IsReady) + if (LocalPlayer.IsReady && player.IsReady) { player.UpdateObjectsFromStates(); } @@ -178,7 +178,7 @@ namespace QSB.Player return null; } - return playerList.Where(x => x.IsReady).OrderBy(x => Vector3.Distance(x.Body.transform.position, worldPoint)).FirstOrDefault(); + return playerList.Where(x => x.IsReady && x.Body != null).OrderBy(x => Vector3.Distance(x.Body.transform.position, worldPoint)).FirstOrDefault(); } public static IEnumerable> GetPlayerCarryItems() diff --git a/QSB/Player/TransformSync/PlayerTransformSync.cs b/QSB/Player/TransformSync/PlayerTransformSync.cs index e82cfac7..8c90ead7 100644 --- a/QSB/Player/TransformSync/PlayerTransformSync.cs +++ b/QSB/Player/TransformSync/PlayerTransformSync.cs @@ -17,6 +17,8 @@ namespace QSB.Player.TransformSync { static PlayerTransformSync() => AnimControllerPatch.Init(); + public override bool IsPlayerObject => true; + private Transform _visibleCameraRoot; private Transform _networkCameraRoot => gameObject.transform.GetChild(0); @@ -58,12 +60,13 @@ namespace QSB.Player.TransformSync base.OnSceneLoaded(oldScene, newScene, isInUniverse); } - if (isInUniverse) + if (isInUniverse && !_isInitialized) { - Player.IsReady = true; - QSBEventManager.FireEvent(EventNames.QSBPlayerReady, true); + Player.IsReady = false; + QSBEventManager.FireEvent(EventNames.QSBPlayerReady, false); } - else + + if (!isInUniverse) { Player.IsReady = false; QSBEventManager.FireEvent(EventNames.QSBPlayerReady, false); @@ -72,6 +75,14 @@ namespace QSB.Player.TransformSync base.OnSceneLoaded(oldScene, newScene, isInUniverse); } + protected override void Init() + { + base.Init(); + + Player.IsReady = true; + QSBEventManager.FireEvent(EventNames.QSBPlayerReady, true); + } + protected override void OnDestroy() { // TODO : Maybe move this to a leave event...? Would ensure everything could finish up before removing the player diff --git a/QSB/QSBCore.cs b/QSB/QSBCore.cs index 191e0d09..55e7ce70 100644 --- a/QSB/QSBCore.cs +++ b/QSB/QSBCore.cs @@ -1,5 +1,4 @@ -using System.Linq; -using OWML.Common; +using OWML.Common; using OWML.ModHelper; using OWML.ModHelper.Input; using QSB.ConversationSync; @@ -15,6 +14,7 @@ using QSB.Utility; using QSB.WorldSync; using QuantumUNET; using QuantumUNET.Components; +using System.Linq; using UnityEngine; /* @@ -49,7 +49,7 @@ namespace QSB public static bool ShowQuantumDebugBoxes => DebugMode && DebugSettings.ShowQuantumDebugBoxes; public static bool AvoidTimeSync => DebugMode && DebugSettings.AvoidTimeSync; public static bool SkipTitleScreen => DebugMode && DebugSettings.SkipTitleScreen; - public static AssetBundle NetworkAssetBundle { get; private set; } + public static AssetBundle NetworkAssetBundle { get; internal set; } public static AssetBundle InstrumentAssetBundle { get; private set; } public static AssetBundle ConversationAssetBundle { get; private set; } public static AssetBundle DebugAssetBundle { get; private set; } @@ -60,6 +60,7 @@ namespace QSB public static GamePlatform Platform => typeof(Achievements).Assembly.GetTypes().Any(x => x.Name == "EpicEntitlementRetriever") ? GamePlatform.Epic : GamePlatform.Steam; + public static bool DLCInstalled => EntitlementsManager.IsDlcOwned() == EntitlementsManager.AsyncOwnershipStatus.Owned; public static IMenuAPI MenuApi { get; private set; } private static DebugSettings DebugSettings { get; set; } = new DebugSettings(); diff --git a/QSB/QSBNetworkManager.cs b/QSB/QSBNetworkManager.cs index 8917f6c5..4c6af38e 100644 --- a/QSB/QSBNetworkManager.cs +++ b/QSB/QSBNetworkManager.cs @@ -1,11 +1,11 @@ -using System; -using System.Linq; -using OWML.Common; +using OWML.Common; using OWML.Utils; +using QSB.Anglerfish.TransformSync; using QSB.AuthoritySync; using QSB.ClientServerStateSync; using QSB.DeathSync; using QSB.Events; +using QSB.JellyfishSync.TransformSync; using QSB.Messaging; using QSB.OrbSync.TransformSync; using QSB.Patches; @@ -14,10 +14,14 @@ using QSB.Player.TransformSync; using QSB.PoolSync; using QSB.ShipSync.TransformSync; using QSB.TimeSync; +using QSB.Tools.ProbeTool.TransformSync; +using QSB.TornadoSync.TransformSync; using QSB.Utility; using QSB.WorldSync; using QuantumUNET; using QuantumUNET.Components; +using System; +using System.Linq; using UnityEngine; using UnityEngine.Networking; @@ -37,12 +41,12 @@ namespace QSB public GameObject ShipPrefab { get; private set; } public GameObject AnglerPrefab { get; private set; } public GameObject JellyfishPrefab { get; private set; } + public GameObject OccasionalPrefab { 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; @@ -52,25 +56,27 @@ namespace QSB Instance = this; PlayerName = GetPlayerName(); - _assetBundle = QSBCore.NetworkAssetBundle; - playerPrefab = _assetBundle.LoadAsset("Assets/Prefabs/NETWORK_Player_Body.prefab"); + playerPrefab = QSBCore.NetworkAssetBundle.LoadAsset("Assets/Prefabs/NETWORK_Player_Body.prefab"); - ShipPrefab = _assetBundle.LoadAsset("assets/Prefabs/networkship.prefab"); + ShipPrefab = MakeNewNetworkObject(2, "NetworkShip", typeof(ShipTransformSync)); spawnPrefabs.Add(ShipPrefab); - _probePrefab = _assetBundle.LoadAsset("assets/Prefabs/networkprobe.prefab"); + _probePrefab = MakeNewNetworkObject(3, "NetworkProbe", typeof(PlayerProbeSync)); spawnPrefabs.Add(_probePrefab); - OrbPrefab = _assetBundle.LoadAsset("assets/Prefabs/networkorb.prefab"); + OrbPrefab = MakeNewNetworkObject(4, "NetworkOrb", typeof(NomaiOrbTransformSync)); spawnPrefabs.Add(OrbPrefab); - AnglerPrefab = _assetBundle.LoadAsset("assets/Prefabs/networkangler.prefab"); + AnglerPrefab = MakeNewNetworkObject(5, "NetworkAngler", typeof(AnglerTransformSync)); spawnPrefabs.Add(AnglerPrefab); - JellyfishPrefab = _assetBundle.LoadAsset("assets/Prefabs/networkjellyfish.prefab"); + JellyfishPrefab = MakeNewNetworkObject(6, "NetworkJellyfish", typeof(JellyfishTransformSync)); spawnPrefabs.Add(JellyfishPrefab); + OccasionalPrefab = MakeNewNetworkObject(7, "NetworkOccasional", typeof(OccasionalTransformSync)); + spawnPrefabs.Add(OccasionalPrefab); + ConfigureNetworkManager(); } @@ -91,6 +97,24 @@ namespace QSB } } + /// create a new network prefab from the network object prefab template. + /// this works by calling Unload(false) and then reloading the AssetBundle, + /// which makes LoadAsset give you a new resource. + /// see https://docs.unity3d.com/Manual/AssetBundles-Native.html. + private static GameObject MakeNewNetworkObject(int assetId, string name, Type transformSyncType) + { + QSBCore.NetworkAssetBundle.Unload(false); + QSBCore.NetworkAssetBundle = QSBCore.Helper.Assets.LoadBundle("AssetBundles/network"); + + var template = QSBCore.NetworkAssetBundle.LoadAsset("Assets/Prefabs/NetworkObject.prefab"); + DebugLog.DebugWrite($"MakeNewNetworkObject - prefab id {template.GetInstanceID()} " + + $"for {assetId} {name} {transformSyncType.Name}"); + template.name = name; + template.GetRequiredComponent().SetValue("m_AssetId", assetId); + template.AddComponent(transformSyncType); + return template; + } + private void ConfigureNetworkManager() { networkAddress = QSBCore.DefaultServerIP; diff --git a/QSB/QuantumSync/Events/MoonStateChangeEvent.cs b/QSB/QuantumSync/Events/MoonStateChangeEvent.cs index b4332b6b..6f2c82a3 100644 --- a/QSB/QuantumSync/Events/MoonStateChangeEvent.cs +++ b/QSB/QuantumSync/Events/MoonStateChangeEvent.cs @@ -2,7 +2,6 @@ using QSB.Events; using System.Linq; using System.Reflection; -using QSB.WorldSync; using UnityEngine; namespace QSB.QuantumSync.Events diff --git a/QSB/QuantumSync/WorldObjects/QSBMultiStateQuantumObject.cs b/QSB/QuantumSync/WorldObjects/QSBMultiStateQuantumObject.cs index a35c019c..5bb22d7e 100644 --- a/QSB/QuantumSync/WorldObjects/QSBMultiStateQuantumObject.cs +++ b/QSB/QuantumSync/WorldObjects/QSBMultiStateQuantumObject.cs @@ -1,7 +1,7 @@ -using System.Collections.Generic; -using System.Linq; -using QSB.Utility; +using QSB.Utility; using QSB.WorldSync; +using System.Collections.Generic; +using System.Linq; using UnityEngine.UI; namespace QSB.QuantumSync.WorldObjects diff --git a/QSB/QuantumSync/WorldObjects/QSBQuantumObject.cs b/QSB/QuantumSync/WorldObjects/QSBQuantumObject.cs index 8005135a..87c42343 100644 --- a/QSB/QuantumSync/WorldObjects/QSBQuantumObject.cs +++ b/QSB/QuantumSync/WorldObjects/QSBQuantumObject.cs @@ -112,7 +112,7 @@ namespace QSB.QuantumSync.WorldObjects } } - public List GetVisibilityTrackers() + public List GetVisibilityTrackers() => AttachedObject?._visibilityTrackers == null ? new() : AttachedObject._visibilityTrackers.Select(x => (ShapeVisibilityTracker)x).ToList(); diff --git a/QSB/RespawnSync/RespawnManager.cs b/QSB/RespawnSync/RespawnManager.cs index 917b681d..a2f26748 100644 --- a/QSB/RespawnSync/RespawnManager.cs +++ b/QSB/RespawnSync/RespawnManager.cs @@ -56,6 +56,12 @@ namespace QSB.RespawnSync return; } + if (PlayerTransformSync.LocalInstance == null) + { + DebugLog.ToConsole($"Error - Tried to init when PlayerTransformSync.LocalInstance was null!", OWML.Common.MessageType.Error); + return; + } + QSBPlayerManager.ShowAllPlayers(); QSBPlayerManager.LocalPlayer.UpdateStatesFromObjects(); QSBPlayerManager.PlayerList.ForEach(x => x.IsDead = false); diff --git a/QSB/SaveSync/Events/GameStateEvent.cs b/QSB/SaveSync/Events/GameStateEvent.cs new file mode 100644 index 00000000..b8b257fc --- /dev/null +++ b/QSB/SaveSync/Events/GameStateEvent.cs @@ -0,0 +1,47 @@ +using QSB.Events; +using QSB.Menus; +using QSB.Utility; + +namespace QSB.SaveSync.Events +{ + // only to be sent from host + internal class GameStateEvent : QSBEvent + { + public override bool RequireWorldObjectsReady => false; + + public override void SetupListener() => GlobalMessenger.AddListener(EventNames.QSBGameDetails, Handler); + public override void CloseListener() => GlobalMessenger.RemoveListener(EventNames.QSBGameDetails, Handler); + + private void Handler(uint toId) => SendEvent(CreateMessage(toId)); + + private GameStateMessage CreateMessage(uint toId) => new() + { + AboutId = LocalPlayerId, + ForId = toId, + InSolarSystem = QSBSceneManager.CurrentScene == OWScene.SolarSystem, + InEye = QSBSceneManager.CurrentScene == OWScene.EyeOfTheUniverse, + LaunchCodesGiven = PlayerData.KnowsLaunchCodes(), + LoopCount = StandaloneProfileManager.SharedInstance.currentProfileGameSave.loopCount, + KnownFrequencies = StandaloneProfileManager.SharedInstance.currentProfileGameSave.knownFrequencies, + KnownSignals = StandaloneProfileManager.SharedInstance.currentProfileGameSave.knownSignals + }; + + public override void OnReceiveRemote(bool isHost, GameStateMessage message) + { + var gameSave = StandaloneProfileManager.SharedInstance.currentProfileGameSave; + gameSave.loopCount = message.LoopCount; + gameSave.knownFrequencies = message.KnownFrequencies; + gameSave.knownSignals = message.KnownSignals; + + PlayerData.SetPersistentCondition("LAUNCH_CODES_GIVEN", message.LaunchCodesGiven); + + PlayerData.SaveCurrentGame(); + + if (message.InEye != (QSBSceneManager.CurrentScene == OWScene.EyeOfTheUniverse) + || message.InSolarSystem != (QSBSceneManager.CurrentScene == OWScene.SolarSystem)) + { + MenuManager.Instance.JoinGame(message.InEye, message.InSolarSystem); + } + } + } +} diff --git a/QSB/SaveSync/Events/GameStateMessage.cs b/QSB/SaveSync/Events/GameStateMessage.cs new file mode 100644 index 00000000..74755a57 --- /dev/null +++ b/QSB/SaveSync/Events/GameStateMessage.cs @@ -0,0 +1,66 @@ +using QSB.Messaging; +using QuantumUNET.Transport; +using System; +using System.Collections.Generic; + +namespace QSB.SaveSync.Events +{ + internal class GameStateMessage : PlayerMessage + { + public bool InSolarSystem { get; set; } + public bool InEye { get; set; } + public bool LaunchCodesGiven { get; set; } + public int LoopCount { get; set; } + public bool[] KnownFrequencies { get; set; } + public Dictionary KnownSignals { get; set; } = new(); + + public override void Deserialize(QNetworkReader reader) + { + base.Deserialize(reader); + InSolarSystem = reader.ReadBoolean(); + InEye = reader.ReadBoolean(); + LaunchCodesGiven = reader.ReadBoolean(); + LoopCount = reader.ReadInt32(); + + var frequenciesLength = reader.ReadInt32(); + var knownFrequencies = KnownFrequencies; + Array.Resize(ref knownFrequencies, frequenciesLength); + KnownFrequencies = knownFrequencies; + for (var i = 0; i < frequenciesLength; i++) + { + KnownFrequencies[i] = reader.ReadBoolean(); + } + + var signalsLength = reader.ReadInt32(); + KnownSignals.Clear(); + for (var i = 0; i < signalsLength; i++) + { + var key = reader.ReadInt32(); + var value = reader.ReadBoolean(); + KnownSignals.Add(key, value); + } + } + + public override void Serialize(QNetworkWriter writer) + { + base.Serialize(writer); + writer.Write(InSolarSystem); + writer.Write(InEye); + writer.Write(LaunchCodesGiven); + writer.Write(LoopCount); + + writer.Write(KnownFrequencies.Length); + foreach (var item in KnownFrequencies) + { + writer.Write(item); + } + + writer.Write(KnownSignals.Count); + foreach (var item in KnownSignals) + { + writer.Write(item.Key); + writer.Write(item.Value); + } + } + } +} diff --git a/QSB/SaveSync/Events/RequestGameStateEvent.cs b/QSB/SaveSync/Events/RequestGameStateEvent.cs new file mode 100644 index 00000000..94f78312 --- /dev/null +++ b/QSB/SaveSync/Events/RequestGameStateEvent.cs @@ -0,0 +1,23 @@ +using QSB.Events; +using QSB.Messaging; + +namespace QSB.SaveSync.Events +{ + internal class RequestGameStateEvent : QSBEvent + { + public override bool RequireWorldObjectsReady => false; + + public override void SetupListener() => GlobalMessenger.AddListener(EventNames.QSBRequestGameDetails, Handler); + public override void CloseListener() => GlobalMessenger.RemoveListener(EventNames.QSBRequestGameDetails, Handler); + + private void Handler() => SendEvent(CreateMessage()); + + private PlayerMessage CreateMessage() => new() + { + AboutId = LocalPlayerId, + OnlySendToHost = true + }; + + public override void OnReceiveRemote(bool isHost, PlayerMessage message) => QSBEventManager.FireEvent(EventNames.QSBGameDetails, message.FromId); + } +} diff --git a/QSB/SectorSync/SectorSync.cs b/QSB/SectorSync/SectorSync.cs index ed1f9895..09ca9ec9 100644 --- a/QSB/SectorSync/SectorSync.cs +++ b/QSB/SectorSync/SectorSync.cs @@ -127,7 +127,6 @@ namespace QSB.SectorSync if (_sectorDetector == null || _attachedOWRigidbody == null || _targetType == TargetType.None) { IsReady = false; - DebugLog.ToConsole($"Error - SectorSync is no longer ready. Detector Null : {_sectorDetector == null}, OWRigidbody Null : {_attachedOWRigidbody == null}, None TargetType : {_targetType == TargetType.None}", MessageType.Error); return null; } diff --git a/QSB/ShipSync/Patches/ShipPatches.cs b/QSB/ShipSync/Patches/ShipPatches.cs index 9d786168..663dc505 100644 --- a/QSB/ShipSync/Patches/ShipPatches.cs +++ b/QSB/ShipSync/Patches/ShipPatches.cs @@ -3,8 +3,8 @@ using OWML.Utils; using QSB.Events; using QSB.Patches; using QSB.Utility; -using System; using QSB.WorldSync; +using System; using UnityEngine; namespace QSB.ShipSync.Patches diff --git a/QSB/ShipSync/TransformSync/ShipTransformSync.cs b/QSB/ShipSync/TransformSync/ShipTransformSync.cs index d5f910ed..9921365d 100644 --- a/QSB/ShipSync/TransformSync/ShipTransformSync.cs +++ b/QSB/ShipSync/TransformSync/ShipTransformSync.cs @@ -10,6 +10,8 @@ namespace QSB.ShipSync.TransformSync { public static ShipTransformSync LocalInstance { get; private set; } + public override bool IsPlayerObject => false; + private const int ForcePositionAfterUpdates = 50; private int _updateCount; diff --git a/QSB/StatueSync/Events/StartStatueEvent.cs b/QSB/StatueSync/Events/StartStatueEvent.cs index f9e95ecc..c0df8d01 100644 --- a/QSB/StatueSync/Events/StartStatueEvent.cs +++ b/QSB/StatueSync/Events/StartStatueEvent.cs @@ -28,8 +28,6 @@ namespace QSB.StatueSync.Events public override void OnReceiveLocal(bool server, StartStatueMessage message) { - DebugLog.DebugWrite($"OnReceiveLocal StartStatueEvent"); - if (!QSBCore.IsHost) { return; @@ -40,8 +38,14 @@ namespace QSB.StatueSync.Events public override void OnReceiveRemote(bool server, StartStatueMessage message) { - DebugLog.DebugWrite($"OnReceiveRemote StartStatueEvent"); StatueManager.Instance.BeginSequence(message.PlayerPosition, message.PlayerRotation, message.CameraDegrees); + + if (!QSBCore.IsHost) + { + return; + } + + QSBEventManager.FireEvent(EventNames.QSBServerState, ServerState.InStatueCutscene); } } } diff --git a/QSB/StatueSync/StatueManager.cs b/QSB/StatueSync/StatueManager.cs index 6ee6ebee..62ed255c 100644 --- a/QSB/StatueSync/StatueManager.cs +++ b/QSB/StatueSync/StatueManager.cs @@ -1,4 +1,6 @@ using QSB.Player; +using QSB.Player.TransformSync; +using QSB.Utility; using System.Collections; using UnityEngine; @@ -25,6 +27,12 @@ namespace QSB.StatueSync return; } + if (PlayerTransformSync.LocalInstance == null) + { + DebugLog.ToConsole($"Error - Tried to run OnUniverseSceneLoaded when PlayerTransformSync.LocalInstance was null!", OWML.Common.MessageType.Error); + return; + } + QSBPlayerManager.ShowAllPlayers(); QSBPlayerManager.LocalPlayer.UpdateStatesFromObjects(); } diff --git a/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs b/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs index 460bcf1e..4a547b9d 100644 --- a/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs +++ b/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs @@ -94,10 +94,20 @@ namespace QSB.Syncs.Sectored.Rigidbodies protected void SetValuesToSync() { - transform.position = ReferenceTransform.EncodePos(AttachedObject.transform.position); - transform.rotation = ReferenceTransform.EncodeRot(AttachedObject.transform.rotation); - _relativeVelocity = ReferenceTransform.GetAttachedOWRigidbody().EncodeVel(((OWRigidbody)AttachedObject).GetVelocity(), AttachedObject.transform.position); - _relativeAngularVelocity = ReferenceTransform.GetAttachedOWRigidbody().EncodeAngVel(((OWRigidbody)AttachedObject).GetAngularVelocity()); + if (ReferenceTransform != null) + { + transform.position = ReferenceTransform.EncodePos(AttachedObject.transform.position); + transform.rotation = ReferenceTransform.EncodeRot(AttachedObject.transform.rotation); + _relativeVelocity = ReferenceTransform.GetAttachedOWRigidbody().EncodeVel(((OWRigidbody)AttachedObject).GetVelocity(), AttachedObject.transform.position); + _relativeAngularVelocity = ReferenceTransform.GetAttachedOWRigidbody().EncodeAngVel(((OWRigidbody)AttachedObject).GetAngularVelocity()); + } + else + { + transform.position = Vector3.zero; + transform.rotation = Quaternion.identity; + _relativeVelocity = Vector3.zero; + _relativeAngularVelocity = Vector3.zero; + } } protected override bool UpdateTransform() diff --git a/QSB/Syncs/SyncBase.cs b/QSB/Syncs/SyncBase.cs index 1a25d7d8..5409c977 100644 --- a/QSB/Syncs/SyncBase.cs +++ b/QSB/Syncs/SyncBase.cs @@ -18,28 +18,6 @@ namespace QSB.Syncs public abstract class SyncBase : QNetworkTransform { - private static readonly Dictionary> _storedTransformSyncs = new(); - - public static T GetPlayers(PlayerInfo player) - where T : SyncBase - { - var dictOfOwnedSyncs = _storedTransformSyncs[player.PlayerId]; - var wantedSync = dictOfOwnedSyncs[typeof(T)]; - if (wantedSync == default) - { - DebugLog.ToConsole($"Error - _storedTransformSyncs does not contain type:{typeof(T)} under player {player.PlayerId}. Attempting to find manually...", MessageType.Error); - var allSyncs = QSBWorldSync.GetUnityObjects(); - wantedSync = allSyncs.First(x => x.Player == player); - if (wantedSync == default) - { - DebugLog.ToConsole($"Error - Could not find type:{typeof(T)} for player {player.PlayerId} manually!", MessageType.Error); - return default; - } - } - - return (T)wantedSync; - } - public uint AttachedNetId { get @@ -58,6 +36,11 @@ namespace QSB.Syncs { get { + if (!IsPlayerObject) + { + return uint.MaxValue; + } + if (NetIdentity == null) { DebugLog.ToConsole($"Error - Trying to get PlayerId with null NetIdentity! Type:{GetType().Name} GrandType:{GetType().GetType().Name}", MessageType.Error); @@ -71,17 +54,49 @@ namespace QSB.Syncs } public PlayerInfo Player => QSBPlayerManager.GetPlayer(PlayerId); - private bool _baseIsReady => QSBPlayerManager.PlayerExists(PlayerId) - && Player != null - // && Player.IsReady - && NetId.Value != uint.MaxValue - && NetId.Value != 0U - && WorldObjectManager.AllObjectsAdded; + + private bool _baseIsReady + { + get + { + if (NetId.Value is uint.MaxValue or 0U) + { + return false; + } + + if (!WorldObjectManager.AllObjectsAdded) + { + return false; + } + + if (IsPlayerObject) + { + if (!QSBPlayerManager.PlayerExists(PlayerId)) + { + return false; + } + + if (Player == null) + { + return false; + } + + if (!Player.IsReady && !IsLocalPlayer) + { + return false; + } + } + + return true; + } + } + public abstract bool IsReady { get; } public abstract bool UseInterpolation { get; } public abstract bool IgnoreDisabledAttachedObject { get; } public abstract bool IgnoreNullReferenceTransform { get; } public abstract bool ShouldReparentAttachedObject { get; } + public abstract bool IsPlayerObject { get; } public Component AttachedObject { get; set; } public Transform ReferenceTransform { get; set; } @@ -99,26 +114,15 @@ namespace QSB.Syncs public virtual void Start() { - var lowestBound = QSBWorldSync.GetUnityObjects() + if (IsPlayerObject) + { + var lowestBound = QSBWorldSync.GetUnityObjects() .Where(x => x.NetId.Value <= NetId.Value).OrderBy(x => x.NetId.Value).Last(); - NetIdentity.SetRootIdentity(lowestBound.NetIdentity); + NetIdentity.SetRootIdentity(lowestBound.NetIdentity); + } DontDestroyOnLoad(gameObject); QSBSceneManager.OnSceneLoaded += OnSceneLoaded; - - if (Player == null) - { - DebugLog.ToConsole($"Error - Player in start of {LogName} was null!", MessageType.Error); - return; - } - - if (!_storedTransformSyncs.ContainsKey(PlayerId)) - { - _storedTransformSyncs.Add(PlayerId, new Dictionary()); - } - - var playerDict = _storedTransformSyncs[PlayerId]; - playerDict[GetType()] = this; } protected virtual void OnDestroy() @@ -132,14 +136,6 @@ namespace QSB.Syncs } QSBSceneManager.OnSceneLoaded -= OnSceneLoaded; - - if (!QSBPlayerManager.PlayerExists(PlayerId)) - { - return; - } - - var playerDict = _storedTransformSyncs[PlayerId]; - playerDict.Remove(GetType()); } protected virtual void Init() @@ -168,7 +164,6 @@ namespace QSB.Syncs { if (!_isInitialized && IsReady && _baseIsReady) { - try { Init(); diff --git a/QSB/Syncs/Unsectored/Transforms/UnsectoredTransformSync.cs b/QSB/Syncs/Unsectored/Transforms/UnsectoredTransformSync.cs index 867952c9..36709e64 100644 --- a/QSB/Syncs/Unsectored/Transforms/UnsectoredTransformSync.cs +++ b/QSB/Syncs/Unsectored/Transforms/UnsectoredTransformSync.cs @@ -1,5 +1,4 @@ -using OWML.Common; -using QSB.Utility; +using QSB.Utility; using QSB.WorldSync; using QuantumUNET.Transport; using UnityEngine; diff --git a/QSB/TimeSync/WakeUpSync.cs b/QSB/TimeSync/WakeUpSync.cs index c3ff6069..4cc99d05 100644 --- a/QSB/TimeSync/WakeUpSync.cs +++ b/QSB/TimeSync/WakeUpSync.cs @@ -153,6 +153,11 @@ namespace QSB.TimeSync var myTime = Time.timeSinceLevelLoad; var diff = myTime - _serverTime; + if (ServerStateManager.Instance.GetServerState() is not ServerState.InSolarSystem and not ServerState.InEye) + { + return; + } + if (diff > PauseOrFastForwardThreshold) { StartPausing(PauseReason.TooFarAhead); @@ -272,7 +277,6 @@ namespace QSB.TimeSync { if (CurrentState != State.Pausing) { - DebugLog.DebugWrite($"Wait for other clients to be ready"); StartPausing(PauseReason.WaitingForAllPlayersToBeReady); } } @@ -281,7 +285,6 @@ namespace QSB.TimeSync { if (clientState == ClientState.AliveInSolarSystem && serverState == ServerState.InSolarSystem) { - DebugLog.DebugWrite($"start of new loop!"); ResetTimeScale(); } } @@ -346,24 +349,16 @@ namespace QSB.TimeSync if (serverState == ServerState.NotLoaded && CurrentState != State.Pausing && QSBSceneManager.IsInUniverse) { - DebugLog.DebugWrite($"Server Not Loaded"); StartPausing(PauseReason.ServerNotStarted); } if (serverState == ServerState.WaitingForAllPlayersToReady && CurrentState != State.Pausing && clientState == ClientState.WaitingForOthersToReadyInSolarSystem) { - DebugLog.DebugWrite($"Awaiting Play Confirmation"); StartPausing(PauseReason.WaitingForAllPlayersToBeReady); } - if (serverState == ServerState.InSolarSystem && (clientState == ClientState.WaitingForOthersToReadyInSolarSystem || clientState == ClientState.WaitingForOthersToDieInSolarSystem)) - { - DebugLog.DebugWrite($"Server is still running game normally, but this player has died from an accepted death!", MessageType.Warning); - } - if (serverState == ServerState.WaitingForAllPlayersToDie && clientState == ClientState.WaitingForOthersToReadyInSolarSystem) { - DebugLog.DebugWrite($"Wait for others to load new scene"); StartPausing(PauseReason.WaitingForAllPlayersToBeReady); } @@ -373,7 +368,6 @@ namespace QSB.TimeSync { if (serverState != ServerState.NotLoaded) { - DebugLog.DebugWrite($"Server started!"); ResetTimeScale(); } } @@ -382,7 +376,6 @@ namespace QSB.TimeSync { if (clientState == ClientState.AliveInSolarSystem && serverState == ServerState.InSolarSystem) { - DebugLog.DebugWrite($"start of new loop!"); ResetTimeScale(); } } @@ -391,7 +384,6 @@ namespace QSB.TimeSync { if (Time.timeSinceLevelLoad <= _serverTime) { - DebugLog.DebugWrite($"Done pausing to match time!"); ResetTimeScale(); } } @@ -400,7 +392,6 @@ namespace QSB.TimeSync { if (Time.timeSinceLevelLoad >= _serverTime) { - DebugLog.DebugWrite($"Done fast-forwarding to match time!"); ResetTimeScale(); } } diff --git a/QSB/Tools/ProbeTool/TransformSync/PlayerProbeSync.cs b/QSB/Tools/ProbeTool/TransformSync/PlayerProbeSync.cs index 7eb63bd3..d2ea936b 100644 --- a/QSB/Tools/ProbeTool/TransformSync/PlayerProbeSync.cs +++ b/QSB/Tools/ProbeTool/TransformSync/PlayerProbeSync.cs @@ -15,6 +15,7 @@ namespace QSB.Tools.ProbeTool.TransformSync protected override float DistanceLeeway => 10f; public override bool UseInterpolation => true; public override bool IgnoreDisabledAttachedObject => true; + public override bool IsPlayerObject => true; public static PlayerProbeSync LocalInstance { get; private set; } diff --git a/QSB/TornadoSync/Events/TornadoFormStateEvent.cs b/QSB/TornadoSync/Events/TornadoFormStateEvent.cs new file mode 100644 index 00000000..6e5c9c21 --- /dev/null +++ b/QSB/TornadoSync/Events/TornadoFormStateEvent.cs @@ -0,0 +1,32 @@ +using QSB.Events; +using QSB.TornadoSync.WorldObjects; +using QSB.WorldSync; +using QSB.WorldSync.Events; + +namespace QSB.TornadoSync.Events +{ + public class TornadoFormStateEvent : QSBEvent + { + public override bool RequireWorldObjectsReady => true; + + public override void SetupListener() + => GlobalMessenger.AddListener(EventNames.QSBTornadoFormState, Handler); + + public override void CloseListener() + => GlobalMessenger.RemoveListener(EventNames.QSBTornadoFormState, Handler); + + private void Handler(QSBTornado qsbTornado) => SendEvent(CreateMessage(qsbTornado)); + + private BoolWorldObjectMessage CreateMessage(QSBTornado qsbTornado) => new() + { + ObjectId = qsbTornado.ObjectId, + State = qsbTornado.FormState + }; + + public override void OnReceiveRemote(bool isHost, BoolWorldObjectMessage message) + { + var qsbTornado = QSBWorldSync.GetWorldFromId(message.ObjectId); + qsbTornado.FormState = message.State; + } + } +} diff --git a/QSB/TornadoSync/Patches/TornadoPatches.cs b/QSB/TornadoSync/Patches/TornadoPatches.cs new file mode 100644 index 00000000..ca6ad2f1 --- /dev/null +++ b/QSB/TornadoSync/Patches/TornadoPatches.cs @@ -0,0 +1,71 @@ +using HarmonyLib; +using QSB.Events; +using QSB.Patches; +using QSB.TornadoSync.WorldObjects; +using QSB.WorldSync; +using UnityEngine; + +namespace QSB.TornadoSync.Patches +{ + public class TornadoPatches : QSBPatch + { + public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; + + [HarmonyPrefix] + [HarmonyPatch(typeof(TornadoController), nameof(TornadoController.FixedUpdate))] + public static bool FixedUpdate(TornadoController __instance) + { + if (QSBCore.IsHost && __instance._secondsUntilFormation > 0f) + { + __instance._secondsUntilFormation -= Time.fixedDeltaTime; + if (__instance._secondsUntilFormation < 0f) + { + __instance.StartFormation(); + var qsbTornado = QSBWorldSync.GetWorldFromUnity(__instance); + QSBEventManager.FireEvent(EventNames.QSBTornadoFormState, qsbTornado); + return false; + } + } + else + { + if (__instance._tornadoCollapsing) + { + __instance.UpdateCollapse(); + } + else if (__instance._tornadoForming) + { + __instance.UpdateFormation(); + } + if (__instance._isSectorOccupied) + { + __instance.UpdateAnimation(); + if (__instance._wander) + { + var num = Mathf.PerlinNoise(Time.time * __instance._wanderRate, 0f) * 2f - 1f; + var num2 = Mathf.PerlinNoise(Time.time * __instance._wanderRate, 5f) * 2f - 1f; + var localEulerAngles = new Vector3(num * __instance._wanderDegreesX, 0f, num2 * __instance._wanderDegreesZ); + __instance.transform.localEulerAngles = localEulerAngles; + } + } + } + + return false; + } + + + [HarmonyPrefix] + [HarmonyPatch(typeof(TornadoController), nameof(TornadoController.OnEnterCollapseTrigger))] + public static bool OnEnterCollapseTrigger(TornadoController __instance, + GameObject hitObject) + { + if (QSBCore.IsHost && hitObject.GetComponentInParent().GetMass() > 50f) + { + __instance.StartCollapse(); + var qsbTornado = QSBWorldSync.GetWorldFromUnity(__instance); + QSBEventManager.FireEvent(EventNames.QSBTornadoFormState, qsbTornado); + } + + return false; + } + } +} diff --git a/QSB/TornadoSync/TornadoManager.cs b/QSB/TornadoSync/TornadoManager.cs new file mode 100644 index 00000000..e07ec13d --- /dev/null +++ b/QSB/TornadoSync/TornadoManager.cs @@ -0,0 +1,49 @@ +using QSB.TornadoSync.TransformSync; +using QSB.TornadoSync.WorldObjects; +using QSB.Utility; +using QSB.WorldSync; +using QuantumUNET; + +namespace QSB.TornadoSync +{ + public class TornadoManager : WorldObjectManager + { + protected override void RebuildWorldObjects(OWScene scene) + { + QSBWorldSync.Init(); + + if (!QSBCore.IsHost) + { + return; + } + + foreach (var transformSync in QSBWorldSync.GetUnityObjects()) + { + QNetworkServer.Destroy(transformSync.gameObject); + } + + var gdBody = Locator._giantsDeep.GetOWRigidbody(); + // cannon + var cannon = Locator._orbitalProbeCannon.GetRequiredComponent(); + SpawnOccasional(cannon.GetAttachedOWRigidbody(), gdBody); + foreach (var proxy in cannon._realDebrisSectorProxies) + { + SpawnOccasional(proxy.transform.root.GetAttachedOWRigidbody(), gdBody); + } + SpawnOccasional(cannon._probeBody, gdBody); + + // islands + foreach (var island in QSBWorldSync.GetUnityObjects()) + { + SpawnOccasional(island._islandBody, gdBody); + } + } + + private static void SpawnOccasional(OWRigidbody body, OWRigidbody refBody) + { + var transformSync = Instantiate(QSBNetworkManager.Instance.OccasionalPrefab).GetRequiredComponent(); + transformSync.InitBodyIndexes(body, refBody); + transformSync.gameObject.SpawnWithServerAuthority(); + } + } +} diff --git a/QSB/TornadoSync/TransformSync/OccasionalTransformSync.cs b/QSB/TornadoSync/TransformSync/OccasionalTransformSync.cs new file mode 100644 index 00000000..bf844bfc --- /dev/null +++ b/QSB/TornadoSync/TransformSync/OccasionalTransformSync.cs @@ -0,0 +1,185 @@ +using System.Collections.Generic; +using System.Linq; +using QSB.Player.TransformSync; +using QSB.ShipSync.TransformSync; +using QSB.Syncs; +using QSB.Syncs.Unsectored.Rigidbodies; +using QSB.Tools.ProbeTool.TransformSync; +using QSB.WorldSync; +using QuantumUNET.Transport; +using UnityEngine; + +namespace QSB.TornadoSync.TransformSync +{ + public class OccasionalTransformSync : UnsectoredRigidbodySync + { + public override bool IsReady => WorldObjectManager.AllObjectsReady; + public override bool UseInterpolation => false; + public override bool IsPlayerObject => false; + + protected override OWRigidbody GetRigidbody() => CenterOfTheUniverse.s_rigidbodies[_bodyIndex]; + + private int _bodyIndex = -1; + private int _refBodyIndex = -1; + private Sector[] _sectors; + private OWRigidbody[] _orbs; + + public void InitBodyIndexes(OWRigidbody body, OWRigidbody refBody) + { + _bodyIndex = CenterOfTheUniverse.s_rigidbodies.IndexOf(body); + _refBodyIndex = CenterOfTheUniverse.s_rigidbodies.IndexOf(refBody); + } + + public override float GetNetworkSendInterval() => 20; + + protected override void Init() + { + base.Init(); + SetReferenceTransform(CenterOfTheUniverse.s_rigidbodies[_refBodyIndex].transform); + + _sectors = SectorManager.s_sectors + .Where(x => x._attachedOWRigidbody == AttachedObject).ToArray(); + _orbs = QSBWorldSync.OldOrbList + .Where(x => _sectors.Contains(x._sector)) + .Select(x => x._orbBody).ToArray(); + } + + public override void SerializeTransform(QNetworkWriter writer, bool initialState) + { + base.SerializeTransform(writer, initialState); + + if (initialState) + { + writer.Write(_bodyIndex); + writer.Write(_refBodyIndex); + } + } + + private bool _shouldUpdate; + + public override void DeserializeTransform(QNetworkReader reader, bool initialState) + { + base.DeserializeTransform(reader, initialState); + + if (initialState) + { + _bodyIndex = reader.ReadInt32(); + _refBodyIndex = reader.ReadInt32(); + } + + if (!WorldObjectManager.AllObjectsReady || HasAuthority) + { + return; + } + + _shouldUpdate = true; + } + + protected override bool UpdateTransform() + { + if (HasAuthority) + { + SetValuesToSync(); + return true; + } + + if (!_shouldUpdate) + { + return false; + } + + _shouldUpdate = false; + + var hasMoved = CustomHasMoved( + transform.position, + _localPrevPosition, + transform.rotation, + _localPrevRotation, + _relativeVelocity, + _localPrevVelocity, + _relativeAngularVelocity, + _localPrevAngularVelocity); + + _localPrevPosition = transform.position; + _localPrevRotation = transform.rotation; + _localPrevVelocity = _relativeVelocity; + _localPrevAngularVelocity = _relativeAngularVelocity; + + if (!hasMoved) + { + return true; + } + + if (_sectors.Contains(PlayerTransformSync.LocalInstance?.ReferenceSector?.AttachedObject)) + { + QueueMove(Locator._playerBody); + } + if (_sectors.Contains(ShipTransformSync.LocalInstance?.ReferenceSector?.AttachedObject)) + { + QueueMove(Locator._shipBody); + } + if (_sectors.Contains(PlayerProbeSync.LocalInstance?.ReferenceSector?.AttachedObject)) + { + QueueMove(Locator._probe._owRigidbody); + } + foreach (var orb in _orbs) + { + QueueMove(orb); + } + + var pos = ReferenceTransform.DecodePos(transform.position); + ((OWRigidbody)AttachedObject).SetPosition(pos); + ((OWRigidbody)AttachedObject).SetRotation(ReferenceTransform.DecodeRot(transform.rotation)); + ((OWRigidbody)AttachedObject).SetVelocity(ReferenceTransform.GetAttachedOWRigidbody().DecodeVel(_relativeVelocity, pos)); + ((OWRigidbody)AttachedObject).SetAngularVelocity(ReferenceTransform.GetAttachedOWRigidbody().DecodeAngVel(_relativeAngularVelocity)); + + Move(); + + return true; + } + + + private readonly List _toMove = new(); + + private struct MoveData + { + public OWRigidbody Child; + public Vector3 RelPos; + public Quaternion RelRot; + public Vector3 RelVel; + public Vector3 RelAngVel; + } + + private void QueueMove(OWRigidbody child) + { + if (child.transform.parent != null) + { + // it's parented to AttachedObject or one of its children + return; + } + + var pos = child.GetPosition(); + _toMove.Add(new MoveData + { + Child = child, + RelPos = ((OWRigidbody)AttachedObject).transform.EncodePos(pos), + RelRot = ((OWRigidbody)AttachedObject).transform.EncodeRot(child.GetRotation()), + RelVel = ((OWRigidbody)AttachedObject).EncodeVel(child.GetVelocity(), pos), + RelAngVel = ((OWRigidbody)AttachedObject).EncodeAngVel(child.GetAngularVelocity()) + }); + } + + private void Move() + { + foreach (var data in _toMove) + { + var pos = ((OWRigidbody)AttachedObject).transform.DecodePos(data.RelPos); + data.Child.SetPosition(pos); + data.Child.SetRotation(((OWRigidbody)AttachedObject).transform.DecodeRot(data.RelRot)); + data.Child.SetVelocity(((OWRigidbody)AttachedObject).DecodeVel(data.RelVel, pos)); + data.Child.SetAngularVelocity(((OWRigidbody)AttachedObject).DecodeAngVel(data.RelAngVel)); + } + _toMove.Clear(); + } + } +} diff --git a/QSB/TornadoSync/WorldObjects/QSBTornado.cs b/QSB/TornadoSync/WorldObjects/QSBTornado.cs new file mode 100644 index 00000000..4295be74 --- /dev/null +++ b/QSB/TornadoSync/WorldObjects/QSBTornado.cs @@ -0,0 +1,37 @@ +using QSB.WorldSync; + +namespace QSB.TornadoSync.WorldObjects +{ + public class QSBTornado : WorldObject + { + public override void Init(TornadoController attachedObject, int id) + { + ObjectId = id; + AttachedObject = attachedObject; + } + + public bool FormState + { + get => AttachedObject._tornadoRoot.activeSelf // forming or formed or collapsing + && !AttachedObject._tornadoCollapsing; // and not collapsing + set + { + if (FormState == value) + { + return; + } + + if (value) + { + AttachedObject._tornadoCollapsing = false; + AttachedObject.StartFormation(); + } + else + { + AttachedObject._secondsUntilFormation = 0; + AttachedObject.StartCollapse(); + } + } + } + } +} diff --git a/QSB/Utility/DebugGUI.cs b/QSB/Utility/DebugGUI.cs index e124ecec..16cb8656 100644 --- a/QSB/Utility/DebugGUI.cs +++ b/QSB/Utility/DebugGUI.cs @@ -1,9 +1,7 @@ using QSB.ClientServerStateSync; using QSB.OrbSync.TransformSync; -using QSB.OrbSync.WorldObjects; using QSB.Player; using QSB.QuantumSync; -using QSB.QuantumSync.WorldObjects; using QSB.ShipSync; using QSB.ShipSync.TransformSync; using QSB.ShipSync.WorldObjects; @@ -11,7 +9,6 @@ using QSB.TimeSync; using QSB.WorldSync; using System.Linq; using UnityEngine; -using UnityEngine.Assertions.Must; namespace QSB.Utility { @@ -112,6 +109,12 @@ namespace QSB.Utility WriteLine(1, $"Timescale : {OWTime.GetTimeScale()}"); WriteLine(1, $"Time Remaining : {Mathf.Floor(TimeLoop.GetSecondsRemaining() / 60f)}:{Mathf.Round(TimeLoop.GetSecondsRemaining() % 60f * 100f / 100f)}"); WriteLine(1, $"Loop Count : {TimeLoop.GetLoopCount()}"); + WriteLine(1, $"TimeLoop Initialized : {TimeLoop._initialized}"); + if (TimeLoop._initialized) + { + WriteLine(1, $"TimeLoop IsTimeFlowing : {TimeLoop.IsTimeFlowing()}"); + WriteLine(1, $"TimeLoop IsTimeLoopEnabled : {TimeLoop.IsTimeLoopEnabled()}"); + } } #endregion diff --git a/QSB/WorldSync/QSBWorldSync.cs b/QSB/WorldSync/QSBWorldSync.cs index 5342543f..13348a41 100644 --- a/QSB/WorldSync/QSBWorldSync.cs +++ b/QSB/WorldSync/QSBWorldSync.cs @@ -1,10 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using OWML.Common; +using OWML.Common; using QSB.OrbSync.TransformSync; using QSB.OrbSync.WorldObjects; using QSB.Utility; +using System; +using System.Collections.Generic; +using System.Linq; using UnityEngine; namespace QSB.WorldSync diff --git a/QSB/WorldSync/WorldObjectManager.cs b/QSB/WorldSync/WorldObjectManager.cs index e5c081b7..76394646 100644 --- a/QSB/WorldSync/WorldObjectManager.cs +++ b/QSB/WorldSync/WorldObjectManager.cs @@ -1,8 +1,8 @@ -using System; -using System.Collections.Generic; -using OWML.Common; +using OWML.Common; using QSB.Player; using QSB.Utility; +using System; +using System.Collections.Generic; using UnityEngine; namespace QSB.WorldSync @@ -49,25 +49,19 @@ namespace QSB.WorldSync { if (!QSBNetworkManager.Instance.IsReady) { - DebugLog.ToConsole($"Warning - Tried to rebuild WorldObjects when Network Manager not ready!", OWML.Common.MessageType.Warning); + DebugLog.ToConsole($"Warning - Tried to rebuild WorldObjects when Network Manager not ready! Building when ready...", MessageType.Warning); QSBCore.UnityEvents.RunWhen(() => QSBNetworkManager.Instance.IsReady, () => Rebuild(scene)); return; } if (QSBPlayerManager.LocalPlayerId == uint.MaxValue) { - DebugLog.ToConsole($"Warning - Tried to rebuild WorldObjects when LocalPlayer is not ready!", OWML.Common.MessageType.Warning); + DebugLog.ToConsole($"Warning - Tried to rebuild WorldObjects when LocalPlayer is not ready! Building when ready...", MessageType.Warning); QSBCore.UnityEvents.RunWhen(() => QSBPlayerManager.LocalPlayerId != uint.MaxValue, () => Rebuild(scene)); return; } - if (QSBPlayerManager.LocalPlayer.IsReady) - { - DoRebuild(scene); - return; - } - - QSBCore.UnityEvents.RunWhen(() => QSBPlayerManager.LocalPlayer.IsReady, () => DoRebuild(scene)); + DoRebuild(scene); } private static void DoRebuild(OWScene scene) diff --git a/QSB/debugsettings.json b/QSB/debugsettings.json index c125ec65..c6d0afb9 100644 --- a/QSB/debugsettings.json +++ b/QSB/debugsettings.json @@ -1,6 +1,6 @@ { "debugMode": true, - "drawLines": true, + "drawLines": false, "showQuantumVisibilityObjects": false, "showQuantumDebugBoxes": false, "avoidTimeSync": false, diff --git a/QuantumUNET/QNetworkScene.cs b/QuantumUNET/QNetworkScene.cs index 102eed16..032bf292 100644 --- a/QuantumUNET/QNetworkScene.cs +++ b/QuantumUNET/QNetworkScene.cs @@ -1,6 +1,6 @@ using QuantumUNET.Components; -using System.Collections.Generic; using QuantumUNET.Messages; +using System.Collections.Generic; using UnityEngine; namespace QuantumUNET diff --git a/QuantumUNET/QNetworkServer.cs b/QuantumUNET/QNetworkServer.cs index eee1fc59..417de46a 100644 --- a/QuantumUNET/QNetworkServer.cs +++ b/QuantumUNET/QNetworkServer.cs @@ -1259,7 +1259,16 @@ namespace QuantumUNET if (handlers.ContainsKey(msgType) && m_LocalConnection != null) { var writer = new QNetworkWriter(); - msg.Serialize(writer); + try + { + msg.Serialize(writer); + } + catch (Exception ex) + { + QLog.Error($"Error serializing msgId:{msgType} - {ex}"); + return false; + } + var reader = new QNetworkReader(writer); m_LocalConnection.InvokeHandler(msgType, reader, channelId); result = true;