From 66ce7b692708c0466785e9dca421352210d3ed15 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Fri, 4 Jun 2021 11:31:09 +0100 Subject: [PATCH 001/118] added basic menus --- QSB/Menus/IMenuAPI.cs | 32 +++++++++++++++++ QSB/Menus/MenuManager.cs | 55 +++++++++++++++++++++++++++++ QSB/QSB.csproj | 2 ++ QSB/QSB.csproj.user | 2 +- QSB/QSBCore.cs | 7 +++- QSB/manifest.json | 5 +-- QuantumUNET/QuantumUNET.csproj | 2 +- QuantumUNET/QuantumUNET.csproj.user | 2 +- 8 files changed, 101 insertions(+), 6 deletions(-) create mode 100644 QSB/Menus/IMenuAPI.cs create mode 100644 QSB/Menus/MenuManager.cs diff --git a/QSB/Menus/IMenuAPI.cs b/QSB/Menus/IMenuAPI.cs new file mode 100644 index 00000000..cbcdbd3e --- /dev/null +++ b/QSB/Menus/IMenuAPI.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; +using UnityEngine.UI; + +namespace QSB.Menus +{ + public interface IMenuAPI + { + // Title screen + GameObject TitleScreen_MakeMenuOpenButton(string name, Menu menuToOpen); + GameObject TitleScreen_MakeSceneLoadButton(string name, SubmitActionLoadScene.LoadableScenes sceneToLoad, PopupMenu confirmPopup = null); + Button TitleScreen_MakeSimpleButton(string name); + // 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); + Button PauseMenu_MakeSimpleButton(string name, Menu customMenu = null); + Menu PauseMenu_MakePauseListMenu(string title); + // Options + Menu OptionsMenu_MakeNonScrollingOptionsTab(string name); + GameObject OptionsMenu_MakeTwoButtonToggle(string label, string trueText, string falseText, string tooltipText, bool savedValue, Menu menuTab); + GameObject OptionsMenu_MakeNonDisplaySliderElement(string label, string tooltipText, float savedValue, Menu menuTab); + void OptionsMenu_MakeSpacer(float minHeight, Menu menuTab); + void OptionsMenu_MakeLabel(string label, Menu menuTab); + void OptionsMenu_MakeTextInput(string label, string placeholderText, string savedValue, Menu menuTab); + // Misc + PopupMenu MakeTwoChoicePopup(string message, string confirmText, string cancelText); + PopupInputMenu MakeInputFieldPopup(string message, string placeholderMessage, string confirmText, string cancelText); + } +} diff --git a/QSB/Menus/MenuManager.cs b/QSB/Menus/MenuManager.cs new file mode 100644 index 00000000..17978e70 --- /dev/null +++ b/QSB/Menus/MenuManager.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; + +namespace QSB.Menus +{ + class MenuManager : MonoBehaviour + { + private IMenuAPI MenuApi => QSBCore.MenuApi; + private PopupMenu HostWaitingPopup; + private PopupMenu ClientWaitingPopup; + + public void Start() + { + MakeTitleMenus(); + } + + private void MakeTitleMenus() + { + HostWaitingPopup = MenuApi.MakeTwoChoicePopup("Waiting for players to join...", "Start Multiplayer Game", "Stop Server"); + HostWaitingPopup.OnPopupCancel += StopServerOrLeaveServer; + ClientWaitingPopup = MenuApi.MakeTwoChoicePopup("Waiting for game to start...", "uhhhhh", "Disconnect"); + ClientWaitingPopup.OnPopupCancel += StopServerOrLeaveServer; + + var hostButton = MenuApi.TitleScreen_MakeSimpleButton("HOST SERVER"); + hostButton.onClick.AddListener(HostServer); + var connectButton = MenuApi.TitleScreen_MakeSimpleButton("CONNECT TO SERVER"); + connectButton.onClick.AddListener(ConnectToServer); + + var menu = MenuApi.OptionsMenu_MakeNonScrollingOptionsTab("MULTIPLAYER"); + //MenuApi.OptionsMenu_MakeLabel("Connection Information", menu); + MenuApi.OptionsMenu_MakeTextInput("IP Address", "IP Address", QSBCore.DefaultServerIP, menu); + MenuApi.OptionsMenu_MakeTextInput("Port", "Port", $"{QSBCore.Port}", menu); + } + + private void HostServer() + { + HostWaitingPopup.EnableMenu(true); + QSBNetworkManager.Instance.StartHost(); + } + + private void StopServerOrLeaveServer() + { + QSBNetworkManager.Instance.StopHost(); + } + + private void ConnectToServer() + { + ClientWaitingPopup.EnableMenu(true); + QSBNetworkManager.Instance.StartClient(); + } + } +} \ No newline at end of file diff --git a/QSB/QSB.csproj b/QSB/QSB.csproj index 96db5440..e3c85420 100644 --- a/QSB/QSB.csproj +++ b/QSB/QSB.csproj @@ -159,6 +159,8 @@ + + diff --git a/QSB/QSB.csproj.user b/QSB/QSB.csproj.user index 9f1e3257..7bf6c3df 100644 --- a/QSB/QSB.csproj.user +++ b/QSB/QSB.csproj.user @@ -1,7 +1,7 @@  - E:\Epic\Epic Games\OuterWilds + D:\EpicGames\OuterWilds C:\Users\Henry\AppData\Roaming\OuterWildsModManager\OWML ShowAllFiles diff --git a/QSB/QSBCore.cs b/QSB/QSBCore.cs index e8f6bd67..d1c53753 100644 --- a/QSB/QSBCore.cs +++ b/QSB/QSBCore.cs @@ -8,6 +8,7 @@ using QSB.ConversationSync; using QSB.ElevatorSync; using QSB.GeyserSync; using QSB.ItemSync; +using QSB.Menus; using QSB.OrbSync; using QSB.Patches; using QSB.Player; @@ -69,6 +70,7 @@ namespace QSB public static bool IsInMultiplayer => QNetworkManager.singleton.isNetworkActive; public static string QSBVersion => Helper.Manifest.Version; public static GameObject GameObjectInstance => _thisInstance.gameObject; + public static IMenuAPI MenuApi { get; private set; } private static QSBCore _thisInstance; private const float _debugLineSpacing = 11f; @@ -89,6 +91,8 @@ namespace QSB Helper = ModHelper; DebugLog.ToConsole($"* Start of QSB version {Helper.Manifest.Version} - authored by {Helper.Manifest.Author}", MessageType.Info); + MenuApi = ModHelper.Interaction.GetModApi("_nebula.MenuFramework"); + NetworkAssetBundle = Helper.Assets.LoadBundle("assets/network"); InstrumentAssetBundle = Helper.Assets.LoadBundle("assets/instruments"); ConversationAssetBundle = Helper.Assets.LoadBundle("assets/conversation"); @@ -96,7 +100,7 @@ namespace QSB QSBPatchManager.Init(); gameObject.AddComponent(); - gameObject.AddComponent(); + //gameObject.AddComponent(); gameObject.AddComponent(); gameObject.AddComponent(); gameObject.AddComponent(); @@ -104,6 +108,7 @@ namespace QSB gameObject.AddComponent(); gameObject.AddComponent(); gameObject.AddComponent(); + gameObject.AddComponent(); // WorldObject managers gameObject.AddComponent(); diff --git a/QSB/manifest.json b/QSB/manifest.json index c84b03ae..9f226f2b 100644 --- a/QSB/manifest.json +++ b/QSB/manifest.json @@ -5,9 +5,10 @@ "description": "Adds online multiplayer to the game.", "warning": { "title": "Follow these steps before playing multiplayer :", - "body": "- Disable *all* other mods. (Can heavily affect performance)\n- Make sure you are not running any other network-intensive applications.\n- Make sure you have forwarded/opened the correct ports. (See the GitHub readme.)" + "body": "- Disable *all* other mods. (Can heavily affect performance)\n- Make sure you are not running any other network-intensive applications.\n- Make sure you have forwarded/opened the correct ports. (See the GitHub readme.)" }, "uniqueName": "Raicuparta.QuantumSpaceBuddies", "version": "0.11.0", - "owmlVersion": "1.1.8" + "owmlVersion": "1.1.8", + "dependencies": [ "_nebula.MenuFramework" ] } \ No newline at end of file diff --git a/QuantumUNET/QuantumUNET.csproj b/QuantumUNET/QuantumUNET.csproj index 4b0d8041..9f52e6c5 100644 --- a/QuantumUNET/QuantumUNET.csproj +++ b/QuantumUNET/QuantumUNET.csproj @@ -57,7 +57,7 @@ False - E:\Epic\Epic Games\OuterWilds\OuterWilds_Data\Managed\UnityEngine.PhysicsModule.dll + $(GameDir)\OuterWilds_Data\Managed\UnityEngine.PhysicsModule.dll False diff --git a/QuantumUNET/QuantumUNET.csproj.user b/QuantumUNET/QuantumUNET.csproj.user index ec6d119b..c8a11e84 100644 --- a/QuantumUNET/QuantumUNET.csproj.user +++ b/QuantumUNET/QuantumUNET.csproj.user @@ -1,7 +1,7 @@  - E:\Epic\Epic Games\OuterWilds + D:\EpicGames\OuterWilds C:\Users\Henry\AppData\Roaming\OuterWildsModManager\OWML ShowAllFiles From 87b490e022536b04eacb451f3ad2dc5092f017ff Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Mon, 9 Aug 2021 22:42:29 +0100 Subject: [PATCH 002/118] dont serialize if world objects aren't ready --- QSB/Syncs/RigidbodySync/SectoredRigidbodySync.cs | 5 +++++ QSB/Syncs/RigidbodySync/UnparentedBaseRigidbodySync.cs | 5 +++++ QSB/Syncs/TransformSync/BaseTransformSync.cs | 5 +++++ QSB/Syncs/TransformSync/SectoredTransformSync.cs | 5 +++++ QSB/Syncs/TransformSync/UnparentedBaseTransformSync.cs | 5 +++++ 5 files changed, 25 insertions(+) diff --git a/QSB/Syncs/RigidbodySync/SectoredRigidbodySync.cs b/QSB/Syncs/RigidbodySync/SectoredRigidbodySync.cs index fe7a7b59..ccde5963 100644 --- a/QSB/Syncs/RigidbodySync/SectoredRigidbodySync.cs +++ b/QSB/Syncs/RigidbodySync/SectoredRigidbodySync.cs @@ -58,6 +58,11 @@ namespace QSB.Syncs.RigidbodySync _intermediaryTransform = new IntermediaryTransform(transform); } + if (!QSBCore.WorldObjectsReady) + { + return; + } + if (ReferenceSector != null) { writer.Write(ReferenceSector.ObjectId); diff --git a/QSB/Syncs/RigidbodySync/UnparentedBaseRigidbodySync.cs b/QSB/Syncs/RigidbodySync/UnparentedBaseRigidbodySync.cs index a1dc5d6c..bf700f4c 100644 --- a/QSB/Syncs/RigidbodySync/UnparentedBaseRigidbodySync.cs +++ b/QSB/Syncs/RigidbodySync/UnparentedBaseRigidbodySync.cs @@ -51,6 +51,11 @@ namespace QSB.Syncs.RigidbodySync _intermediaryTransform = new IntermediaryTransform(transform); } + if (!QSBCore.WorldObjectsReady) + { + return; + } + /* We need to send : * - Position * - Rotation diff --git a/QSB/Syncs/TransformSync/BaseTransformSync.cs b/QSB/Syncs/TransformSync/BaseTransformSync.cs index b0297ac5..bdf1ec60 100644 --- a/QSB/Syncs/TransformSync/BaseTransformSync.cs +++ b/QSB/Syncs/TransformSync/BaseTransformSync.cs @@ -141,6 +141,11 @@ namespace QSB.Syncs.TransformSync _intermediaryTransform = new IntermediaryTransform(transform); } + if (!QSBCore.WorldObjectsReady) + { + return; + } + var worldPos = _intermediaryTransform.GetPosition(); var worldRot = _intermediaryTransform.GetRotation(); writer.Write(worldPos); diff --git a/QSB/Syncs/TransformSync/SectoredTransformSync.cs b/QSB/Syncs/TransformSync/SectoredTransformSync.cs index 1dd4218d..18e9b409 100644 --- a/QSB/Syncs/TransformSync/SectoredTransformSync.cs +++ b/QSB/Syncs/TransformSync/SectoredTransformSync.cs @@ -102,6 +102,11 @@ namespace QSB.Syncs.TransformSync _intermediaryTransform = new IntermediaryTransform(transform); } + if (!QSBCore.WorldObjectsReady) + { + return; + } + if (!QSBPlayerManager.PlayerExists(PlayerId)) { DebugLog.ToConsole($"Warning - Tried to serialize {_logName} before the right player exists.", OWML.Common.MessageType.Warning); diff --git a/QSB/Syncs/TransformSync/UnparentedBaseTransformSync.cs b/QSB/Syncs/TransformSync/UnparentedBaseTransformSync.cs index dea2b32c..2306746d 100644 --- a/QSB/Syncs/TransformSync/UnparentedBaseTransformSync.cs +++ b/QSB/Syncs/TransformSync/UnparentedBaseTransformSync.cs @@ -50,6 +50,11 @@ namespace QSB.Syncs.TransformSync _intermediaryTransform = new IntermediaryTransform(transform); } + if (!QSBCore.WorldObjectsReady) + { + return; + } + var worldPos = _intermediaryTransform.GetPosition(); var worldRot = _intermediaryTransform.GetRotation(); writer.Write(worldPos); From 28301b53f4d3d479faef34504cfefe6e4d84b572 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Tue, 10 Aug 2021 14:54:23 +0100 Subject: [PATCH 003/118] add stuff for pc move --- QSB/Player/Events/PlayerJoinEvent.cs | 24 +++++++++++++++---- QSB/Player/Events/PlayerJoinMessage.cs | 3 +++ QSB/Player/KickReason.cs | 3 ++- QSB/QSBCore.cs | 1 + QSB/SectorSync/SectorSync.cs | 4 +++- .../RigidbodySync/SectoredRigidbodySync.cs | 5 ---- .../UnparentedBaseRigidbodySync.cs | 5 ---- QSB/Syncs/TransformSync/BaseTransformSync.cs | 11 +++++---- .../TransformSync/SectoredTransformSync.cs | 7 ------ .../UnparentedBaseTransformSync.cs | 5 ---- QSB/TimeSync/WakeUpSync.cs | 5 ++++ 11 files changed, 39 insertions(+), 34 deletions(-) diff --git a/QSB/Player/Events/PlayerJoinEvent.cs b/QSB/Player/Events/PlayerJoinEvent.cs index 443c30b4..87de9bf2 100644 --- a/QSB/Player/Events/PlayerJoinEvent.cs +++ b/QSB/Player/Events/PlayerJoinEvent.cs @@ -17,22 +17,36 @@ namespace QSB.Player.Events { AboutId = LocalPlayerId, PlayerName = name, - QSBVersion = QSBCore.QSBVersion + QSBVersion = QSBCore.QSBVersion, + GameVersion = QSBCore.GameVersion }; public override void OnReceiveRemote(bool server, PlayerJoinMessage message) { - if (server && (message.QSBVersion != QSBCore.QSBVersion)) + if (message.QSBVersion != QSBCore.QSBVersion) { - DebugLog.ToConsole($"Error - Client {message.PlayerName} connecting with wrong version. (Client:{message.QSBVersion}, Server:{QSBCore.QSBVersion})", MessageType.Error); - QSBEventManager.FireEvent(EventNames.QSBPlayerKick, message.AboutId, KickReason.VersionNotMatching); + if (server) + { + DebugLog.ToConsole($"Error - Client {message.PlayerName} connecting with wrong QSB version. (Client:{message.QSBVersion}, Server:{QSBCore.QSBVersion})", MessageType.Error); + QSBEventManager.FireEvent(EventNames.QSBPlayerKick, message.AboutId, KickReason.QSBVersionNotMatching); + } + return; + } + + if (message.GameVersion != QSBCore.GameVersion) + { + if (server) + { + DebugLog.ToConsole($"Error - Client {message.PlayerName} connecting with wrong game version. (Client:{message.GameVersion}, Server:{QSBCore.GameVersion})", MessageType.Error); + QSBEventManager.FireEvent(EventNames.QSBPlayerKick, message.AboutId, KickReason.GameVersionNotMatching); + } return; } var player = QSBPlayerManager.GetPlayer(message.AboutId); player.Name = message.PlayerName; DebugLog.ToAll($"{player.Name} joined!", MessageType.Info); - DebugLog.DebugWrite($"{player.Name} joined as id {player.PlayerId}", MessageType.Info); + DebugLog.DebugWrite($"{player.Name} joined. id:{player.PlayerId}, qsbVersion:{message.QSBVersion}, gameVersion:{message.GameVersion}", 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 5c854aec..1dab8bda 100644 --- a/QSB/Player/Events/PlayerJoinMessage.cs +++ b/QSB/Player/Events/PlayerJoinMessage.cs @@ -7,12 +7,14 @@ namespace QSB.Player.Events { public string PlayerName { get; set; } public string QSBVersion { get; set; } + public string GameVersion { get; set; } public override void Deserialize(QNetworkReader reader) { base.Deserialize(reader); PlayerName = reader.ReadString(); QSBVersion = reader.ReadString(); + GameVersion = reader.ReadString(); } public override void Serialize(QNetworkWriter writer) @@ -20,6 +22,7 @@ namespace QSB.Player.Events base.Serialize(writer); writer.Write(PlayerName); writer.Write(QSBVersion); + writer.Write(GameVersion); } } } \ No newline at end of file diff --git a/QSB/Player/KickReason.cs b/QSB/Player/KickReason.cs index 9973eb5a..57c75148 100644 --- a/QSB/Player/KickReason.cs +++ b/QSB/Player/KickReason.cs @@ -2,6 +2,7 @@ { public enum KickReason { - VersionNotMatching + QSBVersionNotMatching, + GameVersionNotMatching } } diff --git a/QSB/QSBCore.cs b/QSB/QSBCore.cs index d89a2b23..e5c0d8b5 100644 --- a/QSB/QSBCore.cs +++ b/QSB/QSBCore.cs @@ -63,6 +63,7 @@ namespace QSB public static bool IsHost => QNetworkServer.active; public static bool IsInMultiplayer => QNetworkManager.singleton.isNetworkActive; public static string QSBVersion => Helper.Manifest.Version; + public static string GameVersion => Application.version; public void Awake() { diff --git a/QSB/SectorSync/SectorSync.cs b/QSB/SectorSync/SectorSync.cs index 487be4e7..4765bf8f 100644 --- a/QSB/SectorSync/SectorSync.cs +++ b/QSB/SectorSync/SectorSync.cs @@ -4,6 +4,7 @@ using QSB.SectorSync.WorldObjects; using QSB.Syncs; using QSB.Utility; using QSB.WorldSync; +using System; using System.Collections.Generic; using System.Linq; using UnityEngine; @@ -31,6 +32,7 @@ namespace QSB.SectorSync public void Init(SectorDetector detector, ISectoredSync sectoredSync) { + DebugLog.DebugWrite($"INIT SECTOR SYNC detector:{detector.name}"); if (_sectorDetector != null) { _sectorDetector.OnEnterSector -= AddSector; @@ -119,7 +121,7 @@ namespace QSB.SectorSync if (!_isReady) { - DebugLog.ToConsole($"Warning - Tried to use GetClosestSector before it was initialized. Transform:{trans.name}", MessageType.Warning); + DebugLog.ToConsole($"Warning - Tried to use GetClosestSector before it was initialized. Transform:{trans.name} Stacktrace:{Environment.StackTrace}", MessageType.Warning); return null; } diff --git a/QSB/Syncs/RigidbodySync/SectoredRigidbodySync.cs b/QSB/Syncs/RigidbodySync/SectoredRigidbodySync.cs index ccde5963..fe7a7b59 100644 --- a/QSB/Syncs/RigidbodySync/SectoredRigidbodySync.cs +++ b/QSB/Syncs/RigidbodySync/SectoredRigidbodySync.cs @@ -58,11 +58,6 @@ namespace QSB.Syncs.RigidbodySync _intermediaryTransform = new IntermediaryTransform(transform); } - if (!QSBCore.WorldObjectsReady) - { - return; - } - if (ReferenceSector != null) { writer.Write(ReferenceSector.ObjectId); diff --git a/QSB/Syncs/RigidbodySync/UnparentedBaseRigidbodySync.cs b/QSB/Syncs/RigidbodySync/UnparentedBaseRigidbodySync.cs index bf700f4c..a1dc5d6c 100644 --- a/QSB/Syncs/RigidbodySync/UnparentedBaseRigidbodySync.cs +++ b/QSB/Syncs/RigidbodySync/UnparentedBaseRigidbodySync.cs @@ -51,11 +51,6 @@ namespace QSB.Syncs.RigidbodySync _intermediaryTransform = new IntermediaryTransform(transform); } - if (!QSBCore.WorldObjectsReady) - { - return; - } - /* We need to send : * - Position * - Rotation diff --git a/QSB/Syncs/TransformSync/BaseTransformSync.cs b/QSB/Syncs/TransformSync/BaseTransformSync.cs index bdf1ec60..100d3e0b 100644 --- a/QSB/Syncs/TransformSync/BaseTransformSync.cs +++ b/QSB/Syncs/TransformSync/BaseTransformSync.cs @@ -50,6 +50,12 @@ namespace QSB.Syncs.TransformSync _intermediaryTransform = new IntermediaryTransform(transform); QSBSceneManager.OnSceneLoaded += OnSceneLoaded; + if (Player == null) + { + DebugLog.ToConsole($"Error - Player in start of {_logName} was null!", MessageType.Error); + return; + } + if (!_storedTransformSyncs.ContainsKey(Player)) { _storedTransformSyncs.Add(Player, new Dictionary()); @@ -141,11 +147,6 @@ namespace QSB.Syncs.TransformSync _intermediaryTransform = new IntermediaryTransform(transform); } - if (!QSBCore.WorldObjectsReady) - { - return; - } - var worldPos = _intermediaryTransform.GetPosition(); var worldRot = _intermediaryTransform.GetRotation(); writer.Write(worldPos); diff --git a/QSB/Syncs/TransformSync/SectoredTransformSync.cs b/QSB/Syncs/TransformSync/SectoredTransformSync.cs index 18e9b409..b489c8bf 100644 --- a/QSB/Syncs/TransformSync/SectoredTransformSync.cs +++ b/QSB/Syncs/TransformSync/SectoredTransformSync.cs @@ -102,19 +102,12 @@ namespace QSB.Syncs.TransformSync _intermediaryTransform = new IntermediaryTransform(transform); } - if (!QSBCore.WorldObjectsReady) - { - return; - } - if (!QSBPlayerManager.PlayerExists(PlayerId)) { - DebugLog.ToConsole($"Warning - Tried to serialize {_logName} before the right player exists.", OWML.Common.MessageType.Warning); writer.Write(-1); } else if (!Player.PlayerStates.IsReady) { - DebugLog.ToConsole($"Warning - Tried to serialize {_logName} before the player was ready.", OWML.Common.MessageType.Warning); writer.Write(-1); } diff --git a/QSB/Syncs/TransformSync/UnparentedBaseTransformSync.cs b/QSB/Syncs/TransformSync/UnparentedBaseTransformSync.cs index 2306746d..dea2b32c 100644 --- a/QSB/Syncs/TransformSync/UnparentedBaseTransformSync.cs +++ b/QSB/Syncs/TransformSync/UnparentedBaseTransformSync.cs @@ -50,11 +50,6 @@ namespace QSB.Syncs.TransformSync _intermediaryTransform = new IntermediaryTransform(transform); } - if (!QSBCore.WorldObjectsReady) - { - return; - } - var worldPos = _intermediaryTransform.GetPosition(); var worldRot = _intermediaryTransform.GetRotation(); writer.Write(worldPos); diff --git a/QSB/TimeSync/WakeUpSync.cs b/QSB/TimeSync/WakeUpSync.cs index 442d9e67..78f34a8d 100644 --- a/QSB/TimeSync/WakeUpSync.cs +++ b/QSB/TimeSync/WakeUpSync.cs @@ -366,6 +366,11 @@ namespace QSB.TimeSync ResetTimeScale(); } } + + if (CurrentState == State.Loaded) + { + CheckTimeDifference(); + } } private void CheckTimeDifference() From e5c984459fb978790b5e1ef6192c681448f8eb42 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Tue, 10 Aug 2021 17:50:51 +0100 Subject: [PATCH 004/118] add assembly-csharp reference to qsbtests --- QSBTests/QSBTests.csproj | 3 +++ QSBTests/QSBTests.csproj.user | 8 ++++++++ 2 files changed, 11 insertions(+) create mode 100644 QSBTests/QSBTests.csproj.user diff --git a/QSBTests/QSBTests.csproj b/QSBTests/QSBTests.csproj index 3bee2d59..fa834982 100644 --- a/QSBTests/QSBTests.csproj +++ b/QSBTests/QSBTests.csproj @@ -39,6 +39,9 @@ 4 + + $(GameDir)\OuterWilds_Data\Managed\Assembly-CSharp.dll + ..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll diff --git a/QSBTests/QSBTests.csproj.user b/QSBTests/QSBTests.csproj.user new file mode 100644 index 00000000..c8a11e84 --- /dev/null +++ b/QSBTests/QSBTests.csproj.user @@ -0,0 +1,8 @@ + + + + D:\EpicGames\OuterWilds + C:\Users\Henry\AppData\Roaming\OuterWildsModManager\OWML + ShowAllFiles + + \ No newline at end of file From bc9444a44e7b825e13e960051d74d2550d62f363 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 12 Aug 2021 14:18:35 +0100 Subject: [PATCH 005/118] change some warnings to infos --- QSB/ItemSync/ItemManager.cs | 2 +- QSB/QuantumSync/QuantumManager.cs | 2 +- QSB/SectorSync/QSBSectorManager.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/QSB/ItemSync/ItemManager.cs b/QSB/ItemSync/ItemManager.cs index bb7c3c5b..97c304e2 100644 --- a/QSB/ItemSync/ItemManager.cs +++ b/QSB/ItemSync/ItemManager.cs @@ -9,7 +9,7 @@ namespace QSB.ItemSync { protected override void RebuildWorldObjects(OWScene scene) { - DebugLog.DebugWrite("Rebuilding OWItems...", MessageType.Warning); + DebugLog.DebugWrite("Rebuilding OWItems...", MessageType.Info); QSBWorldSync.Init(); QSBWorldSync.Init(); QSBWorldSync.Init(); diff --git a/QSB/QuantumSync/QuantumManager.cs b/QSB/QuantumSync/QuantumManager.cs index 7581cc58..422acc48 100644 --- a/QSB/QuantumSync/QuantumManager.cs +++ b/QSB/QuantumSync/QuantumManager.cs @@ -31,7 +31,7 @@ namespace QSB.QuantumSync protected override void RebuildWorldObjects(OWScene scene) { - DebugLog.DebugWrite("Rebuilding quantum objects...", MessageType.Warning); + DebugLog.DebugWrite("Rebuilding quantum objects...", MessageType.Info); QSBWorldSync.Init(); QSBWorldSync.Init(); QSBWorldSync.Init(); diff --git a/QSB/SectorSync/QSBSectorManager.cs b/QSB/SectorSync/QSBSectorManager.cs index c7fbb47c..fc94c8a5 100644 --- a/QSB/SectorSync/QSBSectorManager.cs +++ b/QSB/SectorSync/QSBSectorManager.cs @@ -68,7 +68,7 @@ namespace QSB.SectorSync protected override void RebuildWorldObjects(OWScene scene) { - DebugLog.DebugWrite("Rebuilding sectors...", MessageType.Warning); + DebugLog.DebugWrite("Rebuilding sectors...", MessageType.Info); if (QSBSceneManager.CurrentScene == OWScene.SolarSystem) { var timeLoopRing = GameObject.Find("TimeLoopRing_Body"); From a3ebb18919742428000ad73992ed1600f79dcb17 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 12 Aug 2021 14:19:13 +0100 Subject: [PATCH 006/118] rename UnparentedBaseRigidbodySync to BaseRigidbodySync --- .../{UnparentedBaseRigidbodySync.cs => BaseRigidbodySync.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename QSB/Syncs/RigidbodySync/{UnparentedBaseRigidbodySync.cs => BaseRigidbodySync.cs} (99%) diff --git a/QSB/Syncs/RigidbodySync/UnparentedBaseRigidbodySync.cs b/QSB/Syncs/RigidbodySync/BaseRigidbodySync.cs similarity index 99% rename from QSB/Syncs/RigidbodySync/UnparentedBaseRigidbodySync.cs rename to QSB/Syncs/RigidbodySync/BaseRigidbodySync.cs index a1dc5d6c..4483341b 100644 --- a/QSB/Syncs/RigidbodySync/UnparentedBaseRigidbodySync.cs +++ b/QSB/Syncs/RigidbodySync/BaseRigidbodySync.cs @@ -8,7 +8,7 @@ using UnityEngine; namespace QSB.Syncs.RigidbodySync { - public abstract class UnparentedBaseRigidbodySync : SyncBase + public abstract class BaseRigidbodySync : SyncBase { protected Vector3 _relativeVelocity; protected Vector3 _relativeAngularVelocity; From f2e833abaff650995d51b6327683fdf8b079a670 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 12 Aug 2021 14:19:17 +0100 Subject: [PATCH 007/118] Update QSB.csproj --- QSB/QSB.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QSB/QSB.csproj b/QSB/QSB.csproj index 16b2c7e0..82b416e4 100644 --- a/QSB/QSB.csproj +++ b/QSB/QSB.csproj @@ -279,7 +279,7 @@ - + From 3cc3aa3a2270e27a46e78392aeb78b525a073c33 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 12 Aug 2021 14:19:34 +0100 Subject: [PATCH 008/118] remove debug write --- QSB/QSBNetworkManager.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/QSB/QSBNetworkManager.cs b/QSB/QSBNetworkManager.cs index a80bf9d5..06665466 100644 --- a/QSB/QSBNetworkManager.cs +++ b/QSB/QSBNetworkManager.cs @@ -138,7 +138,6 @@ namespace QSB public override void OnStartClient(QNetworkClient _) { - DebugLog.DebugWrite($"Setting defaultServerIP to {networkAddress}"); var config = QSBCore.Helper.Config; config.SetSettingsValue("defaultServerIP", networkAddress); QSBCore.Helper.Storage.Save(config, Constants.ModConfigFileName); From 2dcf0d8c01b96cd4c6daca91851f7f092d9d6195 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 12 Aug 2021 14:19:48 +0100 Subject: [PATCH 009/118] fix inheritance name (still using unparented) --- QSB/Syncs/RigidbodySync/SectoredRigidbodySync.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QSB/Syncs/RigidbodySync/SectoredRigidbodySync.cs b/QSB/Syncs/RigidbodySync/SectoredRigidbodySync.cs index fe7a7b59..666e5f88 100644 --- a/QSB/Syncs/RigidbodySync/SectoredRigidbodySync.cs +++ b/QSB/Syncs/RigidbodySync/SectoredRigidbodySync.cs @@ -5,7 +5,7 @@ using QuantumUNET.Transport; namespace QSB.Syncs.RigidbodySync { - public abstract class SectoredRigidbodySync : UnparentedBaseRigidbodySync, ISectoredSync + public abstract class SectoredRigidbodySync : BaseRigidbodySync, ISectoredSync { public QSBSector ReferenceSector { get; set; } public SectorSync.SectorSync SectorSync { get; private set; } From b5fad4b070c8b7e8318d83953ccd9c4b42584fbc Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 12 Aug 2021 14:20:05 +0100 Subject: [PATCH 010/118] dont run moon patch if worldobjects aren't ready --- QSB/QuantumSync/Patches/ServerQuantumPatches.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/QSB/QuantumSync/Patches/ServerQuantumPatches.cs b/QSB/QuantumSync/Patches/ServerQuantumPatches.cs index 49427de1..e312f571 100644 --- a/QSB/QuantumSync/Patches/ServerQuantumPatches.cs +++ b/QSB/QuantumSync/Patches/ServerQuantumPatches.cs @@ -38,6 +38,11 @@ namespace QSB.QuantumSync.Patches GameObject[] ____deactivateAtEye ) { + if (!QSBCore.WorldObjectsReady) + { + return false; + } + var isVisibleOutput = QuantumManager.IsVisibleUsingCameraFrustum((ShapeVisibilityTracker)____visibilityTracker, skipInstantVisibilityCheck); //var moonVisible = isVisibleOutput.First; var moonVisiblePlayers = isVisibleOutput.Second; From b7038febe95a155754a1ce1c2702426836e97e7d Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 12 Aug 2021 14:20:37 +0100 Subject: [PATCH 011/118] add InitSector, only write some debug writes if initialized --- .../TransformSync/SectoredTransformSync.cs | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/QSB/Syncs/TransformSync/SectoredTransformSync.cs b/QSB/Syncs/TransformSync/SectoredTransformSync.cs index b489c8bf..7efe7f16 100644 --- a/QSB/Syncs/TransformSync/SectoredTransformSync.cs +++ b/QSB/Syncs/TransformSync/SectoredTransformSync.cs @@ -49,6 +49,12 @@ namespace QSB.Syncs.TransformSync return; } + QSBCore.UnityEvents.RunWhen(() => SectorSync.IsReady, InitSector); + } + + private void InitSector() + { + DebugLog.DebugWrite($"InitSector of {_logName}"); var closestSector = SectorSync.GetClosestSector(AttachedObject.transform); if (closestSector != null) { @@ -56,7 +62,7 @@ namespace QSB.Syncs.TransformSync } else { - DebugLog.DebugWrite($"INIT - {PlayerId}.{GetType().Name}'s closest sector is null!"); + DebugLog.ToConsole($"Warning - {_logName}'s initial sector was null.", OWML.Common.MessageType.Warning); } } @@ -117,7 +123,10 @@ namespace QSB.Syncs.TransformSync } else { - DebugLog.ToConsole($"Warning - ReferenceSector of {PlayerId}.{GetType().Name} is null.", OWML.Common.MessageType.Warning); + if (_isInitialized) + { + DebugLog.ToConsole($"Warning - ReferenceSector of {PlayerId}.{GetType().Name} is null.", OWML.Common.MessageType.Warning); + } writer.Write(-1); } @@ -132,7 +141,7 @@ namespace QSB.Syncs.TransformSync sectorId = reader.ReadInt32(); if (initialState && sectorId != -1) { - DebugLog.DebugWrite($"SET WAITING FOR SECTOR SET - id {sectorId}"); + DebugLog.DebugWrite($"{_logName} set waiting sector id:{sectorId}"); _sectorIdWaitingSlot = sectorId; } reader.ReadVector3(); @@ -190,8 +199,12 @@ namespace QSB.Syncs.TransformSync } else { - DebugLog.ToConsole($"Error - No closest sector found to {PlayerId}.{GetType().Name}!", OWML.Common.MessageType.Error); - return false; + if (SectorSync.IsReady) + { + DebugLog.ToConsole($"Error - No closest sector found to {PlayerId}.{GetType().Name}!", OWML.Common.MessageType.Error); + } + + return base.UpdateTransform(); } } From d2c91f5d5ad9e95710c6789ad897ea3b0aeb8082 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 12 Aug 2021 14:20:59 +0100 Subject: [PATCH 012/118] rename _isReady to IsReady, make {get; private set;} --- QSB/SectorSync/SectorSync.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/QSB/SectorSync/SectorSync.cs b/QSB/SectorSync/SectorSync.cs index 4765bf8f..aa55d862 100644 --- a/QSB/SectorSync/SectorSync.cs +++ b/QSB/SectorSync/SectorSync.cs @@ -13,12 +13,12 @@ namespace QSB.SectorSync { public class SectorSync : MonoBehaviour { + public bool IsReady { get; private set; } public List SectorList = new List(); private OWRigidbody _attachedOWRigidbody; private SectorDetector _sectorDetector; private TargetType _targetType; - private bool _isReady; private void OnDestroy() { @@ -27,7 +27,7 @@ namespace QSB.SectorSync _sectorDetector.OnEnterSector -= AddSector; _sectorDetector.OnExitSector -= RemoveSector; } - _isReady = false; + IsReady = false; } public void Init(SectorDetector detector, ISectoredSync sectoredSync) @@ -58,7 +58,7 @@ namespace QSB.SectorSync PopulateSectorList(); _targetType = sectoredSync.Type; - _isReady = true; + IsReady = true; } private void PopulateSectorList() @@ -119,7 +119,7 @@ namespace QSB.SectorSync return null; } - if (!_isReady) + if (!IsReady) { DebugLog.ToConsole($"Warning - Tried to use GetClosestSector before it was initialized. Transform:{trans.name} Stacktrace:{Environment.StackTrace}", MessageType.Warning); return null; @@ -127,7 +127,7 @@ namespace QSB.SectorSync if (_sectorDetector == null || _attachedOWRigidbody == null || _targetType == TargetType.None) { - _isReady = false; + 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; } From fc01efd40726477c3b6cbab2b4e25d1a698c0551 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 14 Aug 2021 14:45:36 +0100 Subject: [PATCH 013/118] Create BaseUnsectoredSync.cs --- QSB/Syncs/Unsectored/BaseUnsectoredSync.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 QSB/Syncs/Unsectored/BaseUnsectoredSync.cs diff --git a/QSB/Syncs/Unsectored/BaseUnsectoredSync.cs b/QSB/Syncs/Unsectored/BaseUnsectoredSync.cs new file mode 100644 index 00000000..f4252909 --- /dev/null +++ b/QSB/Syncs/Unsectored/BaseUnsectoredSync.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace QSB.Syncs.Unsectored +{ + public abstract class BaseUnsectoredSync : SyncBase + { + public override bool IgnoreDisabledAttachedObject => false; + public override bool IgnoreNullReferenceTransform => false; + public override bool ShouldReparentAttachedObject => false; + } +} From f30b4375898e0fdec13e5846e7c5d2e5d0152241 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 14 Aug 2021 14:45:52 +0100 Subject: [PATCH 014/118] Move lots of things to SyncBase --- QSB/Syncs/SyncBase.cs | 175 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 164 insertions(+), 11 deletions(-) diff --git a/QSB/Syncs/SyncBase.cs b/QSB/Syncs/SyncBase.cs index d329ddb0..63eecd77 100644 --- a/QSB/Syncs/SyncBase.cs +++ b/QSB/Syncs/SyncBase.cs @@ -1,14 +1,46 @@ -using OWML.Common; +using Harmony; +using OWML.Common; using QSB.Player; +using QSB.Player.TransformSync; using QSB.Utility; using QSB.WorldSync; using QuantumUNET.Components; +using System; +using System.Collections.Generic; +using System.Linq; using UnityEngine; namespace QSB.Syncs { + /* + * Rewrite number : 9 + * God has cursed me for my hubris, and my work is never finished. + */ + public abstract class SyncBase : QNetworkTransform { + private static readonly Dictionary> _storedTransformSyncs = new Dictionary>(); + + public static T GetPlayers(PlayerInfo player) + where T : SyncBase + { + var dictOfOwnedSyncs = _storedTransformSyncs[player]; + 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 = Resources.FindObjectsOfTypeAll(); + 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 @@ -50,6 +82,7 @@ namespace QSB.Syncs public abstract bool UseInterpolation { get; } public abstract bool IgnoreDisabledAttachedObject { get; } public abstract bool IgnoreNullReferenceTransform { get; } + public abstract bool ShouldReparentAttachedObject { get; } public Component AttachedObject { get; set; } public Transform ReferenceTransform { get; set; } @@ -63,22 +96,82 @@ namespace QSB.Syncs protected IntermediaryTransform _intermediaryTransform; protected bool _isInitialized; - protected abstract Component InitLocalTransform(); - protected abstract Component InitRemoteTransform(); + protected abstract Component SetAttachedObject(); protected abstract bool UpdateTransform(); - protected abstract void Init(); - protected Vector3 SmartSmoothDamp(Vector3 currentPosition, Vector3 targetPosition) + public virtual void Start() { - var distance = Vector3.Distance(currentPosition, targetPosition); - if (distance > _previousDistance + DistanceLeeway) + DebugLog.DebugWrite($"[SyncBase] {_logName} Start"); + var lowestBound = Resources.FindObjectsOfTypeAll() + .Where(x => x.NetId.Value <= NetId.Value).OrderBy(x => x.NetId.Value).Last(); + NetIdentity.SetRootIdentity(lowestBound.NetIdentity); + + DontDestroyOnLoad(gameObject); + _intermediaryTransform = new IntermediaryTransform(transform); + QSBSceneManager.OnSceneLoaded += OnSceneLoaded; + + if (Player == null) { - _previousDistance = distance; - return targetPosition; + DebugLog.ToConsole($"Error - Player in start of {_logName} was null!", MessageType.Error); + return; } - _previousDistance = distance; - return Vector3.SmoothDamp(currentPosition, targetPosition, ref _positionSmoothVelocity, SmoothTime); + if (!_storedTransformSyncs.ContainsKey(Player)) + { + _storedTransformSyncs.Add(Player, new Dictionary()); + } + + var playerDict = _storedTransformSyncs[Player]; + playerDict[GetType()] = this; + } + + protected virtual void OnDestroy() + { + DebugLog.DebugWrite($"[SyncBase] {_logName} OnDestroy"); + if (ShouldReparentAttachedObject) + { + if (!HasAuthority && AttachedObject != null) + { + Destroy(AttachedObject.gameObject); + } + } + + QSBSceneManager.OnSceneLoaded -= OnSceneLoaded; + + if (!QSBPlayerManager.PlayerExists(PlayerId)) + { + return; + } + + var playerDict = _storedTransformSyncs[Player]; + playerDict.Remove(GetType()); + } + + protected virtual void Init() + { + DebugLog.DebugWrite($"[SyncBase] {_logName} Init"); + if (!QSBSceneManager.IsInUniverse) + { + DebugLog.ToConsole($"Error - {_logName} is being init-ed when not in the universe!", MessageType.Error); + } + + // TODO : maybe make it's own option + if (ShouldReparentAttachedObject) + { + if (!HasAuthority && AttachedObject != null) + { + Destroy(AttachedObject.gameObject); + } + } + + AttachedObject = SetAttachedObject(); + _isInitialized = true; + } + + protected virtual void OnSceneLoaded(OWScene oldScene, OWScene newScene, bool isInUniverse) + { + DebugLog.DebugWrite($"[SyncBase] {_logName} OnSceneLoaded"); + _isInitialized = false; } public override void Update() @@ -161,6 +254,66 @@ namespace QSB.Syncs base.Update(); } + protected Vector3 SmartSmoothDamp(Vector3 currentPosition, Vector3 targetPosition) + { + var distance = Vector3.Distance(currentPosition, targetPosition); + if (distance > _previousDistance + DistanceLeeway) + { + _previousDistance = distance; + return targetPosition; + } + + _previousDistance = distance; + return Vector3.SmoothDamp(currentPosition, targetPosition, ref _positionSmoothVelocity, SmoothTime); + } + + public void SetReferenceTransform(Transform transform) + { + DebugLog.DebugWrite($"[SyncBase] {_logName} SetReferenceTransform"); + if (ReferenceTransform == transform) + { + return; + } + + ReferenceTransform = transform; + _intermediaryTransform.SetReferenceTransform(transform); + + if (ShouldReparentAttachedObject) + { + if (AttachedObject == null) + { + DebugLog.ToConsole($"Warning - AttachedObject was null for {_logName} when trying to set reference transform to {transform?.name}. Waiting until not null...", MessageType.Warning); + QSBCore.UnityEvents.RunWhen( + () => AttachedObject != null, + () => ReparentAttachedObject(transform)); + return; + } + + if (!HasAuthority) + { + ReparentAttachedObject(transform); + } + } + + if (HasAuthority) + { + _intermediaryTransform.EncodePosition(AttachedObject.transform.position); + _intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation); + } + } + + private void ReparentAttachedObject(Transform newParent) + { + DebugLog.DebugWrite($"[SyncBase] {_logName} ReparentAttachedObject"); + if (AttachedObject.transform.parent != null && AttachedObject.transform.parent.GetComponent() == null) + { + DebugLog.ToConsole($"Warning - Trying to reparent AttachedObject {AttachedObject.name} which wasnt attached to sector!", MessageType.Warning); + } + + AttachedObject.transform.SetParent(newParent, true); + AttachedObject.transform.localScale = Vector3.one; + } + protected virtual void OnRenderObject() { if (!QSBCore.WorldObjectsReady From 1a7d172b37730bd07156fdd660e585608e63b408 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 14 Aug 2021 14:49:46 +0100 Subject: [PATCH 015/118] remoev ISectoredSync --- QSB/QSB.csproj | 1 - QSB/SectorSync/SectorSync.cs | 3 ++- QSB/Syncs/ISectoredSync.cs | 14 -------------- 3 files changed, 2 insertions(+), 16 deletions(-) delete mode 100644 QSB/Syncs/ISectoredSync.cs diff --git a/QSB/QSB.csproj b/QSB/QSB.csproj index 82b416e4..ac1b7919 100644 --- a/QSB/QSB.csproj +++ b/QSB/QSB.csproj @@ -278,7 +278,6 @@ - diff --git a/QSB/SectorSync/SectorSync.cs b/QSB/SectorSync/SectorSync.cs index aa55d862..b1d352fd 100644 --- a/QSB/SectorSync/SectorSync.cs +++ b/QSB/SectorSync/SectorSync.cs @@ -2,6 +2,7 @@ using OWML.Utils; using QSB.SectorSync.WorldObjects; using QSB.Syncs; +using QSB.Syncs.Sectored; using QSB.Utility; using QSB.WorldSync; using System; @@ -30,7 +31,7 @@ namespace QSB.SectorSync IsReady = false; } - public void Init(SectorDetector detector, ISectoredSync sectoredSync) + public void Init(SectorDetector detector, BaseSectoredSync sectoredSync) { DebugLog.DebugWrite($"INIT SECTOR SYNC detector:{detector.name}"); if (_sectorDetector != null) diff --git a/QSB/Syncs/ISectoredSync.cs b/QSB/Syncs/ISectoredSync.cs deleted file mode 100644 index 7c592894..00000000 --- a/QSB/Syncs/ISectoredSync.cs +++ /dev/null @@ -1,14 +0,0 @@ -using QSB.SectorSync; -using QSB.SectorSync.WorldObjects; - -namespace QSB.Syncs -{ - public interface ISectoredSync - { - SectorSync.SectorSync SectorSync { get; } - QSBSector ReferenceSector { get; } - TargetType Type { get; } - - void SetReferenceSector(QSBSector sector); - } -} From 235632200d473b058b990313f07ecc6ae3afcff6 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 14 Aug 2021 15:00:46 +0100 Subject: [PATCH 016/118] Update DebugGUI.cs --- QSB/Utility/DebugGUI.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QSB/Utility/DebugGUI.cs b/QSB/Utility/DebugGUI.cs index 5f1a8f76..81c60c53 100644 --- a/QSB/Utility/DebugGUI.cs +++ b/QSB/Utility/DebugGUI.cs @@ -1,7 +1,7 @@ using QSB.ClientServerStateSync; using QSB.Player; using QSB.ProbeSync.TransformSync; -using QSB.Syncs.TransformSync; +using QSB.Syncs; using QSB.TimeSync; using UnityEngine; @@ -67,7 +67,7 @@ namespace QSB.Utility offset2 += _debugLineSpacing; GUI.Label(new Rect(420, offset2, 400f, 20f), $" - Sector : {(sector == null ? "NULL" : sector.Name)}", guiStyle); offset2 += _debugLineSpacing; - var probeSync = BaseTransformSync.GetPlayers(player); + var probeSync = SyncBase.GetPlayers(player); if (probeSync != default) { var probeSector = probeSync.ReferenceSector; From 120b430cd1d1d8f8fdb90e54db10e6152ba757b7 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 14 Aug 2021 15:03:01 +0100 Subject: [PATCH 017/118] add new transform syncs --- .../TransformSync/NomaiOrbTransformSync.cs | 4 +- .../TransformSync/PlayerTransformSync.cs | 2 +- .../TransformSync/PlayerProbeSync.cs | 2 +- QSB/QSB.csproj | 11 +- QSB/SectorSync/QSBSectorManager.cs | 28 +-- .../TransformSync/ShipTransformSync.cs | 5 +- QSB/Syncs/Sectored/BaseSectoredSync.cs | 226 ++++++++++++++++++ .../Transforms/SectoredTransformSync.cs | 100 ++++++++ .../Transforms/UnsectoredTransformSync.cs | 89 +++++++ 9 files changed, 432 insertions(+), 35 deletions(-) create mode 100644 QSB/Syncs/Sectored/BaseSectoredSync.cs create mode 100644 QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs create mode 100644 QSB/Syncs/Unsectored/Transforms/UnsectoredTransformSync.cs diff --git a/QSB/OrbSync/TransformSync/NomaiOrbTransformSync.cs b/QSB/OrbSync/TransformSync/NomaiOrbTransformSync.cs index bee4e965..dc9cdfc2 100644 --- a/QSB/OrbSync/TransformSync/NomaiOrbTransformSync.cs +++ b/QSB/OrbSync/TransformSync/NomaiOrbTransformSync.cs @@ -1,4 +1,4 @@ -using QSB.Syncs.TransformSync; +using QSB.Syncs.Unsectored.Transforms; using QSB.Utility; using QSB.WorldSync; using System.Collections.Generic; @@ -6,7 +6,7 @@ using UnityEngine; namespace QSB.OrbSync.TransformSync { - internal class NomaiOrbTransformSync : UnparentedBaseTransformSync + internal class NomaiOrbTransformSync : UnsectoredTransformSync { public static List OrbTransformSyncs = new List(); diff --git a/QSB/Player/TransformSync/PlayerTransformSync.cs b/QSB/Player/TransformSync/PlayerTransformSync.cs index f3b89a91..d748cc99 100644 --- a/QSB/Player/TransformSync/PlayerTransformSync.cs +++ b/QSB/Player/TransformSync/PlayerTransformSync.cs @@ -4,7 +4,7 @@ using QSB.Events; using QSB.Instruments; using QSB.RoastingSync; using QSB.SectorSync; -using QSB.Syncs.TransformSync; +using QSB.Syncs.Sectored.Transforms; using QSB.Tools; using QSB.Utility; using QSB.WorldSync; diff --git a/QSB/ProbeSync/TransformSync/PlayerProbeSync.cs b/QSB/ProbeSync/TransformSync/PlayerProbeSync.cs index e51cfa35..dbc7dba6 100644 --- a/QSB/ProbeSync/TransformSync/PlayerProbeSync.cs +++ b/QSB/ProbeSync/TransformSync/PlayerProbeSync.cs @@ -1,7 +1,7 @@ using OWML.Common; using OWML.Utils; using QSB.SectorSync; -using QSB.Syncs.TransformSync; +using QSB.Syncs.Sectored.Transforms; using QSB.Tools; using QSB.Tools.ProbeLauncherTool; using QSB.Utility; diff --git a/QSB/QSB.csproj b/QSB/QSB.csproj index ac1b7919..088c2d87 100644 --- a/QSB/QSB.csproj +++ b/QSB/QSB.csproj @@ -278,9 +278,13 @@ - - + + + + + + @@ -308,10 +312,7 @@ - - - diff --git a/QSB/SectorSync/QSBSectorManager.cs b/QSB/SectorSync/QSBSectorManager.cs index fc94c8a5..534f83fd 100644 --- a/QSB/SectorSync/QSBSectorManager.cs +++ b/QSB/SectorSync/QSBSectorManager.cs @@ -1,6 +1,7 @@ using OWML.Common; using QSB.SectorSync.WorldObjects; using QSB.Syncs; +using QSB.Syncs.Sectored; using QSB.Utility; using QSB.WorldSync; using System.Collections.Generic; @@ -18,8 +19,7 @@ namespace QSB.SectorSync private void OnEnable() => RepeatingManager.Repeatings.Add(this); private void OnDisable() => RepeatingManager.Repeatings.Remove(this); - public List SectoredTransformSyncs = new List(); - public List SectoredRigidbodySyncs = new List(); + public List SectoredSyncs = new List(); public void Invoke() { @@ -28,7 +28,7 @@ namespace QSB.SectorSync return; } - foreach (var sync in SectoredTransformSyncs) + foreach (var sync in SectoredSyncs) { if (sync.AttachedObject == null) { @@ -39,22 +39,7 @@ namespace QSB.SectorSync && sync.AttachedObject.gameObject.activeInHierarchy && sync.IsReady) { - CheckTransformSyncSector(sync as ISectoredSync); - } - } - - foreach (var sync in SectoredRigidbodySyncs) - { - if (sync.AttachedObject == null) - { - continue; - } - - if (sync.HasAuthority - && sync.AttachedObject.gameObject.activeInHierarchy - && sync.IsReady) - { - CheckTransformSyncSector(sync as ISectoredSync); + CheckTransformSyncSector(sync); } } } @@ -89,10 +74,9 @@ namespace QSB.SectorSync IsReady = QSBWorldSync.GetWorldObjects().Any(); } - private void CheckTransformSyncSector(ISectoredSync transformSync) - where T : Component + private void CheckTransformSyncSector(BaseSectoredSync transformSync) { - var attachedObject = (transformSync as SyncBase).AttachedObject; + var attachedObject = transformSync.AttachedObject; var closestSector = transformSync.SectorSync.GetClosestSector(attachedObject.transform); if (closestSector == default(QSBSector)) { diff --git a/QSB/ShipSync/TransformSync/ShipTransformSync.cs b/QSB/ShipSync/TransformSync/ShipTransformSync.cs index a76937ed..4b6f68fd 100644 --- a/QSB/ShipSync/TransformSync/ShipTransformSync.cs +++ b/QSB/ShipSync/TransformSync/ShipTransformSync.cs @@ -1,6 +1,6 @@ using QSB.Player; using QSB.SectorSync; -using QSB.Syncs.RigidbodySync; +using QSB.Syncs.Sectored.Rigidbodies; using QSB.Utility; using QSB.WorldSync; using UnityEngine; @@ -20,9 +20,6 @@ namespace QSB.ShipSync.TransformSync LocalInstance = this; } - protected override Component InitLocalTransform() => throw new System.NotImplementedException(); - protected override Component InitRemoteTransform() => throw new System.NotImplementedException(); - protected override OWRigidbody GetRigidbody() { QSBCore.UnityEvents.RunWhen(() => WorldObjectManager.AllReady, () => SectorSync.Init(Locator.GetShipDetector().GetComponent(), this)); diff --git a/QSB/Syncs/Sectored/BaseSectoredSync.cs b/QSB/Syncs/Sectored/BaseSectoredSync.cs new file mode 100644 index 00000000..bd03e8eb --- /dev/null +++ b/QSB/Syncs/Sectored/BaseSectoredSync.cs @@ -0,0 +1,226 @@ +using QSB.SectorSync.WorldObjects; +using QSB.SectorSync; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using QSB.Player.TransformSync; +using UnityEngine; +using OWML.Common; +using QSB.Utility; +using QSB.Player; +using QSB.WorldSync; +using QuantumUNET.Transport; + +namespace QSB.Syncs.Sectored +{ + public abstract class BaseSectoredSync : SyncBase + { + public override bool IgnoreDisabledAttachedObject => false; + public override bool IgnoreNullReferenceTransform => true; + + public QSBSector ReferenceSector { get; set; } + public SectorSync.SectorSync SectorSync { get; private set; } + public abstract TargetType Type { get; } + + private int _sectorIdWaitingSlot = int.MinValue; + + public override void Start() + { + SectorSync = gameObject.AddComponent(); + QSBSectorManager.Instance.SectoredSyncs.Add(this); + base.Start(); + } + + protected override void OnDestroy() + { + base.OnDestroy(); + QSBSectorManager.Instance.SectoredSyncs.Remove(this); + if (SectorSync != null) + { + Destroy(SectorSync); + } + } + + protected override void Init() + { + base.Init(); + if (!QSBSectorManager.Instance.IsReady) + { + return; + } + + if (!HasAuthority) + { + return; + } + + QSBCore.UnityEvents.RunWhen(() => SectorSync.IsReady, InitSector); + } + + private void InitSector() + { + DebugLog.DebugWrite($"[BaseSectoredSync] {_logName} InitSector"); + var closestSector = SectorSync.GetClosestSector(AttachedObject.transform); + if (closestSector != null) + { + SetReferenceSector(closestSector); + } + else + { + DebugLog.ToConsole($"Warning - {_logName}'s initial sector was null.", OWML.Common.MessageType.Warning); + } + } + + public override void Update() + { + if (_sectorIdWaitingSlot == int.MinValue) + { + base.Update(); + return; + } + + if (!WorldObjectManager.AllReady) + { + base.Update(); + return; + } + + var sector = _sectorIdWaitingSlot == -1 + ? null + : QSBWorldSync.GetWorldFromId(_sectorIdWaitingSlot); + + if (sector != ReferenceSector) + { + if (sector == null) + { + DebugLog.ToConsole($"Error - {PlayerId}.{GetType().Name} got sector of ID -1.", OWML.Common.MessageType.Error); + base.Update(); + return; + } + + SetReferenceSector(sector); + } + + _sectorIdWaitingSlot = int.MinValue; + + base.Update(); + } + + public override void SerializeTransform(QNetworkWriter writer, bool initialState) + { + if (_intermediaryTransform == null) + { + _intermediaryTransform = new IntermediaryTransform(transform); + } + + if (!QSBPlayerManager.PlayerExists(PlayerId)) + { + writer.Write(-1); + } + else if (!Player.PlayerStates.IsReady) + { + writer.Write(-1); + } + else if (ReferenceSector != null) + { + writer.Write(ReferenceSector.ObjectId); + } + else + { + if (_isInitialized) + { + DebugLog.ToConsole($"Warning - ReferenceSector of {PlayerId}.{GetType().Name} is null.", OWML.Common.MessageType.Warning); + } + + writer.Write(-1); + } + } + + public override void DeserializeTransform(QNetworkReader reader, bool initialState) + { + int sectorId; + if (!QSBCore.WorldObjectsReady) + { + sectorId = reader.ReadInt32(); + DebugLog.DebugWrite($" - {_logName} ReadInt32 {sectorId}"); + if (initialState && sectorId != -1) + { + DebugLog.DebugWrite($"{_logName} set waiting sector id:{sectorId}"); + _sectorIdWaitingSlot = sectorId; + } + return; + } + + sectorId = reader.ReadInt32(); + DebugLog.DebugWrite($" - {_logName} ReadInt32 {sectorId}"); + var sector = sectorId == -1 + ? null + : QSBWorldSync.GetWorldFromId(sectorId); + + if (sector != ReferenceSector) + { + if (sector == null) + { + DebugLog.ToConsole($"Error - {PlayerId}.{GetType().Name} got sector of ID -1.", OWML.Common.MessageType.Error); + return; + } + + SetReferenceSector(sector); + } + } + + protected override bool UpdateTransform() + { + var referenceNull = ReferenceTransform == null || ReferenceSector == null || _intermediaryTransform.GetReferenceTransform() == null; + var sectorManagerReady = QSBSectorManager.Instance.IsReady; + + if (!sectorManagerReady) + { + if (referenceNull && HasAuthority) + { + DebugLog.ToConsole($"Warning - Reference was null, but sector manager wasn't ready. " + + $"Transform:{ReferenceTransform == null}, Sector:{ReferenceSector == null}, Intermediary:{_intermediaryTransform.GetReferenceTransform() == null}", + OWML.Common.MessageType.Warning); + } + + DebugLog.DebugWrite($"[BaseSectoredSync] {_logName} : Sector Manager not ready."); + return false; + } + + if (!HasAuthority) + { + DebugLog.DebugWrite($"[BaseSectoredSync] {_logName} : Does not have authority."); + return false; + } + + if (referenceNull) + { + var closestSector = SectorSync.GetClosestSector(AttachedObject.transform); + if (closestSector != null) + { + SetReferenceTransform(closestSector.Transform); + } + else + { + if (SectorSync.IsReady) + { + DebugLog.ToConsole($"Error - No closest sector found to {PlayerId}.{GetType().Name}!", OWML.Common.MessageType.Error); + } + + DebugLog.DebugWrite($"[BaseSectoredSync] {_logName} : No sector found."); + return false; + } + } + + return true; + } + + public void SetReferenceSector(QSBSector sector) + { + DebugLog.DebugWrite($"[BaseSectoredSync] {_logName} SetReferenceSector {sector.Name}"); + ReferenceSector = sector; + SetReferenceTransform(sector?.Transform); + } + } +} diff --git a/QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs b/QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs new file mode 100644 index 00000000..0addacea --- /dev/null +++ b/QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs @@ -0,0 +1,100 @@ +using OWML.Common; +using QSB.Utility; +using QuantumUNET.Transport; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; + +namespace QSB.Syncs.Sectored.Transforms +{ + public abstract class SectoredTransformSync : BaseSectoredSync + { + public override bool ShouldReparentAttachedObject => true; + + protected abstract Component InitLocalTransform(); + protected abstract Component InitRemoteTransform(); + + protected override Component SetAttachedObject() + => HasAuthority ? InitLocalTransform() : InitRemoteTransform(); + + public override void SerializeTransform(QNetworkWriter writer, bool initialState) + { + base.SerializeTransform(writer, initialState); + + var worldPos = _intermediaryTransform.GetPosition(); + var worldRot = _intermediaryTransform.GetRotation(); + writer.Write(worldPos); + SerializeRotation(writer, worldRot); + _prevPosition = worldPos; + _prevRotation = worldRot; + } + + public override void DeserializeTransform(QNetworkReader reader, bool initialState) + { + base.DeserializeTransform(reader, initialState); + + if (!QSBCore.WorldObjectsReady) + { + reader.ReadVector3(); + DeserializeRotation(reader); + return; + } + + var pos = reader.ReadVector3(); + var rot = DeserializeRotation(reader); + + if (HasAuthority) + { + return; + } + + if (_intermediaryTransform == null) + { + _intermediaryTransform = new IntermediaryTransform(transform); + } + + _intermediaryTransform.SetPosition(pos); + _intermediaryTransform.SetRotation(rot); + + if (_intermediaryTransform.GetPosition() == Vector3.zero) + { + DebugLog.ToConsole($"Warning - {_logName} at (0,0,0)! - Given position was {pos}", MessageType.Warning); + } + } + + protected override bool UpdateTransform() + { + if (!base.UpdateTransform()) + { + return false; + } + + if (HasAuthority) + { + _intermediaryTransform.EncodePosition(AttachedObject.transform.position); + _intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation); + return true; + } + + var targetPos = _intermediaryTransform.GetTargetPosition_ParentedToReference(); + var targetRot = _intermediaryTransform.GetTargetRotation_ParentedToReference(); + if (targetPos != Vector3.zero && _intermediaryTransform.GetTargetPosition_Unparented() != Vector3.zero) + { + if (UseInterpolation) + { + AttachedObject.transform.localPosition = SmartSmoothDamp(AttachedObject.transform.localPosition, targetPos); + AttachedObject.transform.localRotation = QuaternionHelper.SmoothDamp(AttachedObject.transform.localRotation, targetRot, ref _rotationSmoothVelocity, SmoothTime); + } + else + { + AttachedObject.transform.localPosition = targetPos; + AttachedObject.transform.localRotation = targetRot; + } + } + + return true; + } + } +} diff --git a/QSB/Syncs/Unsectored/Transforms/UnsectoredTransformSync.cs b/QSB/Syncs/Unsectored/Transforms/UnsectoredTransformSync.cs new file mode 100644 index 00000000..94454dd6 --- /dev/null +++ b/QSB/Syncs/Unsectored/Transforms/UnsectoredTransformSync.cs @@ -0,0 +1,89 @@ +using OWML.Common; +using QSB.Utility; +using QuantumUNET.Transport; +using UnityEngine; + +namespace QSB.Syncs.Unsectored.Transforms +{ + public abstract class UnsectoredTransformSync : BaseUnsectoredSync + { + protected abstract Component InitLocalTransform(); + protected abstract Component InitRemoteTransform(); + + protected override Component SetAttachedObject() + => HasAuthority ? InitLocalTransform() : InitRemoteTransform(); + + public override void SerializeTransform(QNetworkWriter writer, bool initialState) + { + base.SerializeTransform(writer, initialState); + + var worldPos = _intermediaryTransform.GetPosition(); + var worldRot = _intermediaryTransform.GetRotation(); + writer.Write(worldPos); + SerializeRotation(writer, worldRot); + _prevPosition = worldPos; + _prevRotation = worldRot; + } + + public override void DeserializeTransform(QNetworkReader reader, bool initialState) + { + base.DeserializeTransform(reader, initialState); + + if (!QSBCore.WorldObjectsReady) + { + reader.ReadVector3(); + DeserializeRotation(reader); + return; + } + + var pos = reader.ReadVector3(); + var rot = DeserializeRotation(reader); + + if (HasAuthority) + { + return; + } + + if (_intermediaryTransform == null) + { + _intermediaryTransform = new IntermediaryTransform(transform); + } + + _intermediaryTransform.SetPosition(pos); + _intermediaryTransform.SetRotation(rot); + + if (_intermediaryTransform.GetPosition() == Vector3.zero) + { + DebugLog.ToConsole($"Warning - {_logName} at (0,0,0)! - Given position was {pos}", MessageType.Warning); + } + } + + protected override bool UpdateTransform() + { + if (HasAuthority) + { + _intermediaryTransform.EncodePosition(AttachedObject.transform.position); + _intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation); + return true; + } + + var targetPos = _intermediaryTransform.GetTargetPosition_ParentedToReference(); + var targetRot = _intermediaryTransform.GetTargetRotation_ParentedToReference(); + if (targetPos != Vector3.zero && _intermediaryTransform.GetTargetPosition_Unparented() != Vector3.zero) + { + if (UseInterpolation) + { + AttachedObject.transform.localPosition = SmartSmoothDamp(AttachedObject.transform.localPosition, targetPos); + AttachedObject.transform.localRotation = QuaternionHelper.SmoothDamp(AttachedObject.transform.localRotation, targetRot, ref _rotationSmoothVelocity, SmoothTime); + } + else + { + AttachedObject.transform.localPosition = targetPos; + AttachedObject.transform.localRotation = targetRot; + } + } + + return true; + } + } +} From 3a33c0987d0d1997ae0023e7a58d6f16f670917a Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Tue, 17 Aug 2021 10:02:19 +0100 Subject: [PATCH 018/118] update music list --- QSB/QSBCore.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/QSB/QSBCore.cs b/QSB/QSBCore.cs index e5c0d8b5..c59fdd03 100644 --- a/QSB/QSBCore.cs +++ b/QSB/QSBCore.cs @@ -178,4 +178,5 @@ namespace QSB * Teleskärm * Daft Punk * Natalie Holt + * WMD */ \ No newline at end of file From bb5cd44084a5bab8625d5d66024ee6087e1d0a90 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Tue, 17 Aug 2021 10:07:53 +0100 Subject: [PATCH 019/118] remove mouse input debug gui --- QSB/Utility/DebugGUI.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/QSB/Utility/DebugGUI.cs b/QSB/Utility/DebugGUI.cs index 81c60c53..349e539a 100644 --- a/QSB/Utility/DebugGUI.cs +++ b/QSB/Utility/DebugGUI.cs @@ -41,8 +41,6 @@ namespace QSB.Utility offset += _debugLineSpacing; GUI.Label(new Rect(220, offset, 200f, 20f), $"Timescale : {OWTime.GetTimeScale()}", guiStyle); offset += _debugLineSpacing; - GUI.Label(new Rect(220, offset, 200f, 20f), $"Mouse input : {OWInput.GetValue(InputLibrary.look, false, InputMode.All)}", guiStyle); - offset += _debugLineSpacing; } var offset2 = 10f; From bdef0e428ca9b367bbd7cb715c1dfed419ad7909 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 19 Aug 2021 16:32:18 +0100 Subject: [PATCH 020/118] remove logs --- QSB/Syncs/Sectored/BaseSectoredSync.cs | 14 +------------- QSB/Syncs/SyncBase.cs | 11 +++++------ 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/QSB/Syncs/Sectored/BaseSectoredSync.cs b/QSB/Syncs/Sectored/BaseSectoredSync.cs index bd03e8eb..62a649ad 100644 --- a/QSB/Syncs/Sectored/BaseSectoredSync.cs +++ b/QSB/Syncs/Sectored/BaseSectoredSync.cs @@ -1,12 +1,5 @@ using QSB.SectorSync.WorldObjects; using QSB.SectorSync; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using QSB.Player.TransformSync; -using UnityEngine; -using OWML.Common; using QSB.Utility; using QSB.Player; using QSB.WorldSync; @@ -60,7 +53,6 @@ namespace QSB.Syncs.Sectored private void InitSector() { - DebugLog.DebugWrite($"[BaseSectoredSync] {_logName} InitSector"); var closestSector = SectorSync.GetClosestSector(AttachedObject.transform); if (closestSector != null) { @@ -143,7 +135,6 @@ namespace QSB.Syncs.Sectored if (!QSBCore.WorldObjectsReady) { sectorId = reader.ReadInt32(); - DebugLog.DebugWrite($" - {_logName} ReadInt32 {sectorId}"); if (initialState && sectorId != -1) { DebugLog.DebugWrite($"{_logName} set waiting sector id:{sectorId}"); @@ -153,7 +144,6 @@ namespace QSB.Syncs.Sectored } sectorId = reader.ReadInt32(); - DebugLog.DebugWrite($" - {_logName} ReadInt32 {sectorId}"); var sector = sectorId == -1 ? null : QSBWorldSync.GetWorldFromId(sectorId); @@ -190,8 +180,7 @@ namespace QSB.Syncs.Sectored if (!HasAuthority) { - DebugLog.DebugWrite($"[BaseSectoredSync] {_logName} : Does not have authority."); - return false; + return true; } if (referenceNull) @@ -218,7 +207,6 @@ namespace QSB.Syncs.Sectored public void SetReferenceSector(QSBSector sector) { - DebugLog.DebugWrite($"[BaseSectoredSync] {_logName} SetReferenceSector {sector.Name}"); ReferenceSector = sector; SetReferenceTransform(sector?.Transform); } diff --git a/QSB/Syncs/SyncBase.cs b/QSB/Syncs/SyncBase.cs index 63eecd77..da7b6d23 100644 --- a/QSB/Syncs/SyncBase.cs +++ b/QSB/Syncs/SyncBase.cs @@ -101,7 +101,6 @@ namespace QSB.Syncs public virtual void Start() { - DebugLog.DebugWrite($"[SyncBase] {_logName} Start"); var lowestBound = Resources.FindObjectsOfTypeAll() .Where(x => x.NetId.Value <= NetId.Value).OrderBy(x => x.NetId.Value).Last(); NetIdentity.SetRootIdentity(lowestBound.NetIdentity); @@ -127,7 +126,6 @@ namespace QSB.Syncs protected virtual void OnDestroy() { - DebugLog.DebugWrite($"[SyncBase] {_logName} OnDestroy"); if (ShouldReparentAttachedObject) { if (!HasAuthority && AttachedObject != null) @@ -149,7 +147,6 @@ namespace QSB.Syncs protected virtual void Init() { - DebugLog.DebugWrite($"[SyncBase] {_logName} Init"); if (!QSBSceneManager.IsInUniverse) { DebugLog.ToConsole($"Error - {_logName} is being init-ed when not in the universe!", MessageType.Error); @@ -170,7 +167,6 @@ namespace QSB.Syncs protected virtual void OnSceneLoaded(OWScene oldScene, OWScene newScene, bool isInUniverse) { - DebugLog.DebugWrite($"[SyncBase] {_logName} OnSceneLoaded"); _isInitialized = false; } @@ -259,6 +255,11 @@ namespace QSB.Syncs var distance = Vector3.Distance(currentPosition, targetPosition); if (distance > _previousDistance + DistanceLeeway) { + /* + DebugLog.DebugWrite($"{_logName} moved too far!" + + $"\r\n CurrentPosition:{currentPosition}," + + $"\r\n TargetPosition:{targetPosition}"); + */ _previousDistance = distance; return targetPosition; } @@ -269,7 +270,6 @@ namespace QSB.Syncs public void SetReferenceTransform(Transform transform) { - DebugLog.DebugWrite($"[SyncBase] {_logName} SetReferenceTransform"); if (ReferenceTransform == transform) { return; @@ -304,7 +304,6 @@ namespace QSB.Syncs private void ReparentAttachedObject(Transform newParent) { - DebugLog.DebugWrite($"[SyncBase] {_logName} ReparentAttachedObject"); if (AttachedObject.transform.parent != null && AttachedObject.transform.parent.GetComponent() == null) { DebugLog.ToConsole($"Warning - Trying to reparent AttachedObject {AttachedObject.name} which wasnt attached to sector!", MessageType.Warning); From 28691065a05a8a573e98303e0828b63a0f6dcaaf Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 19 Aug 2021 16:37:29 +0100 Subject: [PATCH 021/118] add SpawnWithServerAuthority extension --- QSB/OrbSync/OrbManager.cs | 6 +++++- QSB/ShipSync/ShipManager.cs | 3 ++- QSB/Utility/Extensions.cs | 12 ++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/QSB/OrbSync/OrbManager.cs b/QSB/OrbSync/OrbManager.cs index 2c8b4d97..975452bd 100644 --- a/QSB/OrbSync/OrbManager.cs +++ b/QSB/OrbSync/OrbManager.cs @@ -1,6 +1,7 @@ using OWML.Common; using QSB.OrbSync.TransformSync; using QSB.OrbSync.WorldObjects; +using QSB.Player; using QSB.Utility; using QSB.WorldSync; using QuantumUNET; @@ -26,7 +27,10 @@ namespace QSB.OrbSync { NomaiOrbTransformSync.OrbTransformSyncs.ForEach(x => QNetworkServer.Destroy(x.gameObject)); NomaiOrbTransformSync.OrbTransformSyncs.Clear(); - QSBWorldSync.OldOrbList.ForEach(x => QNetworkServer.Spawn(Instantiate(QSBNetworkManager.Instance.OrbPrefab))); + foreach (var orb in QSBWorldSync.OldOrbList) + { + Instantiate(QSBNetworkManager.Instance.OrbPrefab).SpawnWithServerAuthority(); + } } DebugLog.DebugWrite($"Finished orb build with {QSBWorldSync.OldOrbList.Count} orbs.", MessageType.Success); diff --git a/QSB/ShipSync/ShipManager.cs b/QSB/ShipSync/ShipManager.cs index 0eab4e49..1052fbd4 100644 --- a/QSB/ShipSync/ShipManager.cs +++ b/QSB/ShipSync/ShipManager.cs @@ -85,7 +85,8 @@ namespace QSB.ShipSync { DebugLog.ToConsole($"Error - Tried to spawn ship, but LocalPlayer's TransformSync is null!", MessageType.Error); } - QNetworkServer.SpawnWithClientAuthority(Instantiate(QSBNetworkManager.Instance.ShipPrefab), QSBPlayerManager.LocalPlayer.TransformSync.gameObject); + + Instantiate(QSBNetworkManager.Instance.ShipPrefab).SpawnWithServerAuthority(); } QSBWorldSync.Init(); diff --git a/QSB/Utility/Extensions.cs b/QSB/Utility/Extensions.cs index 7285cba0..dbba8bd7 100644 --- a/QSB/Utility/Extensions.cs +++ b/QSB/Utility/Extensions.cs @@ -1,5 +1,6 @@ using OWML.Common; using OWML.Utils; +using QSB.Player; using QSB.Player.TransformSync; using QuantumUNET; using System; @@ -72,6 +73,17 @@ namespace QSB.Utility return controller.NetId.Value; } + public static void SpawnWithServerAuthority(this GameObject go) + { + if (!QSBCore.IsHost) + { + DebugLog.ToConsole($"Error - Tried to spawn {go.name} using SpawnWithServerAuthority when not the host!", MessageType.Error); + return; + } + + QNetworkServer.SpawnWithClientAuthority(go, QSBPlayerManager.LocalPlayer.TransformSync.gameObject); + } + // C# public static void SafeInvoke(this MulticastDelegate multicast, params object[] args) { From 988ee052110a69f5a11952a0576ff3cc8ce4a18a Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 19 Aug 2021 17:27:37 +0100 Subject: [PATCH 022/118] remove old transform sync --- QSB/Syncs/TransformSync/BaseTransformSync.cs | 264 ------------------ .../TransformSync/SectoredTransformSync.cs | 220 --------------- .../UnparentedBaseTransformSync.cs | 144 ---------- 3 files changed, 628 deletions(-) delete mode 100644 QSB/Syncs/TransformSync/BaseTransformSync.cs delete mode 100644 QSB/Syncs/TransformSync/SectoredTransformSync.cs delete mode 100644 QSB/Syncs/TransformSync/UnparentedBaseTransformSync.cs diff --git a/QSB/Syncs/TransformSync/BaseTransformSync.cs b/QSB/Syncs/TransformSync/BaseTransformSync.cs deleted file mode 100644 index 100d3e0b..00000000 --- a/QSB/Syncs/TransformSync/BaseTransformSync.cs +++ /dev/null @@ -1,264 +0,0 @@ -using OWML.Common; -using QSB.Player; -using QSB.Player.TransformSync; -using QSB.Utility; -using QuantumUNET; -using QuantumUNET.Transport; -using System; -using System.Collections.Generic; -using System.Linq; -using UnityEngine; - -namespace QSB.Syncs.TransformSync -{ - /* - * Rewrite number : 7 - * God has cursed me for my hubris, and my work is never finished. - */ - - public abstract class BaseTransformSync : SyncBase - { - private static readonly Dictionary> _storedTransformSyncs = new Dictionary>(); - - public static T GetPlayers(PlayerInfo player) - where T : BaseTransformSync - { - var dictOfOwnedSyncs = _storedTransformSyncs[player]; - 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 = Resources.FindObjectsOfTypeAll(); - 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 virtual void Start() - { - var lowestBound = Resources.FindObjectsOfTypeAll() - .Where(x => x.NetId.Value <= NetId.Value).OrderBy(x => x.NetId.Value).Last(); - NetIdentity.SetRootIdentity(lowestBound.NetIdentity); - - DontDestroyOnLoad(gameObject); - _intermediaryTransform = new IntermediaryTransform(transform); - QSBSceneManager.OnSceneLoaded += OnSceneLoaded; - - if (Player == null) - { - DebugLog.ToConsole($"Error - Player in start of {_logName} was null!", MessageType.Error); - return; - } - - if (!_storedTransformSyncs.ContainsKey(Player)) - { - _storedTransformSyncs.Add(Player, new Dictionary()); - } - - var playerDict = _storedTransformSyncs[Player]; - playerDict[GetType()] = this; - } - - protected virtual void OnDestroy() - { - if (!HasAuthority && AttachedObject != null) - { - Destroy(AttachedObject.gameObject); - } - - QSBSceneManager.OnSceneLoaded -= OnSceneLoaded; - - if (!QSBPlayerManager.PlayerExists(PlayerId)) - { - return; - } - - var playerDict = _storedTransformSyncs[Player]; - playerDict.Remove(GetType()); - } - - protected virtual void OnSceneLoaded(OWScene oldScene, OWScene newScene, bool isInUniverse) - => _isInitialized = false; - - protected override void Init() - { - if (!QSBSceneManager.IsInUniverse) - { - DebugLog.ToConsole($"Error - {_logName} is being init-ed when not in the universe!", MessageType.Error); - } - - if (!HasAuthority && AttachedObject != null) - { - Destroy(AttachedObject.gameObject); - } - - AttachedObject = HasAuthority ? InitLocalTransform() : InitRemoteTransform(); - _isInitialized = true; - - if (QSBCore.DebugMode) - { - DebugBoxManager.CreateBox(AttachedObject.transform, 0, _logName); - } - } - - public override bool OnSerialize(QNetworkWriter writer, bool initialState) - { - if (!initialState) - { - if (SyncVarDirtyBits == 0U) - { - writer.WritePackedUInt32(0U); - return false; - } - - writer.WritePackedUInt32(1U); - } - - SerializeTransform(writer, initialState); - return true; - } - - public override void OnDeserialize(QNetworkReader reader, bool initialState) - { - if (!IsServer || !QNetworkServer.localClientActive) - { - if (!initialState) - { - if (reader.ReadPackedUInt32() == 0U) - { - return; - } - } - - DeserializeTransform(reader, initialState); - } - } - - public override void SerializeTransform(QNetworkWriter writer, bool initialState) - { - if (_intermediaryTransform == null) - { - _intermediaryTransform = new IntermediaryTransform(transform); - } - - var worldPos = _intermediaryTransform.GetPosition(); - var worldRot = _intermediaryTransform.GetRotation(); - writer.Write(worldPos); - SerializeRotation(writer, worldRot); - _prevPosition = worldPos; - _prevRotation = worldRot; - } - - public override void DeserializeTransform(QNetworkReader reader, bool initialState) - { - if (!QSBCore.WorldObjectsReady) - { - reader.ReadVector3(); - DeserializeRotation(reader); - return; - } - - var pos = reader.ReadVector3(); - var rot = DeserializeRotation(reader); - - if (HasAuthority) - { - return; - } - - if (_intermediaryTransform == null) - { - _intermediaryTransform = new IntermediaryTransform(transform); - } - - _intermediaryTransform.SetPosition(pos); - _intermediaryTransform.SetRotation(rot); - - if (_intermediaryTransform.GetPosition() == Vector3.zero) - { - DebugLog.ToConsole($"Warning - {_logName} at (0,0,0)! - Given position was {pos}", MessageType.Warning); - } - } - - protected override bool UpdateTransform() - { - if (HasAuthority) - { - _intermediaryTransform.EncodePosition(AttachedObject.transform.position); - _intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation); - return true; - } - - var targetPos = _intermediaryTransform.GetTargetPosition_ParentedToReference(); - var targetRot = _intermediaryTransform.GetTargetRotation_ParentedToReference(); - if (targetPos != Vector3.zero && _intermediaryTransform.GetTargetPosition_Unparented() != Vector3.zero) - { - if (UseInterpolation) - { - AttachedObject.transform.localPosition = SmartSmoothDamp(AttachedObject.transform.localPosition, targetPos); - AttachedObject.transform.localRotation = QuaternionHelper.SmoothDamp(AttachedObject.transform.localRotation, targetRot, ref _rotationSmoothVelocity, SmoothTime); - } - else - { - AttachedObject.transform.localPosition = targetPos; - AttachedObject.transform.localRotation = targetRot; - } - } - return true; - } - - public override bool HasMoved() - { - var displacementMagnitude = (_intermediaryTransform.GetPosition() - _prevPosition).magnitude; - return displacementMagnitude > 1E-03f - || Quaternion.Angle(_intermediaryTransform.GetRotation(), _prevRotation) > 1E-03f; - } - - public void SetReferenceTransform(Transform transform) - { - if (ReferenceTransform == transform) - { - return; - } - - ReferenceTransform = transform; - _intermediaryTransform.SetReferenceTransform(transform); - if (AttachedObject == null) - { - DebugLog.ToConsole($"Warning - AttachedObject was null for {_logName} when trying to set reference transform to {transform?.name}. Waiting until not null...", MessageType.Warning); - QSBCore.UnityEvents.RunWhen( - () => AttachedObject != null, - () => ReparentAttachedObject(transform)); - return; - } - - if (!HasAuthority) - { - ReparentAttachedObject(transform); - } - - if (HasAuthority) - { - _intermediaryTransform.EncodePosition(AttachedObject.transform.position); - _intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation); - } - } - - private void ReparentAttachedObject(Transform newParent) - { - if (AttachedObject.transform.parent != null && AttachedObject.transform.parent.GetComponent() == null) - { - DebugLog.ToConsole($"Warning - Trying to reparent AttachedObject {AttachedObject.name} which wasnt attached to sector!", MessageType.Warning); - } - - AttachedObject.transform.SetParent(newParent, true); - AttachedObject.transform.localScale = Vector3.one; - } - } -} diff --git a/QSB/Syncs/TransformSync/SectoredTransformSync.cs b/QSB/Syncs/TransformSync/SectoredTransformSync.cs deleted file mode 100644 index 7efe7f16..00000000 --- a/QSB/Syncs/TransformSync/SectoredTransformSync.cs +++ /dev/null @@ -1,220 +0,0 @@ -using QSB.Player; -using QSB.SectorSync; -using QSB.SectorSync.WorldObjects; -using QSB.Utility; -using QSB.WorldSync; -using QuantumUNET.Transport; -using UnityEngine; - -namespace QSB.Syncs.TransformSync -{ - public abstract class SectoredTransformSync : BaseTransformSync, ISectoredSync - { - public QSBSector ReferenceSector { get; set; } - public SectorSync.SectorSync SectorSync { get; private set; } - public abstract TargetType Type { get; } - - public override bool IgnoreNullReferenceTransform => true; - public override bool IgnoreDisabledAttachedObject => false; - - private int _sectorIdWaitingSlot = int.MinValue; - - public override void Start() - { - SectorSync = gameObject.AddComponent(); - QSBSectorManager.Instance.SectoredTransformSyncs.Add(this); - base.Start(); - } - - protected override void OnDestroy() - { - base.OnDestroy(); - QSBSectorManager.Instance.SectoredTransformSyncs.Remove(this); - if (SectorSync != null) - { - Destroy(SectorSync); - } - } - - protected override void Init() - { - base.Init(); - if (!QSBSectorManager.Instance.IsReady) - { - return; - } - - if (!HasAuthority) - { - return; - } - - QSBCore.UnityEvents.RunWhen(() => SectorSync.IsReady, InitSector); - } - - private void InitSector() - { - DebugLog.DebugWrite($"InitSector of {_logName}"); - var closestSector = SectorSync.GetClosestSector(AttachedObject.transform); - if (closestSector != null) - { - SetReferenceSector(closestSector); - } - else - { - DebugLog.ToConsole($"Warning - {_logName}'s initial sector was null.", OWML.Common.MessageType.Warning); - } - } - - public override void Update() - { - if (_sectorIdWaitingSlot == int.MinValue) - { - base.Update(); - return; - } - - if (!WorldObjectManager.AllReady) - { - base.Update(); - return; - } - - var sector = _sectorIdWaitingSlot == -1 - ? null - : QSBWorldSync.GetWorldFromId(_sectorIdWaitingSlot); - - if (sector != ReferenceSector) - { - if (sector == null) - { - DebugLog.ToConsole($"Error - {PlayerId}.{GetType().Name} got sector of ID -1.", OWML.Common.MessageType.Error); - base.Update(); - return; - } - - SetReferenceSector(sector); - } - - _sectorIdWaitingSlot = int.MinValue; - - base.Update(); - } - - public override void SerializeTransform(QNetworkWriter writer, bool initialState) - { - if (_intermediaryTransform == null) - { - _intermediaryTransform = new IntermediaryTransform(transform); - } - - if (!QSBPlayerManager.PlayerExists(PlayerId)) - { - writer.Write(-1); - } - else if (!Player.PlayerStates.IsReady) - { - writer.Write(-1); - } - - if (ReferenceSector != null) - { - writer.Write(ReferenceSector.ObjectId); - } - else - { - if (_isInitialized) - { - DebugLog.ToConsole($"Warning - ReferenceSector of {PlayerId}.{GetType().Name} is null.", OWML.Common.MessageType.Warning); - } - writer.Write(-1); - } - - base.SerializeTransform(writer, initialState); - } - - public override void DeserializeTransform(QNetworkReader reader, bool initialState) - { - int sectorId; - if (!QSBCore.WorldObjectsReady) - { - sectorId = reader.ReadInt32(); - if (initialState && sectorId != -1) - { - DebugLog.DebugWrite($"{_logName} set waiting sector id:{sectorId}"); - _sectorIdWaitingSlot = sectorId; - } - reader.ReadVector3(); - DeserializeRotation(reader); - return; - } - - sectorId = reader.ReadInt32(); - var sector = sectorId == -1 - ? null - : QSBWorldSync.GetWorldFromId(sectorId); - - if (sector != ReferenceSector) - { - if (sector == null) - { - DebugLog.ToConsole($"Error - {PlayerId}.{GetType().Name} got sector of ID -1.", OWML.Common.MessageType.Error); - base.DeserializeTransform(reader, initialState); - return; - } - - SetReferenceSector(sector); - } - - base.DeserializeTransform(reader, initialState); - } - - protected override bool UpdateTransform() - { - var referenceNull = ReferenceTransform == null || ReferenceSector == null || _intermediaryTransform.GetReferenceTransform() == null; - var sectorManagerReady = QSBSectorManager.Instance.IsReady; - - if (!sectorManagerReady) - { - if (referenceNull && HasAuthority) - { - DebugLog.ToConsole($"Warning - Reference was null, but sector manager wasn't ready. " + - $"Transform:{ReferenceTransform == null}, Sector:{ReferenceSector == null}, Intermediary:{_intermediaryTransform.GetReferenceTransform() == null}", - OWML.Common.MessageType.Warning); - } - return base.UpdateTransform(); - } - - if (!HasAuthority) - { - return base.UpdateTransform(); - } - - if (referenceNull) - { - var closestSector = SectorSync.GetClosestSector(AttachedObject.transform); - if (closestSector != null) - { - SetReferenceTransform(closestSector.Transform); - } - else - { - if (SectorSync.IsReady) - { - DebugLog.ToConsole($"Error - No closest sector found to {PlayerId}.{GetType().Name}!", OWML.Common.MessageType.Error); - } - - return base.UpdateTransform(); - } - } - - return base.UpdateTransform(); - } - - public void SetReferenceSector(QSBSector sector) - { - ReferenceSector = sector; - SetReferenceTransform(sector?.Transform); - } - } -} diff --git a/QSB/Syncs/TransformSync/UnparentedBaseTransformSync.cs b/QSB/Syncs/TransformSync/UnparentedBaseTransformSync.cs deleted file mode 100644 index dea2b32c..00000000 --- a/QSB/Syncs/TransformSync/UnparentedBaseTransformSync.cs +++ /dev/null @@ -1,144 +0,0 @@ -using OWML.Common; -using QSB.Player.TransformSync; -using QSB.Utility; -using QuantumUNET.Transport; -using System.Linq; -using UnityEngine; - -namespace QSB.Syncs.TransformSync -{ - public abstract class UnparentedBaseTransformSync : SyncBase - { - public override bool IgnoreDisabledAttachedObject => false; - public override bool IgnoreNullReferenceTransform => false; - - public virtual void Start() - { - var lowestBound = Resources.FindObjectsOfTypeAll() - .Where(x => x.NetId.Value <= NetId.Value).OrderBy(x => x.NetId.Value).Last(); - NetIdentity.SetRootIdentity(lowestBound.NetIdentity); - - DontDestroyOnLoad(gameObject); - _intermediaryTransform = new IntermediaryTransform(transform); - QSBSceneManager.OnSceneLoaded += OnSceneLoaded; - } - - protected virtual void OnDestroy() - { - DebugLog.DebugWrite($"OnDestroy {_logName}"); - if (!HasAuthority && AttachedObject != null) - { - Destroy(AttachedObject.gameObject); - } - - QSBSceneManager.OnSceneLoaded -= OnSceneLoaded; - } - - protected void OnSceneLoaded(OWScene oldScene, OWScene newScene, bool isInUniverse) => - _isInitialized = false; - - protected override void Init() - { - AttachedObject = HasAuthority ? InitLocalTransform() : InitRemoteTransform(); - _isInitialized = true; - } - - public override void SerializeTransform(QNetworkWriter writer, bool initialState) - { - if (_intermediaryTransform == null) - { - _intermediaryTransform = new IntermediaryTransform(transform); - } - - var worldPos = _intermediaryTransform.GetPosition(); - var worldRot = _intermediaryTransform.GetRotation(); - writer.Write(worldPos); - SerializeRotation(writer, worldRot); - _prevPosition = worldPos; - _prevRotation = worldRot; - } - - public override void DeserializeTransform(QNetworkReader reader, bool initialState) - { - if (!QSBCore.WorldObjectsReady) - { - reader.ReadVector3(); - DeserializeRotation(reader); - return; - } - - var pos = reader.ReadVector3(); - var rot = DeserializeRotation(reader); - - if (HasAuthority) - { - return; - } - - if (_intermediaryTransform == null) - { - _intermediaryTransform = new IntermediaryTransform(transform); - } - - _intermediaryTransform.SetPosition(pos); - _intermediaryTransform.SetRotation(rot); - - if (_intermediaryTransform.GetPosition() == Vector3.zero) - { - DebugLog.ToConsole($"Warning - {PlayerId}.{GetType().Name} at (0,0,0)! - Given position was {pos}", MessageType.Warning); - } - } - - protected override bool UpdateTransform() - { - if (HasAuthority) - { - _intermediaryTransform.EncodePosition(AttachedObject.transform.position); - _intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation); - return true; - } - - var targetPos = _intermediaryTransform.GetTargetPosition_Unparented(); - var targetRot = _intermediaryTransform.GetTargetRotation_Unparented(); - if (targetPos != Vector3.zero && _intermediaryTransform.GetTargetPosition_ParentedToReference() != Vector3.zero) - { - if (UseInterpolation) - { - AttachedObject.transform.position = SmartSmoothDamp(AttachedObject.transform.position, targetPos); - AttachedObject.transform.rotation = QuaternionHelper.SmoothDamp(AttachedObject.transform.rotation, targetRot, ref _rotationSmoothVelocity, SmoothTime); - } - else - { - AttachedObject.transform.position = targetPos; - AttachedObject.transform.rotation = targetRot; - } - } - - return true; - } - - public override bool HasMoved() - { - var displacementMagnitude = (_intermediaryTransform.GetPosition() - _prevPosition).magnitude; - return displacementMagnitude > 1E-03f - || Quaternion.Angle(_intermediaryTransform.GetRotation(), _prevRotation) > 1E-03f; - } - - public void SetReferenceTransform(Transform transform) - { - if (ReferenceTransform == transform) - { - return; - } - - ReferenceTransform = transform; - _intermediaryTransform.SetReferenceTransform(transform); - - if (HasAuthority) - { - _intermediaryTransform.EncodePosition(AttachedObject.transform.position); - _intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation); - } - } - } -} From 5dc14bd0c30b14c5b6486109a401bc6b0b1d0fd9 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 19 Aug 2021 17:33:35 +0100 Subject: [PATCH 023/118] Update BaseUnsectoredSync.cs --- QSB/Syncs/Unsectored/BaseUnsectoredSync.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/QSB/Syncs/Unsectored/BaseUnsectoredSync.cs b/QSB/Syncs/Unsectored/BaseUnsectoredSync.cs index f4252909..c6dd2f59 100644 --- a/QSB/Syncs/Unsectored/BaseUnsectoredSync.cs +++ b/QSB/Syncs/Unsectored/BaseUnsectoredSync.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using QuantumUNET.Transport; namespace QSB.Syncs.Unsectored { @@ -10,5 +11,13 @@ namespace QSB.Syncs.Unsectored public override bool IgnoreDisabledAttachedObject => false; public override bool IgnoreNullReferenceTransform => false; public override bool ShouldReparentAttachedObject => false; + + public override void SerializeTransform(QNetworkWriter writer, bool initialState) + { + if (_intermediaryTransform == null) + { + _intermediaryTransform = new IntermediaryTransform(transform); + } + } } } From 211bdacf167fcea8c5f74572ae7f7d64ad634f83 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 19 Aug 2021 17:33:52 +0100 Subject: [PATCH 024/118] Update UnsectoredTransformSync.cs --- .../Transforms/UnsectoredTransformSync.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/QSB/Syncs/Unsectored/Transforms/UnsectoredTransformSync.cs b/QSB/Syncs/Unsectored/Transforms/UnsectoredTransformSync.cs index 94454dd6..3a8b15c6 100644 --- a/QSB/Syncs/Unsectored/Transforms/UnsectoredTransformSync.cs +++ b/QSB/Syncs/Unsectored/Transforms/UnsectoredTransformSync.cs @@ -27,8 +27,6 @@ namespace QSB.Syncs.Unsectored.Transforms public override void DeserializeTransform(QNetworkReader reader, bool initialState) { - base.DeserializeTransform(reader, initialState); - if (!QSBCore.WorldObjectsReady) { reader.ReadVector3(); @@ -67,21 +65,25 @@ namespace QSB.Syncs.Unsectored.Transforms return true; } - var targetPos = _intermediaryTransform.GetTargetPosition_ParentedToReference(); - var targetRot = _intermediaryTransform.GetTargetRotation_ParentedToReference(); + var targetPos = _intermediaryTransform.GetTargetPosition_Unparented(); + var targetRot = _intermediaryTransform.GetTargetRotation_Unparented(); if (targetPos != Vector3.zero && _intermediaryTransform.GetTargetPosition_Unparented() != Vector3.zero) { if (UseInterpolation) { - AttachedObject.transform.localPosition = SmartSmoothDamp(AttachedObject.transform.localPosition, targetPos); - AttachedObject.transform.localRotation = QuaternionHelper.SmoothDamp(AttachedObject.transform.localRotation, targetRot, ref _rotationSmoothVelocity, SmoothTime); + AttachedObject.transform.position = SmartSmoothDamp(AttachedObject.transform.position, targetPos); + AttachedObject.transform.rotation = QuaternionHelper.SmoothDamp(AttachedObject.transform.rotation, targetRot, ref _rotationSmoothVelocity, SmoothTime); } else { - AttachedObject.transform.localPosition = targetPos; - AttachedObject.transform.localRotation = targetRot; + AttachedObject.transform.position = targetPos; + AttachedObject.transform.rotation = targetRot; } } + else if (targetPos == Vector3.zero) + { + DebugLog.ToConsole($"Warning - TargetPos for {_logName} was (0,0,0).", MessageType.Warning); + } return true; } From a6cdc10d3c57bc2223f67ba4f6f7320278970321 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 19 Aug 2021 17:34:17 +0100 Subject: [PATCH 025/118] dont change owner if same owner --- QSB/OrbSync/Events/OrbUserEvent.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/QSB/OrbSync/Events/OrbUserEvent.cs b/QSB/OrbSync/Events/OrbUserEvent.cs index c2c95c29..f3ff7046 100644 --- a/QSB/OrbSync/Events/OrbUserEvent.cs +++ b/QSB/OrbSync/Events/OrbUserEvent.cs @@ -84,9 +84,17 @@ namespace QSB.OrbSync.Events return; } - if (orbIdentity.ClientAuthorityOwner != null && orbIdentity.ClientAuthorityOwner != fromPlayer) + var currentOwner = orbIdentity.ClientAuthorityOwner; + var newOwner = fromPlayer; + + if (currentOwner == newOwner) { - orbIdentity.RemoveClientAuthority(orbIdentity.ClientAuthorityOwner); + return; + } + + if (currentOwner != null && currentOwner != fromPlayer) + { + orbIdentity.RemoveClientAuthority(currentOwner); } orbIdentity.AssignClientAuthority(fromPlayer); From bb5c3d272a119032b66697bb2d2ac7d8b56b9ea9 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 19 Aug 2021 17:34:21 +0100 Subject: [PATCH 026/118] Update NomaiOrbTransformSync.cs --- QSB/OrbSync/TransformSync/NomaiOrbTransformSync.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/QSB/OrbSync/TransformSync/NomaiOrbTransformSync.cs b/QSB/OrbSync/TransformSync/NomaiOrbTransformSync.cs index dc9cdfc2..bcc44526 100644 --- a/QSB/OrbSync/TransformSync/NomaiOrbTransformSync.cs +++ b/QSB/OrbSync/TransformSync/NomaiOrbTransformSync.cs @@ -17,7 +17,7 @@ namespace QSB.OrbSync.TransformSync protected override void OnDestroy() { OrbTransformSyncs.Remove(this); - QSBSceneManager.OnSceneLoaded -= OnSceneLoaded; + base.OnDestroy(); } protected override void Init() @@ -59,6 +59,7 @@ namespace QSB.OrbSync.TransformSync protected override Component InitLocalTransform() => GetTransform(); protected override Component InitRemoteTransform() => GetTransform(); + protected override float DistanceLeeway => 1f; public override bool IsReady => QSBCore.WorldObjectsReady; public override bool UseInterpolation => false; } From 2500d8a19227e161936ef74a8f22fca875031774 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 19 Aug 2021 23:14:59 +0100 Subject: [PATCH 027/118] remove old rigidbody sync --- QSB/Syncs/RigidbodySync/BaseRigidbodySync.cs | 250 ------------------ .../RigidbodySync/SectoredRigidbodySync.cs | 120 --------- 2 files changed, 370 deletions(-) delete mode 100644 QSB/Syncs/RigidbodySync/BaseRigidbodySync.cs delete mode 100644 QSB/Syncs/RigidbodySync/SectoredRigidbodySync.cs diff --git a/QSB/Syncs/RigidbodySync/BaseRigidbodySync.cs b/QSB/Syncs/RigidbodySync/BaseRigidbodySync.cs deleted file mode 100644 index 4483341b..00000000 --- a/QSB/Syncs/RigidbodySync/BaseRigidbodySync.cs +++ /dev/null @@ -1,250 +0,0 @@ -using OWML.Common; -using OWML.Utils; -using QSB.Player.TransformSync; -using QSB.Utility; -using QuantumUNET.Transport; -using System.Linq; -using UnityEngine; - -namespace QSB.Syncs.RigidbodySync -{ - public abstract class BaseRigidbodySync : SyncBase - { - protected Vector3 _relativeVelocity; - protected Vector3 _relativeAngularVelocity; - protected Vector3 _prevVelocity; - protected Vector3 _prevAngularVelocity; - - protected abstract OWRigidbody GetRigidbody(); - - public virtual void Start() - { - var lowestBound = Resources.FindObjectsOfTypeAll() - .Where(x => x.NetId.Value <= NetId.Value).OrderBy(x => x.NetId.Value).Last(); - NetIdentity.SetRootIdentity(lowestBound.NetIdentity); - - DontDestroyOnLoad(gameObject); - _intermediaryTransform = new IntermediaryTransform(transform); - QSBSceneManager.OnSceneLoaded += OnSceneLoaded; - } - - protected virtual void OnDestroy() => QSBSceneManager.OnSceneLoaded -= OnSceneLoaded; - - private void OnSceneLoaded(OWScene oldScene, OWScene newScene, bool isInUniverse) - => _isInitialized = false; - - protected override void Init() - { - if (!QSBSceneManager.IsInUniverse) - { - DebugLog.ToConsole($"Error - {_logName} is being init-ed when not in the universe!", MessageType.Error); - } - - AttachedObject = GetRigidbody(); - _isInitialized = true; - } - - public override void SerializeTransform(QNetworkWriter writer, bool initialState) - { - if (_intermediaryTransform == null) - { - _intermediaryTransform = new IntermediaryTransform(transform); - } - - /* We need to send : - * - Position - * - Rotation - * - Velocity - * - Angular velocity - * We can't store the last two on the IntermediaryTransform, so they come from fields. - */ - - var worldPos = _intermediaryTransform.GetPosition(); - var worldRot = _intermediaryTransform.GetRotation(); - var relativeVelocity = _relativeVelocity; - var relativeAngularVelocity = _relativeAngularVelocity; - - writer.Write(worldPos); - SerializeRotation(writer, worldRot); - writer.Write(relativeVelocity); - writer.Write(relativeAngularVelocity); - - _prevPosition = worldPos; - _prevRotation = worldRot; - _prevVelocity = relativeVelocity; - _prevAngularVelocity = relativeAngularVelocity; - } - - public override void DeserializeTransform(QNetworkReader reader, bool initialState) - { - if (!QSBCore.WorldObjectsReady) - { - reader.ReadVector3(); - DeserializeRotation(reader); - reader.ReadVector3(); - reader.ReadVector3(); - return; - } - - var pos = reader.ReadVector3(); - var rot = DeserializeRotation(reader); - var relativeVelocity = reader.ReadVector3(); - var relativeAngularVelocity = reader.ReadVector3(); - - if (HasAuthority) - { - return; - } - - if (_intermediaryTransform == null) - { - _intermediaryTransform = new IntermediaryTransform(transform); - } - - _intermediaryTransform.SetPosition(pos); - _intermediaryTransform.SetRotation(rot); - _relativeVelocity = relativeVelocity; - _relativeAngularVelocity = relativeAngularVelocity; - - if (_intermediaryTransform.GetPosition() == Vector3.zero) - { - DebugLog.ToConsole($"Warning - {_logName} at (0,0,0)! - Given position was {pos}", MessageType.Warning); - } - } - - protected override bool UpdateTransform() - { - if (HasAuthority) - { - _intermediaryTransform.EncodePosition(AttachedObject.transform.position); - _intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation); - _relativeVelocity = GetRelativeVelocity(); - _relativeAngularVelocity = (AttachedObject as OWRigidbody).GetRelativeAngularVelocity(ReferenceTransform.GetAttachedOWRigidbody()); - return true; - } - - var targetPos = _intermediaryTransform.GetTargetPosition_Unparented(); - var targetRot = _intermediaryTransform.GetTargetRotation_Unparented(); - - if (targetPos == Vector3.zero || _intermediaryTransform.GetTargetPosition_ParentedToReference() == Vector3.zero) - { - return false; - } - - if (UseInterpolation) - { - (AttachedObject as OWRigidbody).SetPosition(SmartSmoothDamp(AttachedObject.transform.position, targetPos)); - (AttachedObject as OWRigidbody).SetRotation(QuaternionHelper.SmoothDamp(AttachedObject.transform.rotation, targetRot, ref _rotationSmoothVelocity, SmoothTime)); - } - else - { - (AttachedObject as OWRigidbody).SetPosition(targetPos); - (AttachedObject as OWRigidbody).SetRotation(targetRot); - } - - var currentVelocity = GetRelativeVelocity(); - var targetVelocity = ReferenceTransform.GetAttachedOWRigidbody().GetPointVelocity(targetPos) + _relativeVelocity; - var adjustedTarget = targetVelocity + Locator.GetCenterOfTheUniverse().GetStaticFrameWorldVelocity(); - - SetVelocity((AttachedObject as OWRigidbody), targetVelocity); - (AttachedObject as OWRigidbody).SetAngularVelocity(ReferenceTransform.GetAttachedOWRigidbody().GetAngularVelocity() + _relativeAngularVelocity); - - return true; - } - - private void SetVelocity(OWRigidbody rigidbody, Vector3 relativeVelocity) - { - var isRunningKinematic = rigidbody.RunningKinematicSimulation(); - var currentVelocity = rigidbody.GetValue("_currentVelocity"); - - if (isRunningKinematic) - { - var kinematicRigidbody = rigidbody.GetValue("_kinematicRigidbody"); - kinematicRigidbody.velocity = relativeVelocity + Locator.GetCenterOfTheUniverse().GetStaticFrameWorldVelocity(); - } - else - { - var normalRigidbody = rigidbody.GetValue("_rigidbody"); - normalRigidbody.velocity = relativeVelocity + Locator.GetCenterOfTheUniverse().GetStaticFrameWorldVelocity(); - } - - rigidbody.SetValue("_lastVelocity", currentVelocity); - rigidbody.SetValue("_currentVelocity", relativeVelocity); - } - - public void SetReferenceTransform(Transform transform) - { - if (ReferenceTransform == transform) - { - return; - } - - ReferenceTransform = transform; - _intermediaryTransform.SetReferenceTransform(transform); - - if (HasAuthority) - { - _intermediaryTransform.EncodePosition(AttachedObject.transform.position); - _intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation); - } - } - - // TODO : optimize by using sqrMagnitude - public override bool HasMoved() - { - var displacementMagnitude = (_intermediaryTransform.GetPosition() - _prevPosition).magnitude; - - if (displacementMagnitude > 1E-03f) - { - return true; - } - - if (Quaternion.Angle(_intermediaryTransform.GetRotation(), _prevRotation) > 1E-03f) - { - return true; - } - - var velocityChangeMagnitude = (_relativeVelocity - _prevVelocity).magnitude; - var angularVelocityChangeMagnitude = (_relativeAngularVelocity - _prevAngularVelocity).magnitude; - if (velocityChangeMagnitude > 1E-03f) - { - return true; - } - - if (angularVelocityChangeMagnitude > 1E-03f) - { - return true; - } - - return false; - } - - public float GetVelocityChangeMagnitude() - => (_relativeVelocity - _prevVelocity).magnitude; - - public Vector3 GetRelativeVelocity() - { - if (AttachedObject == null) - { - DebugLog.ToConsole($"Error - Trying to get relative velocity when AttachedObject is null.", MessageType.Error); - return Vector3.zero; - } - - if (ReferenceTransform == null) - { - DebugLog.ToConsole($"Error - Trying to get relative velocity when ReferenceTransform is null. ({AttachedObject.name})", MessageType.Error); - return Vector3.zero; - } - - var attachedRigid = ReferenceTransform.GetAttachedOWRigidbody(); - if (attachedRigid == null) - { - DebugLog.ToConsole($"Error - ReferenceTransform ({ReferenceTransform.name}) on {AttachedObject.name} has no attached OWRigidBody.", MessageType.Error); - return Vector3.zero; - } - - var pointVelocity = attachedRigid.GetPointVelocity(AttachedObject.transform.position); - return (AttachedObject as OWRigidbody).GetVelocity() - pointVelocity; - } - } -} \ No newline at end of file diff --git a/QSB/Syncs/RigidbodySync/SectoredRigidbodySync.cs b/QSB/Syncs/RigidbodySync/SectoredRigidbodySync.cs deleted file mode 100644 index 666e5f88..00000000 --- a/QSB/Syncs/RigidbodySync/SectoredRigidbodySync.cs +++ /dev/null @@ -1,120 +0,0 @@ -using QSB.SectorSync; -using QSB.SectorSync.WorldObjects; -using QSB.WorldSync; -using QuantumUNET.Transport; - -namespace QSB.Syncs.RigidbodySync -{ - public abstract class SectoredRigidbodySync : BaseRigidbodySync, ISectoredSync - { - public QSBSector ReferenceSector { get; set; } - public SectorSync.SectorSync SectorSync { get; private set; } - public abstract TargetType Type { get; } - - public override bool IgnoreDisabledAttachedObject => false; - public override bool IgnoreNullReferenceTransform => true; - - public override void Start() - { - SectorSync = gameObject.AddComponent(); - QSBSectorManager.Instance.SectoredRigidbodySyncs.Add(this); - base.Start(); - } - - protected override void OnDestroy() - { - base.OnDestroy(); - QSBSectorManager.Instance.SectoredRigidbodySyncs.Remove(this); - if (SectorSync != null) - { - Destroy(SectorSync); - } - } - - protected override void Init() - { - base.Init(); - if (!QSBSectorManager.Instance.IsReady) - { - return; - } - - if (!HasAuthority) - { - return; - } - - var closestSector = SectorSync.GetClosestSector(AttachedObject.transform); - if (closestSector != null) - { - SetReferenceTransform(closestSector.Transform); - } - } - - public override void SerializeTransform(QNetworkWriter writer, bool initialState) - { - if (_intermediaryTransform == null) - { - _intermediaryTransform = new IntermediaryTransform(transform); - } - - if (ReferenceSector != null) - { - writer.Write(ReferenceSector.ObjectId); - } - else - { - writer.Write(-1); - } - - base.SerializeTransform(writer, initialState); - } - - public override void DeserializeTransform(QNetworkReader reader, bool initialState) - { - if (!QSBCore.WorldObjectsReady) - { - reader.ReadInt32(); - reader.ReadVector3(); - DeserializeRotation(reader); - return; - } - - var sectorId = reader.ReadInt32(); - var sector = sectorId == -1 - ? null - : QSBWorldSync.GetWorldFromId(sectorId); - - if (sector != ReferenceSector) - { - SetReferenceSector(sector); - } - - base.DeserializeTransform(reader, initialState); - } - - protected override bool UpdateTransform() - { - if ((ReferenceTransform == null || ReferenceSector == null || _intermediaryTransform.GetReferenceTransform() == null) && QSBSectorManager.Instance.IsReady && HasAuthority) - { - var closestSector = SectorSync.GetClosestSector(AttachedObject.transform); - if (closestSector != null) - { - SetReferenceTransform(closestSector.Transform); - } - else - { - return false; - } - } - - return base.UpdateTransform(); - } - - public void SetReferenceSector(QSBSector sector) - { - ReferenceSector = sector; - SetReferenceTransform(sector?.Transform); - } - } -} \ No newline at end of file From bc41702ac2a409705146b3aece7fe8286a08da01 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 19 Aug 2021 23:15:20 +0100 Subject: [PATCH 028/118] add IsPlayerInShip to ShipManager --- QSB/ShipSync/ShipManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/QSB/ShipSync/ShipManager.cs b/QSB/ShipSync/ShipManager.cs index 1052fbd4..40ab8bed 100644 --- a/QSB/ShipSync/ShipManager.cs +++ b/QSB/ShipSync/ShipManager.cs @@ -107,6 +107,9 @@ namespace QSB.ShipSync UpdateElectricalComponent(); } + public bool IsPlayerInShip(PlayerInfo player) + => _playersInShip.Contains(player); + private void UpdateElectricalComponent() { var electricalSystem = ShipElectricalComponent.GetValue("_electricalSystem"); From 222c4047a586b5335b0bf175ef33189c30341763 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 21 Aug 2021 19:52:35 +0100 Subject: [PATCH 029/118] rework patches --- QSB/Patches/QSBPatch.cs | 49 ++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/QSB/Patches/QSBPatch.cs b/QSB/Patches/QSBPatch.cs index d3453bc9..e2fc7889 100644 --- a/QSB/Patches/QSBPatch.cs +++ b/QSB/Patches/QSBPatch.cs @@ -35,15 +35,15 @@ namespace QSB.Patches _patchedMethods.Add(method); } - public void Prefix(string patchName) - => DoPrefixPostfix(true, patchName); + public void Prefix(string patchName, params Type[] args) + => DoPrefixPostfix(true, patchName, args); - public void Postfix(string patchName) - => DoPrefixPostfix(false, patchName); + public void Postfix(string patchName, params Type[] args) + => DoPrefixPostfix(false, patchName, args); - private void DoPrefixPostfix(bool isPrefix, string patchName) + private void DoPrefixPostfix(bool isPrefix, string patchName, params Type[] args) { - var method = GetMethodInfo(patchName); + var method = GetMethodInfo(patchName, args); if (method != null) { @@ -62,7 +62,7 @@ namespace QSB.Patches //DebugLog.DebugWrite($"{(isPrefix ? "[Prefix]" : "[Postfix]")} {patchName}", method == null ? MessageType.Error : MessageType.Success); } - private MethodInfo GetMethodInfo(string patchName) + private MethodInfo GetMethodInfo(string patchName, params Type[] args) { var splitName = patchName.Split('_'); var typeName = splitName[0]; @@ -75,14 +75,41 @@ namespace QSB.Patches return null; } - var method = type.GetAnyMethod(methodName); - if (method == null) + var allMethodsOfName = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy).Where(x => x.Name == methodName); + + if (allMethodsOfName.Count() == 0) { - DebugLog.ToConsole($"Error - Couldn't find method for patch name {patchName}!", MessageType.Error); + DebugLog.ToConsole($"Error - Could not find method {methodName} in type {typeName}.", MessageType.Error); return null; } - return method; + if (allMethodsOfName.Count() == 1) + { + return allMethodsOfName.First(); + } + + DebugLog.DebugWrite($"More than one method found with name {methodName} in type {typeName}"); + + foreach (var method in allMethodsOfName) + { + DebugLog.DebugWrite($"checking {method.Name}"); + var paramList = method.GetParameters().Select(x => x.ParameterType); + if (Enumerable.SequenceEqual(args, paramList)) + { + DebugLog.DebugWrite($"match!"); + return method; + } + } + + DebugLog.DebugWrite($"nothing found"); + + DebugLog.ToConsole($"Error - Could not find method {methodName} in type {typeName} with parameter list of {string.Join(", ", args.Select(x => x.FullName).ToArray())}", MessageType.Error); + foreach (var method in allMethodsOfName) + { + var paramList = method.GetParameters().Select(x => x.ParameterType); + DebugLog.ToConsole($"- Found {method.Name}, but with params {string.Join(", ", paramList.Select(x => x.FullName).ToArray())}", MessageType.Error); + } + return null; } private Type GetFirstTypeByName(string typeName) From fc474646881f0929c4def3c214c67fd0f497cfe6 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 21 Aug 2021 19:53:37 +0100 Subject: [PATCH 030/118] change input to sectorsync init from actual sync to just the targettype --- QSB/Player/TransformSync/PlayerTransformSync.cs | 4 +--- QSB/ProbeSync/TransformSync/PlayerProbeSync.cs | 3 +-- QSB/SectorSync/SectorSync.cs | 4 ++-- QSB/Syncs/Sectored/BaseSectoredSync.cs | 1 - 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/QSB/Player/TransformSync/PlayerTransformSync.cs b/QSB/Player/TransformSync/PlayerTransformSync.cs index d748cc99..38cb4f9e 100644 --- a/QSB/Player/TransformSync/PlayerTransformSync.cs +++ b/QSB/Player/TransformSync/PlayerTransformSync.cs @@ -86,7 +86,7 @@ namespace QSB.Player.TransformSync protected override Component InitLocalTransform() { - QSBCore.UnityEvents.RunWhen(() => WorldObjectManager.AllReady, () => SectorSync.Init(Locator.GetPlayerSectorDetector(), this)); + QSBCore.UnityEvents.RunWhen(() => WorldObjectManager.AllReady, () => SectorSync.Init(Locator.GetPlayerSectorDetector(), TargetType.Player)); // player body var player = Locator.GetPlayerTransform(); @@ -275,7 +275,5 @@ namespace QSB.Player.TransformSync public static PlayerTransformSync LocalInstance { get; private set; } public override bool UseInterpolation => true; - - public override TargetType Type => TargetType.Player; } } \ No newline at end of file diff --git a/QSB/ProbeSync/TransformSync/PlayerProbeSync.cs b/QSB/ProbeSync/TransformSync/PlayerProbeSync.cs index dbc7dba6..6ef2cc07 100644 --- a/QSB/ProbeSync/TransformSync/PlayerProbeSync.cs +++ b/QSB/ProbeSync/TransformSync/PlayerProbeSync.cs @@ -14,7 +14,6 @@ namespace QSB.ProbeSync.TransformSync { protected override float DistanceLeeway => 10f; public override bool UseInterpolation => true; - public override TargetType Type => TargetType.Probe; public override bool IgnoreDisabledAttachedObject => true; public static PlayerProbeSync LocalInstance { get; private set; } @@ -23,7 +22,7 @@ namespace QSB.ProbeSync.TransformSync protected override Component InitLocalTransform() { - QSBCore.UnityEvents.RunWhen(() => WorldObjectManager.AllReady, () => SectorSync.Init(Locator.GetProbe().GetSectorDetector(), this)); + QSBCore.UnityEvents.RunWhen(() => WorldObjectManager.AllReady, () => SectorSync.Init(Locator.GetProbe().GetSectorDetector(), TargetType.Probe)); var body = Locator.GetProbe().transform; Player.ProbeBody = body.gameObject; diff --git a/QSB/SectorSync/SectorSync.cs b/QSB/SectorSync/SectorSync.cs index b1d352fd..8d052213 100644 --- a/QSB/SectorSync/SectorSync.cs +++ b/QSB/SectorSync/SectorSync.cs @@ -31,7 +31,7 @@ namespace QSB.SectorSync IsReady = false; } - public void Init(SectorDetector detector, BaseSectoredSync sectoredSync) + public void Init(SectorDetector detector, TargetType type) { DebugLog.DebugWrite($"INIT SECTOR SYNC detector:{detector.name}"); if (_sectorDetector != null) @@ -58,7 +58,7 @@ namespace QSB.SectorSync PopulateSectorList(); - _targetType = sectoredSync.Type; + _targetType = type; IsReady = true; } diff --git a/QSB/Syncs/Sectored/BaseSectoredSync.cs b/QSB/Syncs/Sectored/BaseSectoredSync.cs index 62a649ad..5f1aa5e4 100644 --- a/QSB/Syncs/Sectored/BaseSectoredSync.cs +++ b/QSB/Syncs/Sectored/BaseSectoredSync.cs @@ -14,7 +14,6 @@ namespace QSB.Syncs.Sectored public QSBSector ReferenceSector { get; set; } public SectorSync.SectorSync SectorSync { get; private set; } - public abstract TargetType Type { get; } private int _sectorIdWaitingSlot = int.MinValue; From 0d46a7e07d9df190a5a715cbbe602a1c3f865a9b Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sun, 22 Aug 2021 16:40:50 +0100 Subject: [PATCH 031/118] change some VS tasks --- QSB/Animation/NPC/CharacterAnimManager.cs | 2 +- QSB/Animation/NPC/Patches/CharacterAnimationPatches.cs | 4 ++-- QSB/ConversationSync/ConversationManager.cs | 2 +- QSB/Player/PlayerInfo.cs | 4 ++-- QSB/PoolSync/CustomNomaiRemoteCameraPlatform.cs | 2 +- QSB/QuantumSync/Patches/QuantumPatches.cs | 2 +- QSB/QuantumSync/Patches/QuantumVisibilityPatches.cs | 4 ++-- QSB/Tools/PlayerToolsManager.cs | 2 +- QSB/Tools/ProbeLauncherTool/Patches/LauncherPatches.cs | 1 + QSB/Tools/ProbeLauncherTool/QSBProbeLauncherTool.cs | 2 +- 10 files changed, 13 insertions(+), 12 deletions(-) diff --git a/QSB/Animation/NPC/CharacterAnimManager.cs b/QSB/Animation/NPC/CharacterAnimManager.cs index bd5c7210..59a60c08 100644 --- a/QSB/Animation/NPC/CharacterAnimManager.cs +++ b/QSB/Animation/NPC/CharacterAnimManager.cs @@ -12,7 +12,7 @@ namespace QSB.Animation.NPC QSBWorldSync.Init(); QSBWorldSync.Init(); - //TODO : this is the wrong place to put this... move it to Conversations? + //MOVE : this is the wrong place to put this... move it to Conversations? QSBWorldSync.OldDialogueTrees.Clear(); QSBWorldSync.OldDialogueTrees = Resources.FindObjectsOfTypeAll().ToList(); } diff --git a/QSB/Animation/NPC/Patches/CharacterAnimationPatches.cs b/QSB/Animation/NPC/Patches/CharacterAnimationPatches.cs index 2bf8d561..cddf6461 100644 --- a/QSB/Animation/NPC/Patches/CharacterAnimationPatches.cs +++ b/QSB/Animation/NPC/Patches/CharacterAnimationPatches.cs @@ -46,7 +46,7 @@ namespace QSB.Animation.NPC.Patches var playerId = ConversationManager.Instance.GetPlayerTalkingToTree(____dialogueTree); var player = QSBPlayerManager.GetPlayer(playerId); - var qsbObj = QSBWorldSync.GetWorldFromUnity(__instance); // TODO : maybe cache this somewhere... or assess how slow this is + var qsbObj = QSBWorldSync.GetWorldFromUnity(__instance); // OPTIMIZE : maybe cache this somewhere... or assess how slow this is PlayerInfo playerToUse = null; if (____inConversation) @@ -63,7 +63,7 @@ namespace QSB.Animation.NPC.Patches : player; } } - else if (!___lookOnlyWhenTalking && qsbObj.GetPlayersInHeadZone().Count != 0) // TODO : maybe this would be more fun if characters looked between players at random times? :P + else if (!___lookOnlyWhenTalking && qsbObj.GetPlayersInHeadZone().Count != 0) // IDEA : maybe this would be more fun if characters looked between players at random times? :P { playerToUse = QSBPlayerManager.GetClosestPlayerToWorldPoint(qsbObj.GetPlayersInHeadZone(), __instance.transform.position); } diff --git a/QSB/ConversationSync/ConversationManager.cs b/QSB/ConversationSync/ConversationManager.cs index fc188d46..bd00586f 100644 --- a/QSB/ConversationSync/ConversationManager.cs +++ b/QSB/ConversationSync/ConversationManager.cs @@ -23,7 +23,7 @@ namespace QSB.ConversationSync Instance = this; _boxPrefab = QSBCore.ConversationAssetBundle.LoadAsset("assets/dialoguebubble.prefab"); - // TODO : make dynamic so it can be different sizes! + // BUG : make dynamic so it can be different sizes! // the dynamic font seems to be super lo-res at this size...? var font = (Font)Resources.Load(@"fonts\english - latin\spacemono-bold"); if (font == null) diff --git a/QSB/Player/PlayerInfo.cs b/QSB/Player/PlayerInfo.cs index 02586ece..2f5bfd88 100644 --- a/QSB/Player/PlayerInfo.cs +++ b/QSB/Player/PlayerInfo.cs @@ -66,8 +66,8 @@ namespace QSB.Player public JetpackAccelerationSync JetpackAcceleration { get; set; } // Misc - public bool IsInMoon; // TODO : move into PlayerStates? - public bool IsInShrine; // TODO : move into PlayerStates? + public bool IsInMoon; // MOVE : move into PlayerStates? + public bool IsInShrine; // MOVE : move into PlayerStates? public IQSBQuantumObject EntangledObject; public bool IsDead { get; set; } public ClientState State { get; set; } diff --git a/QSB/PoolSync/CustomNomaiRemoteCameraPlatform.cs b/QSB/PoolSync/CustomNomaiRemoteCameraPlatform.cs index 5fdab7a6..d1116c38 100644 --- a/QSB/PoolSync/CustomNomaiRemoteCameraPlatform.cs +++ b/QSB/PoolSync/CustomNomaiRemoteCameraPlatform.cs @@ -719,7 +719,7 @@ namespace QSB.PoolSync hologramCopy.GetChild(0).Find("player_mesh_noSuit:Traveller_HEA_Player/player_mesh_noSuit:Player_Head").gameObject.layer = 0; hologramCopy.GetChild(0).Find("Traveller_Mesh_v01:Traveller_Geo/Traveller_Mesh_v01:PlayerSuit_Helmet").gameObject.layer = 0; - // TODO : Look at this again... probably need to sync head rotation to something else + // BUG : Look at this again... probably need to sync head rotation to something else //var ikSync = hologramCopy.GetChild(0).gameObject.AddComponent(); //ikSync.Init(player.CameraBody.transform); diff --git a/QSB/QuantumSync/Patches/QuantumPatches.cs b/QSB/QuantumSync/Patches/QuantumPatches.cs index 2033c6e8..a0f2152c 100644 --- a/QSB/QuantumSync/Patches/QuantumPatches.cs +++ b/QSB/QuantumSync/Patches/QuantumPatches.cs @@ -344,7 +344,7 @@ namespace QSB.QuantumSync.Patches return false; } - // TODO : make this *really* check for all players - check other probes and other jetpacks! + // BUG : make this *really* check for all players - check other probes and other jetpacks! __result = ____gate.GetOpenFraction() == 0f && !____isProbeInside && Locator.GetThrusterLightTracker().GetLightRange() <= 0f; diff --git a/QSB/QuantumSync/Patches/QuantumVisibilityPatches.cs b/QSB/QuantumSync/Patches/QuantumVisibilityPatches.cs index d947849b..6fab80da 100644 --- a/QSB/QuantumSync/Patches/QuantumVisibilityPatches.cs +++ b/QSB/QuantumSync/Patches/QuantumVisibilityPatches.cs @@ -83,14 +83,14 @@ namespace QSB.QuantumSync.Patches return false; } - // TODO : Implement checking for other probes! + // BUG : Implement checking for other probes! if (Locator.GetProbe() != null && Locator.GetProbe().IsLaunched() && Locator.GetProbe().CheckIlluminationAtPoint(point, ____illuminationRadius)) { __result = true; return false; } - // TODO : Implement checking for other player's thrusters! + // BUG : Implement checking for other player's thrusters! if (Locator.GetThrusterLightTracker().CheckIlluminationAtPoint(point, ____illuminationRadius)) { __result = true; diff --git a/QSB/Tools/PlayerToolsManager.cs b/QSB/Tools/PlayerToolsManager.cs index 6ee0dd28..4c0ea2f4 100644 --- a/QSB/Tools/PlayerToolsManager.cs +++ b/QSB/Tools/PlayerToolsManager.cs @@ -37,7 +37,7 @@ namespace QSB.Tools { Props_HEA_Lightbulb_mat = GameObject.Find("lantern_lamp").GetComponent().materials[0]; - // TODO : uhhhhh fuckin' uhhhhhhhh (find a material) + // BUG : uhhhhh fuckin' uhhhhhhhh (find a material) Props_HEA_Lightbulb_OFF_mat = null; } diff --git a/QSB/Tools/ProbeLauncherTool/Patches/LauncherPatches.cs b/QSB/Tools/ProbeLauncherTool/Patches/LauncherPatches.cs index 6cf988f7..619931a0 100644 --- a/QSB/Tools/ProbeLauncherTool/Patches/LauncherPatches.cs +++ b/QSB/Tools/ProbeLauncherTool/Patches/LauncherPatches.cs @@ -70,6 +70,7 @@ namespace QSB.Tools.ProbeLauncherTool.Patches return false; } + // BUG : This plays the sound to everyone // TODO : ehhhh idk about this. maybe copy each sound source so we have a 2d version (for local) and a 3d version (for remote)? // this would probably be a whole qsb version on it's own diff --git a/QSB/Tools/ProbeLauncherTool/QSBProbeLauncherTool.cs b/QSB/Tools/ProbeLauncherTool/QSBProbeLauncherTool.cs index 5933f90a..f154dd02 100644 --- a/QSB/Tools/ProbeLauncherTool/QSBProbeLauncherTool.cs +++ b/QSB/Tools/ProbeLauncherTool/QSBProbeLauncherTool.cs @@ -24,7 +24,7 @@ namespace QSB.Tools.ProbeLauncherTool // TODO : make this do underwater stuff correctly Effects.PlayLaunchClip(false); - // TODO : this plays particles on everyone's launcher... + // BUG : this plays particles on everyone's launcher... Effects.PlayLaunchParticles(false); } } From 776eadb3d96c1678649ad0fcf96504b15ca2a517 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sun, 22 Aug 2021 16:42:43 +0100 Subject: [PATCH 032/118] add rigidbody sync (sectored and unsectored) --- .../Rigidbodies/SectoredRigidbodySync.cs | 267 ++++++++++++++++++ .../Rigidbodies/UnsectoredRigidbodySync.cs | 16 ++ 2 files changed, 283 insertions(+) create mode 100644 QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs create mode 100644 QSB/Syncs/Unsectored/Rigidbodies/UnsectoredRigidbodySync.cs diff --git a/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs b/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs new file mode 100644 index 00000000..7c441405 --- /dev/null +++ b/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs @@ -0,0 +1,267 @@ +using OWML.Common; +using OWML.Utils; +using QSB.ShipSync; +using QSB.Utility; +using QuantumUNET.Transport; +using UnityEngine; + +namespace QSB.Syncs.Sectored.Rigidbodies +{ + public abstract class SectoredRigidbodySync : BaseSectoredSync + { + public override bool ShouldReparentAttachedObject => false; + + public const float PositionMovedThreshold = 0.05f; + public const float AngleRotatedThreshold = 0.05f; + public const float VelocityChangeThreshold = 0.05f; + public const float AngVelocityChangeThreshold = 0.05f; + + protected Vector3 _relativeVelocity; + protected Vector3 _relativeAngularVelocity; + protected Vector3 _prevVelocity; + protected Vector3 _prevAngularVelocity; + + /// + /// The previous position of the VISIBLE object, as if parented to the reference. + /// + protected Vector3 _localPrevPosition; + + /// + /// The previous rotation of the VISIBLE object, as if parented to the reference. + /// + protected Quaternion _localPrevRotation; + + protected Vector3 _localPrevVelocity; + protected Vector3 _localPrevAngularVelocity; + + protected abstract OWRigidbody GetRigidbody(); + + protected override Component SetAttachedObject() + => GetRigidbody(); + + public override void SerializeTransform(QNetworkWriter writer, bool initialState) + { + base.SerializeTransform(writer, initialState); + + var worldPos = _intermediaryTransform.GetPosition(); + var worldRot = _intermediaryTransform.GetRotation(); + var relativeVelocity = _relativeVelocity; + var relativeAngularVelocity = _relativeAngularVelocity; + + writer.Write(worldPos); + SerializeRotation(writer, worldRot); + writer.Write(relativeVelocity); + writer.Write(relativeAngularVelocity); + + _prevPosition = worldPos; + _prevRotation = worldRot; + _prevVelocity = relativeVelocity; + _prevAngularVelocity = relativeAngularVelocity; + } + + public override void DeserializeTransform(QNetworkReader reader, bool initialState) + { + base.DeserializeTransform(reader, initialState); + + if (!QSBCore.WorldObjectsReady) + { + reader.ReadVector3(); + DeserializeRotation(reader); + reader.ReadVector3(); + reader.ReadVector3(); + return; + } + + var pos = reader.ReadVector3(); + var rot = DeserializeRotation(reader); + var relativeVelocity = reader.ReadVector3(); + var relativeAngularVelocity = reader.ReadVector3(); + + if (HasAuthority) + { + return; + } + + if (_intermediaryTransform == null) + { + _intermediaryTransform = new IntermediaryTransform(transform); + } + + _intermediaryTransform.SetPosition(pos); + _intermediaryTransform.SetRotation(rot); + _relativeVelocity = relativeVelocity; + _relativeAngularVelocity = relativeAngularVelocity; + + if (_intermediaryTransform.GetPosition() == Vector3.zero) + { + DebugLog.ToConsole($"Warning - {_logName} at (0,0,0)! - Given position was {pos}", MessageType.Warning); + } + } + + protected override bool UpdateTransform() + { + if (!base.UpdateTransform()) + { + return false; + } + + if (HasAuthority) + { + _intermediaryTransform.EncodePosition(AttachedObject.transform.position); + _intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation); + _relativeVelocity = GetRelativeVelocity(); + _relativeAngularVelocity = (AttachedObject as OWRigidbody).GetRelativeAngularVelocity(ReferenceTransform.GetAttachedOWRigidbody()); + return true; + } + + var targetPos = _intermediaryTransform.GetTargetPosition_Unparented(); + var targetRot = _intermediaryTransform.GetTargetRotation_Unparented(); + + if (targetPos == Vector3.zero || _intermediaryTransform.GetTargetPosition_ParentedToReference() == Vector3.zero) + { + return false; + } + + Vector3 positionToSet = targetPos; + Quaternion rotationToSet = targetRot; + + if (UseInterpolation) + { + positionToSet = SmartSmoothDamp(AttachedObject.transform.position, targetPos); + rotationToSet = QuaternionHelper.SmoothDamp(AttachedObject.transform.rotation, targetRot, ref _rotationSmoothVelocity, SmoothTime); + } + + var hasMoved = CustomHasMoved( + _intermediaryTransform.GetTargetPosition_ParentedToReference(), + _localPrevPosition, + _intermediaryTransform.GetTargetRotation_ParentedToReference(), + _localPrevRotation, + _relativeVelocity, + _localPrevVelocity, + _relativeAngularVelocity, + _localPrevAngularVelocity); + + _localPrevPosition = _intermediaryTransform.GetTargetPosition_ParentedToReference(); + _localPrevRotation = _intermediaryTransform.GetTargetRotation_ParentedToReference(); + _localPrevVelocity = _relativeVelocity; + _localPrevAngularVelocity = _relativeAngularVelocity; + + if (!hasMoved) + { + return true; + } + + //(AttachedObject as OWRigidbody).SetPosition(positionToSet); + //(AttachedObject as OWRigidbody).SetRotation(rotationToSet); + + (AttachedObject as OWRigidbody).MoveToPosition(positionToSet); + (AttachedObject as OWRigidbody).MoveToRotation(rotationToSet); + + var targetVelocity = ReferenceTransform.GetAttachedOWRigidbody().GetPointVelocity(targetPos) + _relativeVelocity; + var targetAngularVelocity = ReferenceTransform.GetAttachedOWRigidbody().GetAngularVelocity() + _relativeAngularVelocity; + + SetVelocity(AttachedObject as OWRigidbody, targetVelocity); + (AttachedObject as OWRigidbody).SetAngularVelocity(targetAngularVelocity); + + return true; + } + + public override bool HasMoved() + => CustomHasMoved( + _intermediaryTransform.GetPosition(), + _prevPosition, + _intermediaryTransform.GetRotation(), + _prevRotation, + _relativeVelocity, + _prevVelocity, + _relativeAngularVelocity, + _prevAngularVelocity); + + // OPTIMIZE : optimize by using sqrMagnitude + private bool CustomHasMoved( + Vector3 newPosition, + Vector3 prevPosition, + Quaternion newRotation, + Quaternion prevRotation, + Vector3 newVelocity, + Vector3 prevVelocity, + Vector3 newAngVelocity, + Vector3 prevAngVelocity) + { + var displacementMagnitude = (newPosition - prevPosition).magnitude; + + if (displacementMagnitude > PositionMovedThreshold) + { + return true; + } + + if (Quaternion.Angle(newRotation, prevRotation) > AngleRotatedThreshold) + { + return true; + } + + var velocityChangeMagnitude = (newVelocity - prevVelocity).magnitude; + var angularVelocityChangeMagnitude = (newAngVelocity - prevAngVelocity).magnitude; + if (velocityChangeMagnitude > VelocityChangeThreshold) + { + return true; + } + + if (angularVelocityChangeMagnitude > AngVelocityChangeThreshold) + { + return true; + } + + return false; + } + + // TODO : why? isn't owrigidbody.setvelocity the same...? :P + protected void SetVelocity(OWRigidbody rigidbody, Vector3 relativeVelocity) + { + var isRunningKinematic = rigidbody.RunningKinematicSimulation(); + var currentVelocity = rigidbody.GetValue("_currentVelocity"); + + if (isRunningKinematic) + { + var kinematicRigidbody = rigidbody.GetValue("_kinematicRigidbody"); + kinematicRigidbody.velocity = relativeVelocity + Locator.GetCenterOfTheUniverse().GetStaticFrameWorldVelocity(); + } + else + { + var normalRigidbody = rigidbody.GetValue("_rigidbody"); + normalRigidbody.velocity = relativeVelocity + Locator.GetCenterOfTheUniverse().GetStaticFrameWorldVelocity(); + } + + rigidbody.SetValue("_lastVelocity", currentVelocity); + rigidbody.SetValue("_currentVelocity", relativeVelocity); + } + + public float GetVelocityChangeMagnitude() + => (_relativeVelocity - _prevVelocity).magnitude; + + public Vector3 GetRelativeVelocity() + { + if (AttachedObject == null) + { + DebugLog.ToConsole($"Error - Trying to get relative velocity when AttachedObject is null.", MessageType.Error); + return Vector3.zero; + } + + if (ReferenceTransform == null) + { + DebugLog.ToConsole($"Error - Trying to get relative velocity when ReferenceTransform is null. ({AttachedObject.name})", MessageType.Error); + return Vector3.zero; + } + + var attachedRigid = ReferenceTransform.GetAttachedOWRigidbody(); + if (attachedRigid == null) + { + DebugLog.ToConsole($"Error - ReferenceTransform ({ReferenceTransform.name}) on {AttachedObject.name} has no attached OWRigidBody.", MessageType.Error); + return Vector3.zero; + } + + var pointVelocity = attachedRigid.GetPointVelocity(AttachedObject.transform.position); + return (AttachedObject as OWRigidbody).GetVelocity() - pointVelocity; + } + } +} diff --git a/QSB/Syncs/Unsectored/Rigidbodies/UnsectoredRigidbodySync.cs b/QSB/Syncs/Unsectored/Rigidbodies/UnsectoredRigidbodySync.cs new file mode 100644 index 00000000..677587fa --- /dev/null +++ b/QSB/Syncs/Unsectored/Rigidbodies/UnsectoredRigidbodySync.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; + +namespace QSB.Syncs.Unsectored.Rigidbodies +{ + public abstract class UnsectoredRigidbodySync : BaseUnsectoredSync + { + protected abstract OWRigidbody GetRigidbody(); + + protected override Component SetAttachedObject() + => throw new NotImplementedException(); + } +} From 447ed28b1095a0bc872c611b15ee946651b67c95 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sun, 22 Aug 2021 16:42:57 +0100 Subject: [PATCH 033/118] update ship transform sync --- .../TransformSync/ShipTransformSync.cs | 45 ++++++++++++++----- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/QSB/ShipSync/TransformSync/ShipTransformSync.cs b/QSB/ShipSync/TransformSync/ShipTransformSync.cs index 4b6f68fd..3d827d72 100644 --- a/QSB/ShipSync/TransformSync/ShipTransformSync.cs +++ b/QSB/ShipSync/TransformSync/ShipTransformSync.cs @@ -1,9 +1,7 @@ -using QSB.Player; using QSB.SectorSync; using QSB.Syncs.Sectored.Rigidbodies; using QSB.Utility; using QSB.WorldSync; -using UnityEngine; namespace QSB.ShipSync.TransformSync { @@ -11,6 +9,9 @@ namespace QSB.ShipSync.TransformSync { public static ShipTransformSync LocalInstance { get; private set; } + private const int ForcePositionAfterUpdates = 50; + private int _updateCount; + public override bool IsReady => Locator.GetShipBody() != null; @@ -22,29 +23,49 @@ namespace QSB.ShipSync.TransformSync protected override OWRigidbody GetRigidbody() { - QSBCore.UnityEvents.RunWhen(() => WorldObjectManager.AllReady, () => SectorSync.Init(Locator.GetShipDetector().GetComponent(), this)); + QSBCore.UnityEvents.RunWhen(() => WorldObjectManager.AllReady, () => SectorSync.Init(Locator.GetShipDetector().GetComponent(), TargetType.Ship)); return Locator.GetShipBody(); } + private void ForcePosition() + { + var targetPos = _intermediaryTransform.GetTargetPosition_Unparented(); + var targetRot = _intermediaryTransform.GetTargetRotation_Unparented(); + + (AttachedObject as OWRigidbody).SetPosition(targetPos); + (AttachedObject as OWRigidbody).SetRotation(targetRot); + } + protected override bool UpdateTransform() { - if (HasAuthority && ShipManager.Instance.CurrentFlyer != QSBPlayerManager.LocalPlayerId && ShipManager.Instance.CurrentFlyer != uint.MaxValue) + if (HasAuthority) { - DebugLog.ToConsole("Warning - Has authority, but is not current flyer!", OWML.Common.MessageType.Warning); - return false; + _intermediaryTransform.EncodePosition(AttachedObject.transform.position); + _intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation); + _relativeVelocity = GetRelativeVelocity(); + _relativeAngularVelocity = (AttachedObject as OWRigidbody).GetRelativeAngularVelocity(ReferenceTransform.GetAttachedOWRigidbody()); + return true; } - if (!HasAuthority && ShipManager.Instance.CurrentFlyer == QSBPlayerManager.LocalPlayerId) + _updateCount++; + + if (_updateCount >= ForcePositionAfterUpdates) { - DebugLog.ToConsole($"Warning - Doesn't have authority, but is current flyer!", OWML.Common.MessageType.Warning); - return false; + _updateCount = 0; + ForcePosition(); } - return base.UpdateTransform(); + var targetPos = _intermediaryTransform.GetTargetPosition_Unparented(); + + var targetVelocity = ReferenceTransform.GetAttachedOWRigidbody().GetPointVelocity(targetPos) + _relativeVelocity; + var targetAngularVelocity = ReferenceTransform.GetAttachedOWRigidbody().GetAngularVelocity() + _relativeAngularVelocity; + + SetVelocity(AttachedObject as OWRigidbody, targetVelocity); + (AttachedObject as OWRigidbody).SetAngularVelocity(targetAngularVelocity); + + return true; } - public override TargetType Type => TargetType.Ship; - public override bool UseInterpolation => true; protected override float DistanceLeeway => 20f; } From dc41dff1eb3fa60b887cf518a46b7208649f44c7 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sun, 22 Aug 2021 16:43:12 +0100 Subject: [PATCH 034/118] remove debug log from unsectoredtransformsync --- QSB/Syncs/Unsectored/Transforms/UnsectoredTransformSync.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QSB/Syncs/Unsectored/Transforms/UnsectoredTransformSync.cs b/QSB/Syncs/Unsectored/Transforms/UnsectoredTransformSync.cs index 3a8b15c6..548ea602 100644 --- a/QSB/Syncs/Unsectored/Transforms/UnsectoredTransformSync.cs +++ b/QSB/Syncs/Unsectored/Transforms/UnsectoredTransformSync.cs @@ -52,7 +52,7 @@ namespace QSB.Syncs.Unsectored.Transforms if (_intermediaryTransform.GetPosition() == Vector3.zero) { - DebugLog.ToConsole($"Warning - {_logName} at (0,0,0)! - Given position was {pos}", MessageType.Warning); + //DebugLog.ToConsole($"Warning - {_logName} at (0,0,0)! - Given position was {pos}", MessageType.Warning); } } From 59b2001754273eed107d9c874cfb3ed2b785cb7e Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sun, 22 Aug 2021 16:53:46 +0100 Subject: [PATCH 035/118] CLEANUP - sort usings --- QSB/Syncs/Sectored/BaseSectoredSync.cs | 4 ++-- QSB/Syncs/Unsectored/BaseUnsectoredSync.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/QSB/Syncs/Sectored/BaseSectoredSync.cs b/QSB/Syncs/Sectored/BaseSectoredSync.cs index 5f1aa5e4..845f6c3e 100644 --- a/QSB/Syncs/Sectored/BaseSectoredSync.cs +++ b/QSB/Syncs/Sectored/BaseSectoredSync.cs @@ -1,7 +1,7 @@ -using QSB.SectorSync.WorldObjects; +using QSB.Player; using QSB.SectorSync; +using QSB.SectorSync.WorldObjects; using QSB.Utility; -using QSB.Player; using QSB.WorldSync; using QuantumUNET.Transport; diff --git a/QSB/Syncs/Unsectored/BaseUnsectoredSync.cs b/QSB/Syncs/Unsectored/BaseUnsectoredSync.cs index c6dd2f59..9a8fad4c 100644 --- a/QSB/Syncs/Unsectored/BaseUnsectoredSync.cs +++ b/QSB/Syncs/Unsectored/BaseUnsectoredSync.cs @@ -1,8 +1,8 @@ -using System; +using QuantumUNET.Transport; +using System; using System.Collections.Generic; using System.Linq; using System.Text; -using QuantumUNET.Transport; namespace QSB.Syncs.Unsectored { From 4234631a7b5dea9dbc9f8af86ae2f51bf14260d7 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sun, 22 Aug 2021 16:54:48 +0100 Subject: [PATCH 036/118] CLEANUP - format document --- QSB/ClientServerStateSync/ClientStateManager.cs | 4 ++-- QSB/ClientServerStateSync/Events/ServerStateEvent.cs | 4 ++-- QSB/ClientServerStateSync/ServerStateManager.cs | 4 ++-- QSB/DeathSync/Events/EndLoopEvent.cs | 2 +- QSB/DeathSync/RespawnOnDeath.cs | 2 +- QSB/Player/PlayerInfo.cs | 4 ++-- QSB/ProbeSync/TransformSync/PlayerProbeSync.cs | 4 ++-- QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs | 2 +- QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs | 4 ++-- QSB/Syncs/Unsectored/BaseUnsectoredSync.cs | 2 +- QSB/TimeSync/WakeUpSync.cs | 2 +- QSB/Tools/ProbeLauncherTool/Events/RetrieveProbeEvent.cs | 4 ++-- QSB/Utility/DebugGUI.cs | 2 +- 13 files changed, 20 insertions(+), 20 deletions(-) diff --git a/QSB/ClientServerStateSync/ClientStateManager.cs b/QSB/ClientServerStateSync/ClientStateManager.cs index 079d4540..5b29f9db 100644 --- a/QSB/ClientServerStateSync/ClientStateManager.cs +++ b/QSB/ClientServerStateSync/ClientStateManager.cs @@ -13,7 +13,7 @@ namespace QSB.ClientServerStateSync public event ChangeStateEvent OnChangeState; public delegate void ChangeStateEvent(ClientState newState); - private void Awake() + private void Awake() => Instance = this; private void Start() @@ -78,7 +78,7 @@ namespace QSB.ClientServerStateSync } } } - + public void OnDeath() { var currentScene = QSBSceneManager.CurrentScene; diff --git a/QSB/ClientServerStateSync/Events/ServerStateEvent.cs b/QSB/ClientServerStateSync/Events/ServerStateEvent.cs index e93d7af8..c0cabdef 100644 --- a/QSB/ClientServerStateSync/Events/ServerStateEvent.cs +++ b/QSB/ClientServerStateSync/Events/ServerStateEvent.cs @@ -21,10 +21,10 @@ namespace QSB.ClientServerStateSync.Events EnumValue = state }; - public override void OnReceiveLocal(bool server, EnumMessage message) + public override void OnReceiveLocal(bool server, EnumMessage message) => OnReceiveRemote(server, message); - public override void OnReceiveRemote(bool server, EnumMessage message) + public override void OnReceiveRemote(bool server, EnumMessage message) => ServerStateManager.Instance.ChangeServerState(message.EnumValue); } } diff --git a/QSB/ClientServerStateSync/ServerStateManager.cs b/QSB/ClientServerStateSync/ServerStateManager.cs index 79f114e2..9dc6ebd5 100644 --- a/QSB/ClientServerStateSync/ServerStateManager.cs +++ b/QSB/ClientServerStateSync/ServerStateManager.cs @@ -16,7 +16,7 @@ namespace QSB.ClientServerStateSync private ServerState _currentState; - private void Awake() + private void Awake() => Instance = this; private void Start() @@ -42,7 +42,7 @@ namespace QSB.ClientServerStateSync OnChangeState?.Invoke(newState); } - public ServerState GetServerState() + public ServerState GetServerState() => _currentState; private void OnSceneLoaded(OWScene oldScene, OWScene newScene, bool inUniverse) diff --git a/QSB/DeathSync/Events/EndLoopEvent.cs b/QSB/DeathSync/Events/EndLoopEvent.cs index fda0229f..96aec37c 100644 --- a/QSB/DeathSync/Events/EndLoopEvent.cs +++ b/QSB/DeathSync/Events/EndLoopEvent.cs @@ -20,7 +20,7 @@ namespace QSB.DeathSync.Events EnumValue = type }; - public override void OnReceiveLocal(bool server, EnumMessage message) + public override void OnReceiveLocal(bool server, EnumMessage message) => OnReceiveRemote(server, message); public override void OnReceiveRemote(bool server, EnumMessage message) diff --git a/QSB/DeathSync/RespawnOnDeath.cs b/QSB/DeathSync/RespawnOnDeath.cs index 0ea04441..df5bc325 100644 --- a/QSB/DeathSync/RespawnOnDeath.cs +++ b/QSB/DeathSync/RespawnOnDeath.cs @@ -28,7 +28,7 @@ namespace QSB.DeathSync private Vector3 _deathPositionRelative; public Transform DeathClosestAstroObject { get; private set; } - public Vector3 DeathPositionWorld + public Vector3 DeathPositionWorld => DeathClosestAstroObject == null ? Vector3.zero : DeathClosestAstroObject.TransformPoint(_deathPositionRelative); diff --git a/QSB/Player/PlayerInfo.cs b/QSB/Player/PlayerInfo.cs index 2f5bfd88..e4448cf2 100644 --- a/QSB/Player/PlayerInfo.cs +++ b/QSB/Player/PlayerInfo.cs @@ -32,7 +32,7 @@ namespace QSB.Player // Tools public GameObject ProbeBody { get; set; } public QSBProbe Probe { get; set; } - public QSBFlashlight FlashLight + public QSBFlashlight FlashLight { get { @@ -87,7 +87,7 @@ namespace QSB.Player } } - public Flashlight LocalFlashlight + public Flashlight LocalFlashlight { get { diff --git a/QSB/ProbeSync/TransformSync/PlayerProbeSync.cs b/QSB/ProbeSync/TransformSync/PlayerProbeSync.cs index 6ef2cc07..31e29038 100644 --- a/QSB/ProbeSync/TransformSync/PlayerProbeSync.cs +++ b/QSB/ProbeSync/TransformSync/PlayerProbeSync.cs @@ -52,8 +52,8 @@ namespace QSB.ProbeSync.TransformSync return default; } - var body = probe.gameObject.activeSelf - ? probe.InstantiateInactive() + var body = probe.gameObject.activeSelf + ? probe.InstantiateInactive() : Instantiate(probe); body.name = "RemoteProbeTransform"; diff --git a/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs b/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs index 7c441405..4f2668f1 100644 --- a/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs +++ b/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs @@ -166,7 +166,7 @@ namespace QSB.Syncs.Sectored.Rigidbodies return true; } - public override bool HasMoved() + public override bool HasMoved() => CustomHasMoved( _intermediaryTransform.GetPosition(), _prevPosition, diff --git a/QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs b/QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs index 0addacea..efa831f5 100644 --- a/QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs +++ b/QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs @@ -16,7 +16,7 @@ namespace QSB.Syncs.Sectored.Transforms protected abstract Component InitLocalTransform(); protected abstract Component InitRemoteTransform(); - protected override Component SetAttachedObject() + protected override Component SetAttachedObject() => HasAuthority ? InitLocalTransform() : InitRemoteTransform(); public override void SerializeTransform(QNetworkWriter writer, bool initialState) @@ -36,7 +36,7 @@ namespace QSB.Syncs.Sectored.Transforms base.DeserializeTransform(reader, initialState); if (!QSBCore.WorldObjectsReady) - { + { reader.ReadVector3(); DeserializeRotation(reader); return; diff --git a/QSB/Syncs/Unsectored/BaseUnsectoredSync.cs b/QSB/Syncs/Unsectored/BaseUnsectoredSync.cs index 9a8fad4c..65b47f9d 100644 --- a/QSB/Syncs/Unsectored/BaseUnsectoredSync.cs +++ b/QSB/Syncs/Unsectored/BaseUnsectoredSync.cs @@ -1,4 +1,4 @@ -using QuantumUNET.Transport; +using QuantumUNET.Transport; using System; using System.Collections.Generic; using System.Linq; diff --git a/QSB/TimeSync/WakeUpSync.cs b/QSB/TimeSync/WakeUpSync.cs index 78f34a8d..51192bf4 100644 --- a/QSB/TimeSync/WakeUpSync.cs +++ b/QSB/TimeSync/WakeUpSync.cs @@ -236,7 +236,7 @@ namespace QSB.TimeSync private void UpdateServer() { _serverTime = Time.timeSinceLevelLoad; - + var serverState = ServerStateManager.Instance.GetServerState(); var clientState = QSBPlayerManager.LocalPlayer.State; diff --git a/QSB/Tools/ProbeLauncherTool/Events/RetrieveProbeEvent.cs b/QSB/Tools/ProbeLauncherTool/Events/RetrieveProbeEvent.cs index 0a37d160..493ea936 100644 --- a/QSB/Tools/ProbeLauncherTool/Events/RetrieveProbeEvent.cs +++ b/QSB/Tools/ProbeLauncherTool/Events/RetrieveProbeEvent.cs @@ -9,10 +9,10 @@ namespace QSB.Tools.ProbeLauncherTool.Events { public override EventType Type => EventType.RetrieveProbe; - public override void SetupListener() + public override void SetupListener() => GlobalMessenger.AddListener(EventNames.QSBRetrieveProbe, Handler); - public override void CloseListener() + public override void CloseListener() => GlobalMessenger.RemoveListener(EventNames.QSBRetrieveProbe, Handler); private void Handler(QSBProbeLauncher launcher, bool playEffects) => SendEvent(CreateMessage(launcher, playEffects)); diff --git a/QSB/Utility/DebugGUI.cs b/QSB/Utility/DebugGUI.cs index 349e539a..a04e8759 100644 --- a/QSB/Utility/DebugGUI.cs +++ b/QSB/Utility/DebugGUI.cs @@ -60,7 +60,7 @@ namespace QSB.Utility var networkTransform = player.TransformSync; var sector = networkTransform.ReferenceSector; - + GUI.Label(new Rect(420, offset2, 400f, 20f), $" - L.Pos : {networkTransform.transform.localPosition}", guiStyle); offset2 += _debugLineSpacing; GUI.Label(new Rect(420, offset2, 400f, 20f), $" - Sector : {(sector == null ? "NULL" : sector.Name)}", guiStyle); From ab23ed826ba1e67b5e806f38dec7bbb53620f0f0 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sun, 22 Aug 2021 16:57:09 +0100 Subject: [PATCH 037/118] CLEANUP - remove unnecessary usings --- QSB/OrbSync/OrbManager.cs | 1 - QSB/SectorSync/QSBSectorManager.cs | 1 - QSB/SectorSync/SectorSync.cs | 2 -- QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs | 1 - QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs | 4 ---- QSB/Syncs/SyncBase.cs | 3 +-- QSB/Syncs/Unsectored/BaseUnsectoredSync.cs | 4 ---- QSB/Syncs/Unsectored/Rigidbodies/UnsectoredRigidbodySync.cs | 3 --- 8 files changed, 1 insertion(+), 18 deletions(-) diff --git a/QSB/OrbSync/OrbManager.cs b/QSB/OrbSync/OrbManager.cs index 975452bd..f2c7767b 100644 --- a/QSB/OrbSync/OrbManager.cs +++ b/QSB/OrbSync/OrbManager.cs @@ -1,7 +1,6 @@ using OWML.Common; using QSB.OrbSync.TransformSync; using QSB.OrbSync.WorldObjects; -using QSB.Player; using QSB.Utility; using QSB.WorldSync; using QuantumUNET; diff --git a/QSB/SectorSync/QSBSectorManager.cs b/QSB/SectorSync/QSBSectorManager.cs index 534f83fd..3bbc053a 100644 --- a/QSB/SectorSync/QSBSectorManager.cs +++ b/QSB/SectorSync/QSBSectorManager.cs @@ -1,6 +1,5 @@ using OWML.Common; using QSB.SectorSync.WorldObjects; -using QSB.Syncs; using QSB.Syncs.Sectored; using QSB.Utility; using QSB.WorldSync; diff --git a/QSB/SectorSync/SectorSync.cs b/QSB/SectorSync/SectorSync.cs index 8d052213..5c1f4111 100644 --- a/QSB/SectorSync/SectorSync.cs +++ b/QSB/SectorSync/SectorSync.cs @@ -1,8 +1,6 @@ using OWML.Common; using OWML.Utils; using QSB.SectorSync.WorldObjects; -using QSB.Syncs; -using QSB.Syncs.Sectored; using QSB.Utility; using QSB.WorldSync; using System; diff --git a/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs b/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs index 4f2668f1..6262a503 100644 --- a/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs +++ b/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs @@ -1,6 +1,5 @@ using OWML.Common; using OWML.Utils; -using QSB.ShipSync; using QSB.Utility; using QuantumUNET.Transport; using UnityEngine; diff --git a/QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs b/QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs index efa831f5..f6412e14 100644 --- a/QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs +++ b/QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs @@ -1,10 +1,6 @@ using OWML.Common; using QSB.Utility; using QuantumUNET.Transport; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using UnityEngine; namespace QSB.Syncs.Sectored.Transforms diff --git a/QSB/Syncs/SyncBase.cs b/QSB/Syncs/SyncBase.cs index da7b6d23..5a55267c 100644 --- a/QSB/Syncs/SyncBase.cs +++ b/QSB/Syncs/SyncBase.cs @@ -1,5 +1,4 @@ -using Harmony; -using OWML.Common; +using OWML.Common; using QSB.Player; using QSB.Player.TransformSync; using QSB.Utility; diff --git a/QSB/Syncs/Unsectored/BaseUnsectoredSync.cs b/QSB/Syncs/Unsectored/BaseUnsectoredSync.cs index 65b47f9d..ff200a0e 100644 --- a/QSB/Syncs/Unsectored/BaseUnsectoredSync.cs +++ b/QSB/Syncs/Unsectored/BaseUnsectoredSync.cs @@ -1,8 +1,4 @@ using QuantumUNET.Transport; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace QSB.Syncs.Unsectored { diff --git a/QSB/Syncs/Unsectored/Rigidbodies/UnsectoredRigidbodySync.cs b/QSB/Syncs/Unsectored/Rigidbodies/UnsectoredRigidbodySync.cs index 677587fa..67290c2d 100644 --- a/QSB/Syncs/Unsectored/Rigidbodies/UnsectoredRigidbodySync.cs +++ b/QSB/Syncs/Unsectored/Rigidbodies/UnsectoredRigidbodySync.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using UnityEngine; namespace QSB.Syncs.Unsectored.Rigidbodies From 35dd2fbac2b3fc36e2d1a7da7a73da670a22bcd9 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sun, 22 Aug 2021 17:16:46 +0100 Subject: [PATCH 038/118] CLEANUP - remove extra newlines --- QSB/Player/PlayerInfo.cs | 1 - QSB/TimeSync/WakeUpSync.cs | 1 - QSB/Utility/DebugGUI.cs | 1 - 3 files changed, 3 deletions(-) diff --git a/QSB/Player/PlayerInfo.cs b/QSB/Player/PlayerInfo.cs index e4448cf2..01a28795 100644 --- a/QSB/Player/PlayerInfo.cs +++ b/QSB/Player/PlayerInfo.cs @@ -129,7 +129,6 @@ namespace QSB.Player } } - public PlayerInfo(uint id) { PlayerId = id; diff --git a/QSB/TimeSync/WakeUpSync.cs b/QSB/TimeSync/WakeUpSync.cs index 51192bf4..7e97e712 100644 --- a/QSB/TimeSync/WakeUpSync.cs +++ b/QSB/TimeSync/WakeUpSync.cs @@ -237,7 +237,6 @@ namespace QSB.TimeSync { _serverTime = Time.timeSinceLevelLoad; - var serverState = ServerStateManager.Instance.GetServerState(); var clientState = QSBPlayerManager.LocalPlayer.State; diff --git a/QSB/Utility/DebugGUI.cs b/QSB/Utility/DebugGUI.cs index a04e8759..3328f63f 100644 --- a/QSB/Utility/DebugGUI.cs +++ b/QSB/Utility/DebugGUI.cs @@ -60,7 +60,6 @@ namespace QSB.Utility var networkTransform = player.TransformSync; var sector = networkTransform.ReferenceSector; - GUI.Label(new Rect(420, offset2, 400f, 20f), $" - L.Pos : {networkTransform.transform.localPosition}", guiStyle); offset2 += _debugLineSpacing; GUI.Label(new Rect(420, offset2, 400f, 20f), $" - Sector : {(sector == null ? "NULL" : sector.Name)}", guiStyle); From 0b42ec426294ec10b4e6ccc89984308629c33682 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sun, 22 Aug 2021 17:17:46 +0100 Subject: [PATCH 039/118] CLEANUP - add new lines --- QSB/ClientServerStateSync/ClientStateManager.cs | 1 + QSB/ClientServerStateSync/Events/ClientStateEvent.cs | 1 + QSB/ClientServerStateSync/ServerStateManager.cs | 3 +++ QSB/DeathSync/Events/EndLoopEvent.cs | 1 + QSB/Patches/QSBPatch.cs | 1 + QSB/Player/Events/PlayerJoinEvent.cs | 2 ++ QSB/Player/QSBPlayerManager.cs | 1 + QSB/SectorSync/SectorSync.cs | 1 + QSB/ShipSync/ShipManager.cs | 1 + QSB/StatueSync/Events/StartStatueEvent.cs | 1 + QSB/Syncs/Sectored/BaseSectoredSync.cs | 1 + QSB/TimeSync/TimeSyncUI.cs | 2 ++ QSB/Tools/ProbeLauncherTool/Events/EquipProbeLauncherEvent.cs | 4 ++++ 13 files changed, 20 insertions(+) diff --git a/QSB/ClientServerStateSync/ClientStateManager.cs b/QSB/ClientServerStateSync/ClientStateManager.cs index 5b29f9db..a9d662e0 100644 --- a/QSB/ClientServerStateSync/ClientStateManager.cs +++ b/QSB/ClientServerStateSync/ClientStateManager.cs @@ -28,6 +28,7 @@ namespace QSB.ClientServerStateSync { return; } + DebugLog.DebugWrite($"CHANGE CLIENT STATE FROM {QSBPlayerManager.LocalPlayer.State} to {newState}"); QSBPlayerManager.LocalPlayer.State = newState; OnChangeState?.Invoke(newState); diff --git a/QSB/ClientServerStateSync/Events/ClientStateEvent.cs b/QSB/ClientServerStateSync/Events/ClientStateEvent.cs index 961379bc..e96ac30d 100644 --- a/QSB/ClientServerStateSync/Events/ClientStateEvent.cs +++ b/QSB/ClientServerStateSync/Events/ClientStateEvent.cs @@ -34,6 +34,7 @@ namespace QSB.ClientServerStateSync.Events DebugLog.DebugWrite($"Error - ID is uint.MaxValue!", OWML.Common.MessageType.Error); return; } + var player = QSBPlayerManager.GetPlayer(message.AboutId); player.State = message.EnumValue; } diff --git a/QSB/ClientServerStateSync/ServerStateManager.cs b/QSB/ClientServerStateSync/ServerStateManager.cs index 9dc6ebd5..33cfc873 100644 --- a/QSB/ClientServerStateSync/ServerStateManager.cs +++ b/QSB/ClientServerStateSync/ServerStateManager.cs @@ -25,6 +25,7 @@ namespace QSB.ClientServerStateSync { return; } + QSBSceneManager.OnSceneLoaded += OnSceneLoaded; GlobalMessenger.AddListener("TriggerSupernova", OnTriggerSupernova); @@ -37,6 +38,7 @@ namespace QSB.ClientServerStateSync { return; } + DebugLog.DebugWrite($"CHANGE SERVER STATE FROM {_currentState} to {newState}"); _currentState = newState; OnChangeState?.Invoke(newState); @@ -68,6 +70,7 @@ namespace QSB.ClientServerStateSync { QSBEventManager.FireEvent(EventNames.QSBServerState, ServerState.InSolarSystem); } + break; case OWScene.EyeOfTheUniverse: diff --git a/QSB/DeathSync/Events/EndLoopEvent.cs b/QSB/DeathSync/Events/EndLoopEvent.cs index 96aec37c..c99cbd50 100644 --- a/QSB/DeathSync/Events/EndLoopEvent.cs +++ b/QSB/DeathSync/Events/EndLoopEvent.cs @@ -34,6 +34,7 @@ namespace QSB.DeathSync.Events { QSBEventManager.FireEvent(EventNames.QSBServerState, ServerState.WaitingForDeath); } + break; } } diff --git a/QSB/Patches/QSBPatch.cs b/QSB/Patches/QSBPatch.cs index e2fc7889..45632a99 100644 --- a/QSB/Patches/QSBPatch.cs +++ b/QSB/Patches/QSBPatch.cs @@ -109,6 +109,7 @@ namespace QSB.Patches var paramList = method.GetParameters().Select(x => x.ParameterType); DebugLog.ToConsole($"- Found {method.Name}, but with params {string.Join(", ", paramList.Select(x => x.FullName).ToArray())}", MessageType.Error); } + return null; } diff --git a/QSB/Player/Events/PlayerJoinEvent.cs b/QSB/Player/Events/PlayerJoinEvent.cs index 87de9bf2..b5d6ee1a 100644 --- a/QSB/Player/Events/PlayerJoinEvent.cs +++ b/QSB/Player/Events/PlayerJoinEvent.cs @@ -30,6 +30,7 @@ namespace QSB.Player.Events DebugLog.ToConsole($"Error - Client {message.PlayerName} connecting with wrong QSB version. (Client:{message.QSBVersion}, Server:{QSBCore.QSBVersion})", MessageType.Error); QSBEventManager.FireEvent(EventNames.QSBPlayerKick, message.AboutId, KickReason.QSBVersionNotMatching); } + return; } @@ -40,6 +41,7 @@ namespace QSB.Player.Events DebugLog.ToConsole($"Error - Client {message.PlayerName} connecting with wrong game version. (Client:{message.GameVersion}, Server:{QSBCore.GameVersion})", MessageType.Error); QSBEventManager.FireEvent(EventNames.QSBPlayerKick, message.AboutId, KickReason.GameVersionNotMatching); } + return; } diff --git a/QSB/Player/QSBPlayerManager.cs b/QSB/Player/QSBPlayerManager.cs index 8e2c76fb..28f2545c 100644 --- a/QSB/Player/QSBPlayerManager.cs +++ b/QSB/Player/QSBPlayerManager.cs @@ -91,6 +91,7 @@ namespace QSB.Player { player.UpdateStateObjects(); } + player.State = message.ClientState; } diff --git a/QSB/SectorSync/SectorSync.cs b/QSB/SectorSync/SectorSync.cs index 5c1f4111..fa86b488 100644 --- a/QSB/SectorSync/SectorSync.cs +++ b/QSB/SectorSync/SectorSync.cs @@ -26,6 +26,7 @@ namespace QSB.SectorSync _sectorDetector.OnEnterSector -= AddSector; _sectorDetector.OnExitSector -= RemoveSector; } + IsReady = false; } diff --git a/QSB/ShipSync/ShipManager.cs b/QSB/ShipSync/ShipManager.cs index 40ab8bed..d2b55251 100644 --- a/QSB/ShipSync/ShipManager.cs +++ b/QSB/ShipSync/ShipManager.cs @@ -78,6 +78,7 @@ namespace QSB.ShipSync DebugLog.ToConsole($"Warning - ShipTransformSync's LocalInstance is not null, but it's gameobject is null!", MessageType.Warning); return; } + QNetworkServer.Destroy(ShipTransformSync.LocalInstance.gameObject); } diff --git a/QSB/StatueSync/Events/StartStatueEvent.cs b/QSB/StatueSync/Events/StartStatueEvent.cs index f643797f..75238b05 100644 --- a/QSB/StatueSync/Events/StartStatueEvent.cs +++ b/QSB/StatueSync/Events/StartStatueEvent.cs @@ -31,6 +31,7 @@ namespace QSB.StatueSync.Events { return; } + QSBEventManager.FireEvent(EventNames.QSBServerState, ServerState.InStatueCutscene); } diff --git a/QSB/Syncs/Sectored/BaseSectoredSync.cs b/QSB/Syncs/Sectored/BaseSectoredSync.cs index 845f6c3e..f7c11919 100644 --- a/QSB/Syncs/Sectored/BaseSectoredSync.cs +++ b/QSB/Syncs/Sectored/BaseSectoredSync.cs @@ -139,6 +139,7 @@ namespace QSB.Syncs.Sectored DebugLog.DebugWrite($"{_logName} set waiting sector id:{sectorId}"); _sectorIdWaitingSlot = sectorId; } + return; } diff --git a/QSB/TimeSync/TimeSyncUI.cs b/QSB/TimeSync/TimeSyncUI.cs index 8e027c76..df8a2efc 100644 --- a/QSB/TimeSync/TimeSyncUI.cs +++ b/QSB/TimeSync/TimeSyncUI.cs @@ -91,6 +91,7 @@ namespace QSB.TimeSync + "Fast-forwarding to match server time..."; break; } + break; case TimeSyncType.Pausing: @@ -112,6 +113,7 @@ namespace QSB.TimeSync text = "Waiting for end of loop..."; break; } + break; } diff --git a/QSB/Tools/ProbeLauncherTool/Events/EquipProbeLauncherEvent.cs b/QSB/Tools/ProbeLauncherTool/Events/EquipProbeLauncherEvent.cs index c292e4b1..378b60e3 100644 --- a/QSB/Tools/ProbeLauncherTool/Events/EquipProbeLauncherEvent.cs +++ b/QSB/Tools/ProbeLauncherTool/Events/EquipProbeLauncherEvent.cs @@ -30,11 +30,13 @@ namespace QSB.Tools.ProbeLauncherTool.Events _nonPlayerLauncherEquipped = true; return; } + if (_nonPlayerLauncherEquipped) { DebugLog.ToConsole($"Warning - Trying to equip player launcher whilst non player launcher is still equipped?", OWML.Common.MessageType.Warning); return; } + SendEvent(CreateMessage(true)); } @@ -45,11 +47,13 @@ namespace QSB.Tools.ProbeLauncherTool.Events _nonPlayerLauncherEquipped = false; return; } + if (_nonPlayerLauncherEquipped) { DebugLog.ToConsole($"Warning - Trying to de-equip player launcher whilst non player launcher is still equipped?", OWML.Common.MessageType.Warning); return; } + SendEvent(CreateMessage(false)); } From 170c48d0dc40ffe0ad89a3c14ddf274ea1c31d1d Mon Sep 17 00:00:00 2001 From: _nebula <41904486+misternebula@users.noreply.github.com> Date: Mon, 23 Aug 2021 14:37:21 +0100 Subject: [PATCH 040/118] Delete QSB.csproj.user --- QSB/QSB.csproj.user | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 QSB/QSB.csproj.user diff --git a/QSB/QSB.csproj.user b/QSB/QSB.csproj.user deleted file mode 100644 index 7bf6c3df..00000000 --- a/QSB/QSB.csproj.user +++ /dev/null @@ -1,8 +0,0 @@ - - - - D:\EpicGames\OuterWilds - C:\Users\Henry\AppData\Roaming\OuterWildsModManager\OWML - ShowAllFiles - - From cd71d9faa610fefa55bc85d8bf4b860d71b22be3 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Mon, 23 Aug 2021 14:42:12 +0100 Subject: [PATCH 041/118] Revert "Delete QSB.csproj.user" This reverts commit 170c48d0dc40ffe0ad89a3c14ddf274ea1c31d1d. --- QSB/QSB.csproj.user | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 QSB/QSB.csproj.user diff --git a/QSB/QSB.csproj.user b/QSB/QSB.csproj.user new file mode 100644 index 00000000..7bf6c3df --- /dev/null +++ b/QSB/QSB.csproj.user @@ -0,0 +1,8 @@ + + + + D:\EpicGames\OuterWilds + C:\Users\Henry\AppData\Roaming\OuterWildsModManager\OWML + ShowAllFiles + + From 65f8859c536b43b0efd3b237cfb92a7fdceeb7c0 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Mon, 23 Aug 2021 14:50:08 +0100 Subject: [PATCH 042/118] fix menu api being gone --- QSB/QSBCore.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/QSB/QSBCore.cs b/QSB/QSBCore.cs index 0aa3e3ba..eeac5ba0 100644 --- a/QSB/QSBCore.cs +++ b/QSB/QSBCore.cs @@ -65,6 +65,7 @@ namespace QSB public static bool IsInMultiplayer => QNetworkManager.singleton.isNetworkActive; public static string QSBVersion => Helper.Manifest.Version; public static string GameVersion => Application.version; + public static IMenuAPI MenuApi { get; private set; } public void Awake() { From b77e0c972b58558c1401cf05d7e10f700f610983 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Tue, 24 Aug 2021 20:59:05 +0100 Subject: [PATCH 043/118] fix stuff --- QSB/Menus/IMenuAPI.cs | 2 +- QSB/QSBCore.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/QSB/Menus/IMenuAPI.cs b/QSB/Menus/IMenuAPI.cs index cbcdbd3e..c115ce3a 100644 --- a/QSB/Menus/IMenuAPI.cs +++ b/QSB/Menus/IMenuAPI.cs @@ -24,7 +24,7 @@ namespace QSB.Menus GameObject OptionsMenu_MakeNonDisplaySliderElement(string label, string tooltipText, float savedValue, Menu menuTab); void OptionsMenu_MakeSpacer(float minHeight, Menu menuTab); void OptionsMenu_MakeLabel(string label, Menu menuTab); - void OptionsMenu_MakeTextInput(string label, string placeholderText, string savedValue, Menu menuTab); + void OptionsMenu_MakeTextInput(string label, string tooltipText, string placeholderText, string savedValue, Menu menuTab); // Misc PopupMenu MakeTwoChoicePopup(string message, string confirmText, string cancelText); PopupInputMenu MakeInputFieldPopup(string message, string placeholderMessage, string confirmText, string cancelText); diff --git a/QSB/QSBCore.cs b/QSB/QSBCore.cs index eeac5ba0..27b48d04 100644 --- a/QSB/QSBCore.cs +++ b/QSB/QSBCore.cs @@ -96,6 +96,7 @@ namespace QSB gameObject.AddComponent(); gameObject.AddComponent(); gameObject.AddComponent(); + gameObject.AddComponent(); gameObject.AddComponent(); // WorldObject managers From cc62f4db4ac99f341500886e5458515d764e27d3 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Tue, 24 Aug 2021 21:00:24 +0100 Subject: [PATCH 044/118] remove network manager hud --- QuantumUNET/Components/QNetworkManagerHUD.cs | 100 ------------------- QuantumUNET/QuantumUNET.csproj | 1 - 2 files changed, 101 deletions(-) delete mode 100644 QuantumUNET/Components/QNetworkManagerHUD.cs diff --git a/QuantumUNET/Components/QNetworkManagerHUD.cs b/QuantumUNET/Components/QNetworkManagerHUD.cs deleted file mode 100644 index 3d46d504..00000000 --- a/QuantumUNET/Components/QNetworkManagerHUD.cs +++ /dev/null @@ -1,100 +0,0 @@ -using UnityEngine; - -namespace QuantumUNET.Components -{ - public class QNetworkManagerHUD : MonoBehaviour - { - public QNetworkManager Manager; - public bool ShowGUI = true; - - private void Awake() - => Manager = GetComponent(); - - private void OnGUI() - { - if (ShowGUI) - { - var xOffset = 10; - var yOffset = 30; - var flag = Manager.client == null || Manager.client.connection == null || Manager.client.connection.connectionId == -1; - if (!Manager.IsClientConnected() && !QNetworkServer.active) - { - if (flag) - { - if (Application.platform != RuntimePlatform.WebGLPlayer) - { - if (GUI.Button(new Rect(xOffset, yOffset, 200f, 20f), "Host")) - { - Manager.StartHost(); - } - - yOffset += 20; - } - - if (GUI.Button(new Rect(xOffset, yOffset, 105f, 20f), "Connect")) - { - Manager.StartClient(); - } - - Manager.networkAddress = GUI.TextField(new Rect(xOffset + 100, yOffset, 95f, 20f), Manager.networkAddress); - yOffset += 20; - } - else - { - GUI.Label(new Rect(xOffset, yOffset, 200f, 20f), - $"Connecting to {Manager.networkAddress}:{Manager.networkPort}.."); - yOffset += 24; - if (GUI.Button(new Rect(xOffset, yOffset, 200f, 20f), "Cancel Connection Attempt")) - { - Manager.StopClient(); - } - } - } - else - { - if (QNetworkServer.active) - { - var text = $"Hosting on port {Manager.networkPort}"; - if (Manager.useWebSockets) - { - text += " (using WebSockets)"; - } - - GUI.Label(new Rect(xOffset, yOffset, 300f, 20f), text); - yOffset += 20; - } - - if (Manager.IsClientConnected()) - { - GUI.Label(new Rect(xOffset, yOffset, 300f, 20f), $"Connected to {Manager.networkAddress}, port {Manager.networkPort}"); - yOffset += 20; - } - } - - if (Manager.IsClientConnected() && !QClientScene.ready) - { - if (GUI.Button(new Rect(xOffset, yOffset, 200f, 20f), "Client Ready")) - { - QClientScene.Ready(Manager.client.connection); - if (QClientScene.localPlayers.Count == 0) - { - QClientScene.AddPlayer(0); - } - } - - yOffset += 20; - } - - if (QNetworkServer.active || Manager.IsClientConnected()) - { - if (GUI.Button(new Rect(xOffset, yOffset, 200f, 20f), "Stop")) - { - Manager.StopHost(); - } - - yOffset += 20; - } - } - } - } -} \ No newline at end of file diff --git a/QuantumUNET/QuantumUNET.csproj b/QuantumUNET/QuantumUNET.csproj index 9f52e6c5..74f0f29a 100644 --- a/QuantumUNET/QuantumUNET.csproj +++ b/QuantumUNET/QuantumUNET.csproj @@ -90,7 +90,6 @@ - From 29a470503546ccce3e852ca812d18b5c66d9310c Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Tue, 24 Aug 2021 21:05:31 +0100 Subject: [PATCH 045/118] remove qsbnetworklobby --- QSB/QSB.csproj | 1 - QSB/QSBNetworkLobby.cs | 71 ---------------------------------------- QSB/QSBNetworkManager.cs | 19 +++++++---- 3 files changed, 12 insertions(+), 79 deletions(-) delete mode 100644 QSB/QSBNetworkLobby.cs diff --git a/QSB/QSB.csproj b/QSB/QSB.csproj index c7a43fd6..58f7718f 100644 --- a/QSB/QSB.csproj +++ b/QSB/QSB.csproj @@ -230,7 +230,6 @@ - diff --git a/QSB/QSBNetworkLobby.cs b/QSB/QSBNetworkLobby.cs deleted file mode 100644 index be49c50b..00000000 --- a/QSB/QSBNetworkLobby.cs +++ /dev/null @@ -1,71 +0,0 @@ -using OWML.Utils; -using QuantumUNET; -using System; -using System.Linq; -using UnityEngine; - -namespace QSB -{ - public class QSBNetworkLobby : QNetworkBehaviour - { - public bool CanEditName { get; set; } - public string PlayerName { get; private set; } - - // TODO : Could delete a lot of this - shouldnt be possible to not have a profile and still play - - private readonly string[] _defaultNames = { - "Arkose", - "Chert", - "Esker", - "Hal", - "Hornfels", - "Feldspar", - "Gabbro", - "Galena", - "Gneiss", - "Gossan", - "Marl", - "Mica", - "Moraine", - "Porphy", - "Riebeck", - "Rutile", - "Slate", - "Spinel", - "Tektite", - "Tephra", - "Tuff", - "Jinha" - }; - - public void Awake() - { - PlayerName = GetPlayerName(); - CanEditName = true; - } - - private string GetPlayerName() - { - var profileManager = StandaloneProfileManager.SharedInstance; - profileManager.Initialize(); - var profile = profileManager.GetValue("_currentProfile"); - var profileName = profile?.profileName; - return !string.IsNullOrEmpty(profileName) - ? profileName - : _defaultNames.OrderBy(x => Guid.NewGuid()).First(); - } - - public void OnGUI() - { - GUI.Label(new Rect(10, 10, 200f, 20f), "Name:"); - if (CanEditName) - { - PlayerName = GUI.TextField(new Rect(60, 10, 145, 20f), PlayerName); - } - else - { - GUI.Label(new Rect(60, 10, 145, 20f), PlayerName); - } - } - } -} \ No newline at end of file diff --git a/QSB/QSBNetworkManager.cs b/QSB/QSBNetworkManager.cs index 06665466..222b068a 100644 --- a/QSB/QSBNetworkManager.cs +++ b/QSB/QSBNetworkManager.cs @@ -34,11 +34,11 @@ namespace QSB public bool IsReady { get; private set; } public GameObject OrbPrefab { get; private set; } public GameObject ShipPrefab { get; private set; } + public string PlayerName { get; private set; } private const int MaxConnections = 128; private const int MaxBufferedPackets = 64; - private QSBNetworkLobby _lobby; private AssetBundle _assetBundle; private GameObject _probePrefab; private bool _everConnected; @@ -48,7 +48,7 @@ namespace QSB base.Awake(); Instance = this; - _lobby = gameObject.AddComponent(); + PlayerName = GetPlayerName(); _assetBundle = QSBCore.NetworkAssetBundle; playerPrefab = _assetBundle.LoadAsset("assets/NETWORK_Player_Body.prefab"); @@ -82,6 +82,15 @@ namespace QSB ConfigureNetworkManager(); } + private string GetPlayerName() + { + var profileManager = StandaloneProfileManager.SharedInstance; + profileManager.Initialize(); + var profile = profileManager.GetValue("_currentProfile"); + var profileName = profile.profileName; + return profileName; + } + private void SetupNetworkId(GameObject go) { var ident = go.AddComponent(); @@ -163,13 +172,11 @@ namespace QSB QSBPatchManager.DoPatchType(specificType); QSBPatchManager.DoPatchType(QSBPatchTypes.OnClientConnect); - _lobby.CanEditName = false; - OnNetworkManagerReady?.SafeInvoke(); IsReady = true; QSBCore.UnityEvents.RunWhen(() => QSBEventManager.Ready && PlayerTransformSync.LocalInstance != null, - () => QSBEventManager.FireEvent(EventNames.QSBPlayerJoin, _lobby.PlayerName)); + () => QSBEventManager.FireEvent(EventNames.QSBPlayerJoin, PlayerName)); if (!QSBCore.IsHost) { @@ -201,8 +208,6 @@ namespace QSB QSBPatchManager.DoUnpatchType(QSBPatchTypes.OnClientConnect); } - _lobby.CanEditName = true; - IsReady = false; _everConnected = false; } From 3e5422af02b11db0d76377f9f3446eba21506bae Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Tue, 24 Aug 2021 21:08:55 +0100 Subject: [PATCH 046/118] replace with new buttons --- QSB/Menus/MenuManager.cs | 55 +++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/QSB/Menus/MenuManager.cs b/QSB/Menus/MenuManager.cs index 17978e70..5dd5a4ca 100644 --- a/QSB/Menus/MenuManager.cs +++ b/QSB/Menus/MenuManager.cs @@ -1,16 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using UnityEngine; +using UnityEngine; +using UnityEngine.UI; namespace QSB.Menus { class MenuManager : MonoBehaviour { private IMenuAPI MenuApi => QSBCore.MenuApi; - private PopupMenu HostWaitingPopup; - private PopupMenu ClientWaitingPopup; + private PopupMenu PopupMenu; + private GameObject MultiplayerButton; + private Button DisconnectButton; public void Start() { @@ -19,37 +17,36 @@ namespace QSB.Menus private void MakeTitleMenus() { - HostWaitingPopup = MenuApi.MakeTwoChoicePopup("Waiting for players to join...", "Start Multiplayer Game", "Stop Server"); - HostWaitingPopup.OnPopupCancel += StopServerOrLeaveServer; - ClientWaitingPopup = MenuApi.MakeTwoChoicePopup("Waiting for game to start...", "uhhhhh", "Disconnect"); - ClientWaitingPopup.OnPopupCancel += StopServerOrLeaveServer; + PopupMenu = MenuApi.MakeInputFieldPopup("IP Address", "IP Address", "Host a server", "Connect to server"); + PopupMenu.OnPopupConfirm += Host; + PopupMenu.OnPopupCancel += Connect; - var hostButton = MenuApi.TitleScreen_MakeSimpleButton("HOST SERVER"); - hostButton.onClick.AddListener(HostServer); - var connectButton = MenuApi.TitleScreen_MakeSimpleButton("CONNECT TO SERVER"); - connectButton.onClick.AddListener(ConnectToServer); - - var menu = MenuApi.OptionsMenu_MakeNonScrollingOptionsTab("MULTIPLAYER"); - //MenuApi.OptionsMenu_MakeLabel("Connection Information", menu); - MenuApi.OptionsMenu_MakeTextInput("IP Address", "IP Address", QSBCore.DefaultServerIP, menu); - MenuApi.OptionsMenu_MakeTextInput("Port", "Port", $"{QSBCore.Port}", menu); + MultiplayerButton = MenuApi.TitleScreen_MakeMenuOpenButton("MULTIPLAYER", PopupMenu); + DisconnectButton = MenuApi.TitleScreen_MakeSimpleButton("DISCONNECT"); + DisconnectButton.gameObject.SetActive(false); + DisconnectButton.onClick.AddListener(Disconnect); } - private void HostServer() - { - HostWaitingPopup.EnableMenu(true); - QSBNetworkManager.Instance.StartHost(); - } - - private void StopServerOrLeaveServer() + private void Disconnect() { QSBNetworkManager.Instance.StopHost(); + DisconnectButton.gameObject.SetActive(false); + MultiplayerButton.SetActive(true); } - private void ConnectToServer() + private void Host() { - ClientWaitingPopup.EnableMenu(true); + QSBNetworkManager.Instance.StartHost(); + DisconnectButton.gameObject.SetActive(true); + MultiplayerButton.SetActive(false); + } + + private void Connect() + { + QSBNetworkManager.Instance.networkAddress = (PopupMenu as PopupInputMenu).GetInputText(); QSBNetworkManager.Instance.StartClient(); + DisconnectButton.gameObject.SetActive(true); + MultiplayerButton.SetActive(false); } } } \ No newline at end of file From a5259cb237d2c83af8a921c164b1445a04bdd7b2 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Tue, 24 Aug 2021 22:30:53 +0100 Subject: [PATCH 047/118] Update MenuManager.cs --- QSB/Menus/MenuManager.cs | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/QSB/Menus/MenuManager.cs b/QSB/Menus/MenuManager.cs index 5dd5a4ca..0505bb17 100644 --- a/QSB/Menus/MenuManager.cs +++ b/QSB/Menus/MenuManager.cs @@ -7,7 +7,8 @@ namespace QSB.Menus { private IMenuAPI MenuApi => QSBCore.MenuApi; private PopupMenu PopupMenu; - private GameObject MultiplayerButton; + private Button HostButton; + private GameObject ClientButton; private Button DisconnectButton; public void Start() @@ -17,28 +18,36 @@ namespace QSB.Menus private void MakeTitleMenus() { - PopupMenu = MenuApi.MakeInputFieldPopup("IP Address", "IP Address", "Host a server", "Connect to server"); - PopupMenu.OnPopupConfirm += Host; - PopupMenu.OnPopupCancel += Connect; + PopupMenu = MenuApi.MakeInputFieldPopup("IP Address", "IP Address", "Connect", "Cancel"); + PopupMenu.OnPopupConfirm += Connect; + + HostButton = MenuApi.TitleScreen_MakeSimpleButton("MULTIPLAYER (HOST)"); + HostButton.onClick.AddListener(Host); + + ClientButton = MenuApi.TitleScreen_MakeMenuOpenButton("MULTIPLAYER (CONNECT)", PopupMenu); - MultiplayerButton = MenuApi.TitleScreen_MakeMenuOpenButton("MULTIPLAYER", PopupMenu); DisconnectButton = MenuApi.TitleScreen_MakeSimpleButton("DISCONNECT"); - DisconnectButton.gameObject.SetActive(false); DisconnectButton.onClick.AddListener(Disconnect); + + DisconnectButton.gameObject.SetActive(false); + DisconnectButton.GetComponent().alpha = 1f; } private void Disconnect() { QSBNetworkManager.Instance.StopHost(); DisconnectButton.gameObject.SetActive(false); - MultiplayerButton.SetActive(true); + ClientButton.SetActive(true); + HostButton.gameObject.SetActive(true); } private void Host() { QSBNetworkManager.Instance.StartHost(); DisconnectButton.gameObject.SetActive(true); - MultiplayerButton.SetActive(false); + DisconnectButton.GetComponent().alpha = 1f; + ClientButton.SetActive(false); + HostButton.gameObject.SetActive(false); } private void Connect() @@ -46,7 +55,9 @@ namespace QSB.Menus QSBNetworkManager.Instance.networkAddress = (PopupMenu as PopupInputMenu).GetInputText(); QSBNetworkManager.Instance.StartClient(); DisconnectButton.gameObject.SetActive(true); - MultiplayerButton.SetActive(false); + DisconnectButton.GetComponent().alpha = 1f; + ClientButton.SetActive(false); + HostButton.gameObject.SetActive(false); } } } \ No newline at end of file From 8629c18af10bf58de740506624f745ef137e929c Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Wed, 25 Aug 2021 10:22:33 +0100 Subject: [PATCH 048/118] cleanup --- QSB/Menus/MenuManager.cs | 1 - QSB/QSB.csproj | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/QSB/Menus/MenuManager.cs b/QSB/Menus/MenuManager.cs index 0505bb17..88606c78 100644 --- a/QSB/Menus/MenuManager.cs +++ b/QSB/Menus/MenuManager.cs @@ -28,7 +28,6 @@ namespace QSB.Menus DisconnectButton = MenuApi.TitleScreen_MakeSimpleButton("DISCONNECT"); DisconnectButton.onClick.AddListener(Disconnect); - DisconnectButton.gameObject.SetActive(false); DisconnectButton.GetComponent().alpha = 1f; } diff --git a/QSB/QSB.csproj b/QSB/QSB.csproj index 58f7718f..deadc5b3 100644 --- a/QSB/QSB.csproj +++ b/QSB/QSB.csproj @@ -433,4 +433,9 @@ copy /y "$(ProjectDir)\manifest.json" "$(OwmlDir)\Mods\$(ProjectName)" copy /y "$(SolutionDir)\WeavedFiles\QSB.dll" "$(OwmlDir)\Mods\$(ProjectName)" + + + + + \ No newline at end of file From 403472eef0179ae0948792c2036dc8fafe26eb33 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Wed, 25 Aug 2021 10:41:11 +0100 Subject: [PATCH 049/118] more cleanup --- QSB/Menus/IMenuAPI.cs | 6 +----- QSB/QSBCore.cs | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/QSB/Menus/IMenuAPI.cs b/QSB/Menus/IMenuAPI.cs index c115ce3a..a9575759 100644 --- a/QSB/Menus/IMenuAPI.cs +++ b/QSB/Menus/IMenuAPI.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using UnityEngine; +using UnityEngine; using UnityEngine.UI; namespace QSB.Menus diff --git a/QSB/QSBCore.cs b/QSB/QSBCore.cs index 27b48d04..26974b76 100644 --- a/QSB/QSBCore.cs +++ b/QSB/QSBCore.cs @@ -88,7 +88,6 @@ namespace QSB QSBPatchManager.Init(); gameObject.AddComponent(); - //gameObject.AddComponent(); gameObject.AddComponent(); gameObject.AddComponent(); gameObject.AddComponent(); From 51c096cb86f1c8bb2670150fed569e1262173e09 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Wed, 25 Aug 2021 14:10:05 +0100 Subject: [PATCH 050/118] cleanup --- QSB/ProbeSync/QSBProbeEffects.cs | 1 - QSB/ProbeSync/QSBProbeSpotlight.cs | 4 ---- QSB/TimeSync/WakeUpSync.cs | 3 --- 3 files changed, 8 deletions(-) diff --git a/QSB/ProbeSync/QSBProbeEffects.cs b/QSB/ProbeSync/QSBProbeEffects.cs index 14c9b4e3..86fbdb1e 100644 --- a/QSB/ProbeSync/QSBProbeEffects.cs +++ b/QSB/ProbeSync/QSBProbeEffects.cs @@ -9,7 +9,6 @@ namespace QSB.ProbeSync public OWAudioSource _flightLoopAudio; public OWAudioSource _anchorAudio; public ParticleSystem _anchorParticles; - public ParticleSystem _underwaterAnchorParticles; private QSBProbe _probe; diff --git a/QSB/ProbeSync/QSBProbeSpotlight.cs b/QSB/ProbeSync/QSBProbeSpotlight.cs index 1081ef11..1e1de58b 100644 --- a/QSB/ProbeSync/QSBProbeSpotlight.cs +++ b/QSB/ProbeSync/QSBProbeSpotlight.cs @@ -12,7 +12,6 @@ namespace QSB.ProbeSync private QSBProbe _probe; private OWLight2 _light; - private bool _inFlight; private float _timer; private void Awake() @@ -64,15 +63,12 @@ namespace QSB.ProbeSync { StartFadeIn(); } - - _inFlight = true; } private void OnAnchorOrRetrieve() { _light.GetLight().enabled = false; enabled = false; - _inFlight = false; } } } diff --git a/QSB/TimeSync/WakeUpSync.cs b/QSB/TimeSync/WakeUpSync.cs index 7e97e712..090bdef9 100644 --- a/QSB/TimeSync/WakeUpSync.cs +++ b/QSB/TimeSync/WakeUpSync.cs @@ -31,7 +31,6 @@ namespace QSB.TimeSync private float _sendTimer; private float _serverTime; - private bool _isFirstFastForward = true; private int _serverLoopCount; private bool _hasWokenUp; @@ -46,7 +45,6 @@ namespace QSB.TimeSync if (QSBSceneManager.IsInUniverse) { - _isFirstFastForward = false; Init(); } @@ -203,7 +201,6 @@ namespace QSB.TimeSync CurrentReason = null; DebugLog.DebugWrite($"RESET TIMESCALE", MessageType.Info); - _isFirstFastForward = false; Physics.SyncTransforms(); SpinnerUI.Hide(); TimeSyncUI.Stop(); From 9ad3f9fcaa3a82c7567a4af6e2e74b4a06804cc4 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 26 Aug 2021 08:51:33 +0100 Subject: [PATCH 051/118] push it aaaaa --- QSB/Menus/IMenuAPI.cs | 1 + QSB/Menus/MenuManager.cs | 65 +++++++++++++++++++++++++++++++++++++++- QSB/QSBNetworkManager.cs | 10 +++++++ 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/QSB/Menus/IMenuAPI.cs b/QSB/Menus/IMenuAPI.cs index a9575759..e4682dd2 100644 --- a/QSB/Menus/IMenuAPI.cs +++ b/QSB/Menus/IMenuAPI.cs @@ -24,5 +24,6 @@ namespace QSB.Menus // Misc PopupMenu MakeTwoChoicePopup(string message, string confirmText, string cancelText); PopupInputMenu MakeInputFieldPopup(string message, string placeholderMessage, string confirmText, string cancelText); + PopupMenu MakeInfoPopup(string message, string continueButtonText); } } diff --git a/QSB/Menus/MenuManager.cs b/QSB/Menus/MenuManager.cs index 88606c78..32187499 100644 --- a/QSB/Menus/MenuManager.cs +++ b/QSB/Menus/MenuManager.cs @@ -1,4 +1,7 @@ -using UnityEngine; +using QSB.Utility; +using QuantumUNET; +using UnityEngine; +using UnityEngine.Networking; using UnityEngine.UI; namespace QSB.Menus @@ -10,10 +13,46 @@ namespace QSB.Menus private Button HostButton; private GameObject ClientButton; private Button DisconnectButton; + private PopupMenu InfoPopup; public void Start() { MakeTitleMenus(); + QSBSceneManager.OnSceneLoaded += OnSceneLoaded; + QSBNetworkManager.Instance.OnClientConnected += OnConnected; + QSBNetworkManager.Instance.OnClientDisconnected += OnDisconnected; + } + + void OnSceneLoaded(OWScene oldScene, OWScene newScene, bool isUniverse) + { + if (isUniverse) + { + InitPauseMenus(); + return; + } + + if (newScene == OWScene.TitleScreen) + { + MakeTitleMenus(); + } + } + + private void InitPauseMenus() + { + PopupMenu = MenuApi.MakeInputFieldPopup("IP Address", "IP Address", "Connect", "Cancel"); + PopupMenu.OnPopupConfirm += Connect; + + InfoPopup = MenuApi.MakeInfoPopup("you forgot to set the text!!!", "you dumpty!!!"); + + HostButton = MenuApi.PauseMenu_MakeSimpleButton("MULTIPLAYER (HOST)"); + HostButton.onClick.AddListener(Host); + + ClientButton = MenuApi.PauseMenu_MakeMenuOpenButton("MULTIPLAYER (CONNECT)", PopupMenu); + + DisconnectButton = MenuApi.PauseMenu_MakeSimpleButton("DISCONNECT"); + DisconnectButton.onClick.AddListener(Disconnect); + DisconnectButton.gameObject.SetActive(false); + DisconnectButton.GetComponent().alpha = 1f; } private void MakeTitleMenus() @@ -21,6 +60,8 @@ namespace QSB.Menus PopupMenu = MenuApi.MakeInputFieldPopup("IP Address", "IP Address", "Connect", "Cancel"); PopupMenu.OnPopupConfirm += Connect; + InfoPopup = MenuApi.MakeInfoPopup("you forgot to set the text!!!", "you dumpty!!!"); + HostButton = MenuApi.TitleScreen_MakeSimpleButton("MULTIPLAYER (HOST)"); HostButton.onClick.AddListener(Host); @@ -53,10 +94,32 @@ namespace QSB.Menus { QSBNetworkManager.Instance.networkAddress = (PopupMenu as PopupInputMenu).GetInputText(); 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() + { + DebugLog.DebugWrite($"ON CONNECTED"); + DisconnectButton.transform.GetChild(0).GetChild(1).GetComponent().text = "DISCONNECT"; + } + + private void OnDisconnected(NetworkError error) + { + if (error == NetworkError.Ok) + { + return; + } + + InfoPopup.SetUpPopup($"Client Disconnected. Reason : {error}", InputLibrary.menuConfirm, InputLibrary.cancel, new ScreenPrompt("OK"), null, true, false); + InfoPopup.EnableMenu(true); + + DisconnectButton.gameObject.SetActive(false); + ClientButton.SetActive(true); + HostButton.gameObject.SetActive(true); + } } } \ No newline at end of file diff --git a/QSB/QSBNetworkManager.cs b/QSB/QSBNetworkManager.cs index 222b068a..c574e63d 100644 --- a/QSB/QSBNetworkManager.cs +++ b/QSB/QSBNetworkManager.cs @@ -30,6 +30,8 @@ namespace QSB public static QSBNetworkManager Instance { get; private set; } public event Action OnNetworkManagerReady; + public event Action OnClientConnected; + public event Action OnClientDisconnected; public bool IsReady { get; private set; } public GameObject OrbPrefab { get; private set; } @@ -157,6 +159,8 @@ namespace QSB DebugLog.DebugWrite("OnClientConnect", MessageType.Info); base.OnClientConnect(connection); + OnClientConnected?.SafeInvoke(); + QSBEventManager.Init(); gameObject.AddComponent(); @@ -212,6 +216,12 @@ namespace QSB _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); From be37a831d7a180a7eee07be63e5b7d8f5b09ade2 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 26 Aug 2021 14:56:07 +0100 Subject: [PATCH 052/118] move pause-stopping patch to timepatches --- QSB/QSBCore.cs | 3 --- QSB/TimeSync/Patches/TimePatches.cs | 5 ++++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/QSB/QSBCore.cs b/QSB/QSBCore.cs index 26974b76..51e3ebbb 100644 --- a/QSB/QSBCore.cs +++ b/QSB/QSBCore.cs @@ -117,9 +117,6 @@ namespace QSB Helper.HarmonyHelper.EmptyMethod("Update"); - // Stop players being able to pause - Helper.HarmonyHelper.EmptyMethod(typeof(OWTime).GetMethod("Pause")); - QSBPatchManager.OnPatchType += OnPatchType; QSBPatchManager.OnUnpatchType += OnUnpatchType; } diff --git a/QSB/TimeSync/Patches/TimePatches.cs b/QSB/TimeSync/Patches/TimePatches.cs index fe543a04..9692d9e1 100644 --- a/QSB/TimeSync/Patches/TimePatches.cs +++ b/QSB/TimeSync/Patches/TimePatches.cs @@ -7,7 +7,10 @@ namespace QSB.TimeSync.Patches public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; public override void DoPatches() - => Prefix(nameof(PlayerCameraEffectController_OnStartOfTimeLoop)); + { + Prefix(nameof(PlayerCameraEffectController_OnStartOfTimeLoop)); + Empty("OWTime_Pause"); + } public static bool PlayerCameraEffectController_OnStartOfTimeLoop() => false; From 7aeb530910d884c6f4430215c5fde79998a51e1c Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 26 Aug 2021 14:56:42 +0100 Subject: [PATCH 053/118] cleanup menumanager, fix popup breaking stuff in-game --- QSB/Menus/MenuManager.cs | 48 +++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/QSB/Menus/MenuManager.cs b/QSB/Menus/MenuManager.cs index 32187499..1a8c033e 100644 --- a/QSB/Menus/MenuManager.cs +++ b/QSB/Menus/MenuManager.cs @@ -14,6 +14,7 @@ namespace QSB.Menus private GameObject ClientButton; private Button DisconnectButton; private PopupMenu InfoPopup; + private bool _addedPauseLock; public void Start() { @@ -37,12 +38,47 @@ namespace QSB.Menus } } - private void InitPauseMenus() + private void OpenInfoPopup(string message, string buttonText) + { + InfoPopup.SetUpPopup(message, InputLibrary.menuConfirm, InputLibrary.cancel, new ScreenPrompt(buttonText), null, true, false); + + OWTime.Pause(OWTime.PauseType.System); + OWInput.ChangeInputMode(InputMode.Menu); + + var pauseCommandListener = Locator.GetPauseCommandListener(); + if (pauseCommandListener != null) + { + pauseCommandListener.AddPauseCommandLock(); + _addedPauseLock = true; + } + + InfoPopup.EnableMenu(true); + } + + private void OnCloseInfoPopup() + { + var pauseCommandListener = Locator.GetPauseCommandListener(); + if (pauseCommandListener != null && _addedPauseLock) + { + pauseCommandListener.RemovePauseCommandLock(); + _addedPauseLock = false; + } + OWTime.Unpause(OWTime.PauseType.System); + OWInput.RestorePreviousInputs(); + } + + private void CreateCommonPopups() { PopupMenu = MenuApi.MakeInputFieldPopup("IP Address", "IP Address", "Connect", "Cancel"); PopupMenu.OnPopupConfirm += Connect; - InfoPopup = MenuApi.MakeInfoPopup("you forgot to set the text!!!", "you dumpty!!!"); + InfoPopup = MenuApi.MakeInfoPopup("DEFAULT TEXT", "you forgor 💀"); + InfoPopup.OnDeactivateMenu += OnCloseInfoPopup; + } + + private void InitPauseMenus() + { + CreateCommonPopups(); HostButton = MenuApi.PauseMenu_MakeSimpleButton("MULTIPLAYER (HOST)"); HostButton.onClick.AddListener(Host); @@ -57,10 +93,7 @@ namespace QSB.Menus private void MakeTitleMenus() { - PopupMenu = MenuApi.MakeInputFieldPopup("IP Address", "IP Address", "Connect", "Cancel"); - PopupMenu.OnPopupConfirm += Connect; - - InfoPopup = MenuApi.MakeInfoPopup("you forgot to set the text!!!", "you dumpty!!!"); + CreateCommonPopups(); HostButton = MenuApi.TitleScreen_MakeSimpleButton("MULTIPLAYER (HOST)"); HostButton.onClick.AddListener(Host); @@ -114,8 +147,7 @@ namespace QSB.Menus return; } - InfoPopup.SetUpPopup($"Client Disconnected. Reason : {error}", InputLibrary.menuConfirm, InputLibrary.cancel, new ScreenPrompt("OK"), null, true, false); - InfoPopup.EnableMenu(true); + OpenInfoPopup($"Client Disconnected. Reason : {error}", "OK"); DisconnectButton.gameObject.SetActive(false); ClientButton.SetActive(true); From 9e04dd6509ea72e7cba58af592667e6b74aaea3e Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Fri, 27 Aug 2021 11:05:47 +0100 Subject: [PATCH 054/118] removed joke text --- QSB/Menus/MenuManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QSB/Menus/MenuManager.cs b/QSB/Menus/MenuManager.cs index 1a8c033e..7ac92967 100644 --- a/QSB/Menus/MenuManager.cs +++ b/QSB/Menus/MenuManager.cs @@ -72,7 +72,7 @@ namespace QSB.Menus PopupMenu = MenuApi.MakeInputFieldPopup("IP Address", "IP Address", "Connect", "Cancel"); PopupMenu.OnPopupConfirm += Connect; - InfoPopup = MenuApi.MakeInfoPopup("DEFAULT TEXT", "you forgor 💀"); + InfoPopup = MenuApi.MakeInfoPopup("", ""); InfoPopup.OnDeactivateMenu += OnCloseInfoPopup; } From 95f2e35a953125e0f865370127dadc5481775200 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Fri, 27 Aug 2021 13:02:05 +0100 Subject: [PATCH 055/118] added support for kick messages, better displaying of network errors --- QSB/Menus/MenuManager.cs | 48 ++++++++++++++++++++++++++-- QSB/Player/Events/PlayerKickEvent.cs | 2 ++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/QSB/Menus/MenuManager.cs b/QSB/Menus/MenuManager.cs index 7ac92967..369e788f 100644 --- a/QSB/Menus/MenuManager.cs +++ b/QSB/Menus/MenuManager.cs @@ -1,4 +1,5 @@ -using QSB.Utility; +using QSB.Player; +using QSB.Utility; using QuantumUNET; using UnityEngine; using UnityEngine.Networking; @@ -8,6 +9,8 @@ namespace QSB.Menus { class MenuManager : MonoBehaviour { + public static MenuManager Instance; + private IMenuAPI MenuApi => QSBCore.MenuApi; private PopupMenu PopupMenu; private Button HostButton; @@ -18,6 +21,7 @@ namespace QSB.Menus public void Start() { + Instance = this; MakeTitleMenus(); QSBSceneManager.OnSceneLoaded += OnSceneLoaded; QSBNetworkManager.Instance.OnClientConnected += OnConnected; @@ -137,7 +141,34 @@ namespace QSB.Menus private void OnConnected() { DebugLog.DebugWrite($"ON CONNECTED"); - DisconnectButton.transform.GetChild(0).GetChild(1).GetComponent().text = "DISCONNECT"; + + var text = QSBCore.IsHost + ? "STOP HOSTING" + : "DISCONNECT"; + DisconnectButton.transform.GetChild(0).GetChild(1).GetComponent().text = text; + } + + public void OnKicked(KickReason reason) + { + string text; + switch (reason) + { + case KickReason.QSBVersionNotMatching: + text = "Server refused connection as QSB version does not match."; + break; + case KickReason.GameVersionNotMatching: + text = "Server refused connection as Outer Wilds version does not match."; + break; + default: + text = $"Kicked from server. KickReason:{reason}"; + break; + } + + OpenInfoPopup(text, "OK"); + + DisconnectButton.gameObject.SetActive(false); + ClientButton.SetActive(true); + HostButton.gameObject.SetActive(true); } private void OnDisconnected(NetworkError error) @@ -147,7 +178,18 @@ namespace QSB.Menus return; } - OpenInfoPopup($"Client Disconnected. Reason : {error}", "OK"); + string text; + switch (error) + { + case NetworkError.Timeout: + text = "Connection timed out. Either the server does not exist, or it has stopped responding."; + break; + default: + text = $"Disconnected due to error. NetworkError:{error}"; + break; + } + + OpenInfoPopup(text, "OK"); DisconnectButton.gameObject.SetActive(false); ClientButton.SetActive(true); diff --git a/QSB/Player/Events/PlayerKickEvent.cs b/QSB/Player/Events/PlayerKickEvent.cs index 258eeb03..ad4216d8 100644 --- a/QSB/Player/Events/PlayerKickEvent.cs +++ b/QSB/Player/Events/PlayerKickEvent.cs @@ -1,4 +1,5 @@ using QSB.Events; +using QSB.Menus; using QSB.Messaging; using QSB.Utility; using QuantumUNET; @@ -49,6 +50,7 @@ namespace QSB.Player.Events } DebugLog.ToAll($"Kicked from server. Reason : {message.EnumValue}"); + MenuManager.Instance.OnKicked(message.EnumValue); } } } From d1bc4d4c59afc9d5523d4da4bd78ec9961269e15 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 28 Aug 2021 11:07:59 +0100 Subject: [PATCH 056/118] Update QSB.csproj --- QSB/QSB.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QSB/QSB.csproj b/QSB/QSB.csproj index deadc5b3..9858b117 100644 --- a/QSB/QSB.csproj +++ b/QSB/QSB.csproj @@ -426,7 +426,7 @@ md "$(OwmlDir)\Mods\$(ProjectName)\assets" del "$(OwmlDir)\Mods\$(ProjectName)\config.json" copy /y "$(ProjectDir)\default-config.json" "$(OwmlDir)\Mods\$(ProjectName)" -copy /y "$(SolutionDir)\AssetBundles" "$(OwmlDir)\Mods\$(ProjectName)\assets +copy /y "$(SolutionDir)\AssetBundles" "$(OwmlDir)\Mods\$(ProjectName)\assets" copy /y "$(ProjectDir)\manifest.json" "$(OwmlDir)\Mods\$(ProjectName)" "$(SolutionDir)\QNetWeaver\bin\Debug\QNetWeaver.exe" "$(GameDir)\OuterWilds_Data\Managed\UnityEngine.CoreModule.dll" "$(OwmlDir)\Mods\$(ProjectName)\QuantumUNET.dll" "$(GameDir)\OuterWilds_Data\Managed\UnityEngine.Networking.dll" "$(SolutionDir)\WeavedFiles" "$(TargetPath)" From d8d3fbe5f7b6fb5dcf5fd1108f0ad70ad3f1907f Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 28 Aug 2021 12:55:23 +0100 Subject: [PATCH 057/118] correct & to && --- QSB/TimeSync/TimeSyncUI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QSB/TimeSync/TimeSyncUI.cs b/QSB/TimeSync/TimeSyncUI.cs index df8a2efc..6a0743c7 100644 --- a/QSB/TimeSync/TimeSyncUI.cs +++ b/QSB/TimeSync/TimeSyncUI.cs @@ -38,7 +38,7 @@ namespace QSB.TimeSync public void OnDestroy() { QSBSceneManager.OnUniverseSceneLoaded -= OnUniverseSceneLoad; - if (_canvas != null & _canvas.enabled) + if (_canvas != null && _canvas.enabled) { Canvas.willRenderCanvases -= OnWillRenderCanvases; } From d9eba599f96fbab92b559293f16faefdaa28f8a8 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 28 Aug 2021 12:56:01 +0100 Subject: [PATCH 058/118] add "none" kickreason --- QSB/Menus/MenuManager.cs | 4 +++- QSB/Player/KickReason.cs | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/QSB/Menus/MenuManager.cs b/QSB/Menus/MenuManager.cs index 369e788f..c22548da 100644 --- a/QSB/Menus/MenuManager.cs +++ b/QSB/Menus/MenuManager.cs @@ -1,6 +1,5 @@ using QSB.Player; using QSB.Utility; -using QuantumUNET; using UnityEngine; using UnityEngine.Networking; using UnityEngine.UI; @@ -159,6 +158,9 @@ namespace QSB.Menus case KickReason.GameVersionNotMatching: text = "Server refused connection as Outer Wilds version does not match."; break; + case KickReason.None: + text = "Kicked from server. No reason given."; + break; default: text = $"Kicked from server. KickReason:{reason}"; break; diff --git a/QSB/Player/KickReason.cs b/QSB/Player/KickReason.cs index 57c75148..4e911581 100644 --- a/QSB/Player/KickReason.cs +++ b/QSB/Player/KickReason.cs @@ -2,6 +2,7 @@ { public enum KickReason { + None, QSBVersionNotMatching, GameVersionNotMatching } From 5c0ba4574bcbf669aa40b31d62e52e8f939ea577 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 28 Aug 2021 21:53:35 +0100 Subject: [PATCH 059/118] add onAddPlayer event --- QSB/Player/QSBPlayerManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/QSB/Player/QSBPlayerManager.cs b/QSB/Player/QSBPlayerManager.cs index 28f2545c..fb66ebb2 100644 --- a/QSB/Player/QSBPlayerManager.cs +++ b/QSB/Player/QSBPlayerManager.cs @@ -35,6 +35,7 @@ namespace QSB.Player } public static Action OnRemovePlayer; + public static Action OnAddPlayer; public static PlayerInfo LocalPlayer => GetPlayer(LocalPlayerId); public static List PlayerList { get; } = new List(); @@ -69,6 +70,7 @@ namespace QSB.Player DebugLog.DebugWrite($"Create Player : id<{id}> Stacktrace :\r\n{Environment.StackTrace}", MessageType.Info); player = new PlayerInfo(id); PlayerList.Add(player); + OnAddPlayer?.Invoke(id); return player; } From 524454a18d1baed0594101c0e0795d51f9120fd2 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sun, 29 Aug 2021 16:42:20 +0100 Subject: [PATCH 060/118] cleanup qulocalconnectiontoserver/client --- QuantumUNET/QULocalConnectionToClient.cs | 14 ----------- QuantumUNET/QULocalConnectionToServer.cs | 30 +++++++----------------- 2 files changed, 8 insertions(+), 36 deletions(-) diff --git a/QuantumUNET/QULocalConnectionToClient.cs b/QuantumUNET/QULocalConnectionToClient.cs index 331851c0..adb8368d 100644 --- a/QuantumUNET/QULocalConnectionToClient.cs +++ b/QuantumUNET/QULocalConnectionToClient.cs @@ -42,19 +42,5 @@ namespace QuantumUNET LocalClient.InvokeBytesOnClient(writer.AsArray(), channelId); return true; } - - public override void GetStatsOut(out int numMsgs, out int numBufferedMsgs, out int numBytes, out int lastBufferedPerSecond) - { - numMsgs = 0; - numBufferedMsgs = 0; - numBytes = 0; - lastBufferedPerSecond = 0; - } - - public override void GetStatsIn(out int numMsgs, out int numBytes) - { - numMsgs = 0; - numBytes = 0; - } } } \ No newline at end of file diff --git a/QuantumUNET/QULocalConnectionToServer.cs b/QuantumUNET/QULocalConnectionToServer.cs index d845a1e8..7b991ad0 100644 --- a/QuantumUNET/QULocalConnectionToServer.cs +++ b/QuantumUNET/QULocalConnectionToServer.cs @@ -12,14 +12,14 @@ namespace QuantumUNET m_LocalServer = localServer; } - public override bool Send(short msgType, QMessageBase msg) => - m_LocalServer.InvokeHandlerOnServer(this, msgType, msg, 0); + public override bool Send(short msgType, QMessageBase msg) + => m_LocalServer.InvokeHandlerOnServer(this, msgType, msg, 0); - public override bool SendUnreliable(short msgType, QMessageBase msg) => - m_LocalServer.InvokeHandlerOnServer(this, msgType, msg, 1); + public override bool SendUnreliable(short msgType, QMessageBase msg) + => m_LocalServer.InvokeHandlerOnServer(this, msgType, msg, 1); - public override bool SendByChannel(short msgType, QMessageBase msg, int channelId) => - m_LocalServer.InvokeHandlerOnServer(this, msgType, msg, channelId); + public override bool SendByChannel(short msgType, QMessageBase msg, int channelId) + => m_LocalServer.InvokeHandlerOnServer(this, msgType, msg, channelId); public override bool SendBytes(byte[] bytes, int numBytes, int channelId) { @@ -37,22 +37,8 @@ namespace QuantumUNET return result; } - public override bool SendWriter(QNetworkWriter writer, int channelId) => - m_LocalServer.InvokeBytes(this, writer.AsArray(), (short)writer.AsArray().Length, channelId); - - public override void GetStatsOut(out int numMsgs, out int numBufferedMsgs, out int numBytes, out int lastBufferedPerSecond) - { - numMsgs = 0; - numBufferedMsgs = 0; - numBytes = 0; - lastBufferedPerSecond = 0; - } - - public override void GetStatsIn(out int numMsgs, out int numBytes) - { - numMsgs = 0; - numBytes = 0; - } + public override bool SendWriter(QNetworkWriter writer, int channelId) + => m_LocalServer.InvokeBytes(this, writer.AsArray(), (short)writer.AsArray().Length, channelId); private readonly QNetworkServer m_LocalServer; } From 4f0aabd1c8862acbf2bdd9e070fd52bdff7ea22e Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sun, 29 Aug 2021 16:42:43 +0100 Subject: [PATCH 061/118] remove check for qsbnetworkmanager ready in getplayer --- QSB/Player/QSBPlayerManager.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/QSB/Player/QSBPlayerManager.cs b/QSB/Player/QSBPlayerManager.cs index fb66ebb2..cd77b94a 100644 --- a/QSB/Player/QSBPlayerManager.cs +++ b/QSB/Player/QSBPlayerManager.cs @@ -44,12 +44,6 @@ namespace QSB.Player public static PlayerInfo GetPlayer(uint id) { - if (!QSBNetworkManager.Instance.IsReady) - { - DebugLog.ToConsole($"Warning - GetPlayer() (id<{id}>) called when Network Manager not ready! Is a Player Sync Object still active? " + - $"{Environment.NewLine} Stacktrace :\r\n{Environment.StackTrace}", MessageType.Warning); - } - if (id == uint.MaxValue || id == 0U) { return default; From e34db1af144ef732d6e489e3f9b29354adc3620e Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sun, 29 Aug 2021 16:44:36 +0100 Subject: [PATCH 062/118] cleanup qnetworkidentity and qnetworkmanager --- QuantumUNET/Components/QNetworkIdentity.cs | 1 - QuantumUNET/Components/QNetworkManager.cs | 161 +-------------------- 2 files changed, 3 insertions(+), 159 deletions(-) diff --git a/QuantumUNET/Components/QNetworkIdentity.cs b/QuantumUNET/Components/QNetworkIdentity.cs index e70666ae..1ee300ff 100644 --- a/QuantumUNET/Components/QNetworkIdentity.cs +++ b/QuantumUNET/Components/QNetworkIdentity.cs @@ -815,7 +815,6 @@ namespace QuantumUNET.Components { QNetworkServer.Update(); QNetworkClient.UpdateClients(); - QNetworkManager.UpdateScene(); } [SerializeField] diff --git a/QuantumUNET/Components/QNetworkManager.cs b/QuantumUNET/Components/QNetworkManager.cs index a5fff98c..61aeab26 100644 --- a/QuantumUNET/Components/QNetworkManager.cs +++ b/QuantumUNET/Components/QNetworkManager.cs @@ -21,13 +21,9 @@ namespace QuantumUNET.Components public bool scriptCRCCheck { get; set; } = true; public bool autoCreatePlayer { get; set; } = true; public bool isNetworkActive; - public bool useWebSockets { get; set; } - public bool useSimulator { get; set; } public bool clientLoadedScene { get; set; } public string serverBindAddress { get; set; } = ""; public string networkAddress { get; set; } = "localhost"; - public string offlineScene { get; set; } = ""; - public string onlineScene { get; set; } = ""; public float packetLossPercentage { get; set; } public float maxDelay { get; set; } = 0.01f; public GameObject playerPrefab { get; set; } @@ -159,7 +155,6 @@ namespace QuantumUNET.Components } QNetworkCRC.scriptCRCCheck = scriptCRCCheck; - QNetworkServer.useWebSockets = useWebSockets; if (m_GlobalConfig != null) { NetworkTransport.Init(m_GlobalConfig); @@ -207,6 +202,7 @@ namespace QuantumUNET.Components { QNetworkServer.SpawnObjects(); } + QNetworkServer.SpawnObjects(); return true; } @@ -217,7 +213,6 @@ namespace QuantumUNET.Components client.RegisterHandler(QMsgType.Disconnect, OnClientDisconnectInternal); client.RegisterHandler(QMsgType.NotReady, OnClientNotReadyMessageInternal); client.RegisterHandler(QMsgType.Error, OnClientErrorInternal); - client.RegisterHandler(QMsgType.Scene, OnClientSceneInternal); if (playerPrefab != null) { QClientScene.RegisterPrefab(playerPrefab); @@ -232,35 +227,6 @@ namespace QuantumUNET.Components } } - public void UseExternalClient(QNetworkClient externalClient) - { - if (runInBackground) - { - Application.runInBackground = true; - } - - if (externalClient != null) - { - client = externalClient; - isNetworkActive = true; - RegisterClientMessages(client); - OnStartClient(client); - } - else - { - OnStopClient(); - QClientScene.DestroyAllClientObjects(); - QClientScene.HandleClientDisconnect(client.connection); - client = null; - if (!string.IsNullOrEmpty(offlineScene)) - { - ClientChangeScene(offlineScene, false); - } - } - - s_Address = networkAddress; - } - public QNetworkClient StartClient(ConnectionConfig config, int hostPort) { InitializeSingleton(); @@ -382,10 +348,6 @@ namespace QuantumUNET.Components QLog.Log("NetworkManager StopServer"); isNetworkActive = false; QNetworkServer.Shutdown(); - if (!string.IsNullOrEmpty(offlineScene)) - { - ServerChangeScene(offlineScene); - } CleanupNetworkIdentities(); } @@ -404,31 +366,10 @@ namespace QuantumUNET.Components } QClientScene.DestroyAllClientObjects(); - if (!string.IsNullOrEmpty(offlineScene)) - { - ClientChangeScene(offlineScene, false); - } CleanupNetworkIdentities(); } - public virtual void ServerChangeScene(string newSceneName) - { - if (string.IsNullOrEmpty(newSceneName)) - { - QLog.Error("ServerChangeScene empty scene name"); - } - else - { - QLog.Log($"ServerChangeScene {newSceneName}"); - QNetworkServer.SetAllClientsNotReady(); - networkSceneName = newSceneName; - s_LoadingSceneAsync = SceneManager.LoadSceneAsync(newSceneName); - var msg = new QStringMessage(networkSceneName); - QNetworkServer.SendToAll(39, msg); - } - } - private void CleanupNetworkIdentities() { foreach (var networkIdentity in Resources.FindObjectsOfTypeAll()) @@ -437,75 +378,6 @@ namespace QuantumUNET.Components } } - internal void ClientChangeScene(string newSceneName, bool forceReload) - { - if (string.IsNullOrEmpty(newSceneName)) - { - QLog.Error("ClientChangeScene empty scene name"); - } - else - { - QLog.Log($"ClientChangeScene newSceneName:{newSceneName} networkSceneName:{networkSceneName}"); - if (newSceneName == networkSceneName) - { - if (!forceReload) - { - FinishLoadScene(); - return; - } - } - - s_LoadingSceneAsync = SceneManager.LoadSceneAsync(newSceneName); - networkSceneName = newSceneName; - } - } - - private void FinishLoadScene() - { - if (client != null) - { - if (s_ClientReadyConnection != null) - { - clientLoadedScene = true; - OnClientConnect(s_ClientReadyConnection); - s_ClientReadyConnection = null; - } - } - else - { - QLog.Error("FinishLoadScene client is null"); - } - - if (QNetworkServer.active) - { - QNetworkServer.SpawnObjects(); - OnServerSceneChanged(networkSceneName); - } - - if (IsClientConnected() && client != null) - { - RegisterClientMessages(client); - OnClientSceneChanged(client.connection); - } - } - - internal static void UpdateScene() - { - if (!(singleton == null)) - { - if (s_LoadingSceneAsync != null) - { - if (s_LoadingSceneAsync.isDone) - { - QLog.Log($"ClientChangeScene done readyCon:{s_ClientReadyConnection}"); - singleton.FinishLoadScene(); - s_LoadingSceneAsync.allowSceneActivation = true; - s_LoadingSceneAsync = null; - } - } - } - } - public bool IsClientConnected() => client != null && client.isConnected; public static void Shutdown() @@ -538,12 +410,6 @@ namespace QuantumUNET.Components } } - if (networkSceneName != "" && networkSceneName != offlineScene) - { - var msg = new QStringMessage(networkSceneName); - netMsg.Connection.Send(39, msg); - } - OnServerConnect(netMsg.Connection); } @@ -595,24 +461,13 @@ namespace QuantumUNET.Components QLog.Log("NetworkManager:OnClientConnectInternal"); netMsg.Connection.SetMaxDelay(maxDelay); var name = SceneManager.GetSceneAt(0).name; - if (string.IsNullOrEmpty(onlineScene) || onlineScene == offlineScene || name == onlineScene) - { - clientLoadedScene = false; - OnClientConnect(netMsg.Connection); - } - else - { - s_ClientReadyConnection = netMsg.Connection; - } + clientLoadedScene = false; + OnClientConnect(netMsg.Connection); } internal void OnClientDisconnectInternal(QNetworkMessage netMsg) { QLog.Log("NetworkManager:OnClientDisconnectInternal"); - if (!string.IsNullOrEmpty(offlineScene)) - { - ClientChangeScene(offlineScene, false); - } OnClientDisconnect(netMsg.Connection); } @@ -631,16 +486,6 @@ namespace QuantumUNET.Components OnClientError(netMsg.Connection, s_ErrorMessage.errorCode); } - internal void OnClientSceneInternal(QNetworkMessage netMsg) - { - QLog.Log("NetworkManager:OnClientSceneInternal"); - var newSceneName = netMsg.Reader.ReadString(); - if (IsClientConnected() && !QNetworkServer.active) - { - ClientChangeScene(newSceneName, true); - } - } - public virtual void OnServerConnect(QNetworkConnection conn) { } From 6e964512db7ec4871db2dc2cc5bab9e989a1947c Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sun, 29 Aug 2021 16:45:00 +0100 Subject: [PATCH 063/118] cleanup more of qnetworkmanager --- QuantumUNET/Components/QNetworkManager.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/QuantumUNET/Components/QNetworkManager.cs b/QuantumUNET/Components/QNetworkManager.cs index 61aeab26..73a6c6d1 100644 --- a/QuantumUNET/Components/QNetworkManager.cs +++ b/QuantumUNET/Components/QNetworkManager.cs @@ -11,10 +11,8 @@ namespace QuantumUNET.Components public class QNetworkManager : MonoBehaviour { public static QNetworkManager singleton; - public static string networkSceneName = ""; public int networkPort { get; set; } = 7777; - public int simulatedLatency { get; set; } = 1; public bool serverBindToIP { get; set; } public bool dontDestroyOnLoad { get; set; } = true; public bool runInBackground { get; set; } = true; @@ -193,15 +191,6 @@ namespace QuantumUNET.Components RegisterServerMessages(); QLog.Log($"NetworkManager StartServer port:{networkPort}"); isNetworkActive = true; - var name = SceneManager.GetSceneAt(0).name; - if (!string.IsNullOrEmpty(onlineScene) && onlineScene != name && onlineScene != offlineScene) - { - ServerChangeScene(onlineScene); - } - else - { - QNetworkServer.SpawnObjects(); - } QNetworkServer.SpawnObjects(); return true; From 979fe21e9b9ad219d1d1dae863ce560deb62736a Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sun, 29 Aug 2021 16:45:35 +0100 Subject: [PATCH 064/118] cleanup qnetworkclient/qnetworkconnection --- QuantumUNET/QNetworkClient.cs | 22 ---------------------- QuantumUNET/QNetworkConnection.cs | 26 -------------------------- 2 files changed, 48 deletions(-) diff --git a/QuantumUNET/QNetworkClient.cs b/QuantumUNET/QNetworkClient.cs index 42cac309..292b84f1 100644 --- a/QuantumUNET/QNetworkClient.cs +++ b/QuantumUNET/QNetworkClient.cs @@ -509,28 +509,6 @@ namespace QuantumUNET } } - public void GetStatsOut(out int numMsgs, out int numBufferedMsgs, out int numBytes, out int lastBufferedPerSecond) - { - numMsgs = 0; - numBufferedMsgs = 0; - numBytes = 0; - lastBufferedPerSecond = 0; - if (m_Connection != null) - { - m_Connection.GetStatsOut(out numMsgs, out numBufferedMsgs, out numBytes, out lastBufferedPerSecond); - } - } - - public void GetStatsIn(out int numMsgs, out int numBytes) - { - numMsgs = 0; - numBytes = 0; - if (m_Connection != null) - { - m_Connection.GetStatsIn(out numMsgs, out numBytes); - } - } - public Dictionary GetConnectionStats() => m_Connection?.PacketStats; diff --git a/QuantumUNET/QNetworkConnection.cs b/QuantumUNET/QNetworkConnection.cs index 3d0c915d..a7cbfaa2 100644 --- a/QuantumUNET/QNetworkConnection.cs +++ b/QuantumUNET/QNetworkConnection.cs @@ -350,32 +350,6 @@ namespace QuantumUNET } } - public virtual void GetStatsOut(out int numMsgs, out int numBufferedMsgs, out int numBytes, out int lastBufferedPerSecond) - { - numMsgs = 0; - numBufferedMsgs = 0; - numBytes = 0; - lastBufferedPerSecond = 0; - foreach (var channelBuffer in m_Channels) - { - numMsgs += channelBuffer.NumMsgsOut; - numBufferedMsgs += channelBuffer.NumBufferedMsgsOut; - numBytes += channelBuffer.NumBytesOut; - lastBufferedPerSecond += channelBuffer.LastBufferedPerSecond; - } - } - - public virtual void GetStatsIn(out int numMsgs, out int numBytes) - { - numMsgs = 0; - numBytes = 0; - foreach (var channelBuffer in m_Channels) - { - numMsgs += channelBuffer.NumMsgsIn; - numBytes += channelBuffer.NumBytesIn; - } - } - public override string ToString() => $"hostId: {hostId} connectionId: {connectionId} isReady: {isReady} channel count: {m_Channels?.Length ?? 0}"; From dd4481e82071350b82863df43b29cd4fd28925e0 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sun, 29 Aug 2021 16:46:11 +0100 Subject: [PATCH 065/118] cleanup qnetworkscene --- QuantumUNET/QNetworkScene.cs | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/QuantumUNET/QNetworkScene.cs b/QuantumUNET/QNetworkScene.cs index 16ff0fe4..8704b830 100644 --- a/QuantumUNET/QNetworkScene.cs +++ b/QuantumUNET/QNetworkScene.cs @@ -8,11 +8,8 @@ namespace QuantumUNET internal class QNetworkScene { internal static Dictionary guidToPrefab { get; } = new Dictionary(); - internal static Dictionary spawnHandlers { get; } = new Dictionary(); - internal static Dictionary unspawnHandlers { get; } = new Dictionary(); - internal Dictionary localObjects { get; } = new Dictionary(); internal void Shutdown() @@ -76,7 +73,8 @@ namespace QuantumUNET return result; } - internal bool RemoveLocalObject(NetworkInstanceId netId) => localObjects.Remove(netId); + internal bool RemoveLocalObject(NetworkInstanceId netId) + => localObjects.Remove(netId); internal bool RemoveLocalObjectAndDestroy(NetworkInstanceId netId) { @@ -95,7 +93,8 @@ namespace QuantumUNET return result; } - internal void ClearLocalObjects() => localObjects.Clear(); + internal void ClearLocalObjects() + => localObjects.Clear(); internal static void RegisterPrefab(GameObject prefab, NetworkHash128 newAssetId) { @@ -272,16 +271,5 @@ namespace QuantumUNET ClearLocalObjects(); } - - internal void DumpAllClientObjects() - { - foreach (var networkInstanceId in localObjects.Keys) - { - var networkIdentity = localObjects[networkInstanceId]; - Debug.Log(networkIdentity != null - ? $"ID:{networkInstanceId} OBJ:{networkIdentity.gameObject} AS:{networkIdentity.AssetId}" - : $"ID:{networkInstanceId} OBJ: null"); - } - } } } \ No newline at end of file From e83a9219f1e5715d9edf7192432f7f855eadbf4d Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sun, 29 Aug 2021 16:46:33 +0100 Subject: [PATCH 066/118] cleanup qnetworkserver --- QuantumUNET/QNetworkServer.cs | 65 ----------------------------------- 1 file changed, 65 deletions(-) diff --git a/QuantumUNET/QNetworkServer.cs b/QuantumUNET/QNetworkServer.cs index 25bb25cb..df98093f 100644 --- a/QuantumUNET/QNetworkServer.cs +++ b/QuantumUNET/QNetworkServer.cs @@ -39,12 +39,6 @@ namespace QuantumUNET public static bool dontListen { get; set; } - public static bool useWebSockets - { - get => instance.m_SimpleServerSimple.useWebSockets; - set => instance.m_SimpleServerSimple.useWebSockets = value; - } - internal static QNetworkServer instance { get @@ -71,12 +65,6 @@ namespace QuantumUNET public static int numChannels => instance.m_SimpleServerSimple.hostTopology.DefaultConfig.ChannelCount; - public static float maxDelay - { - get => instance.m_MaxDelay; - set => instance.InternalSetMaxDelay(value); - } - public static Type networkConnectionClass => instance.m_SimpleServerSimple.networkConnectionClass; public static void SetNetworkConnectionClass() where T : QNetworkConnection => instance.m_SimpleServerSimple.SetNetworkConnectionClass(); @@ -140,15 +128,6 @@ namespace QuantumUNET } } - public static void ListenRelay(string relayIp, int relayPort, NetworkID netGuid, SourceID sourceId, NodeID nodeId) => instance.InternalListenRelay(relayIp, relayPort, netGuid, sourceId, nodeId); - - private void InternalListenRelay(string relayIp, int relayPort, NetworkID netGuid, SourceID sourceId, NodeID nodeId) - { - m_SimpleServerSimple.ListenRelay(relayIp, relayPort, netGuid, sourceId, nodeId); - active = true; - RegisterMessageHandlers(); - } - public static bool Listen(int serverPort) => instance.InternalListen(null, serverPort); public static bool Listen(string ipAddress, int serverPort) => instance.InternalListen(ipAddress, serverPort); @@ -170,16 +149,6 @@ namespace QuantumUNET return true; } - private void InternalSetMaxDelay(float seconds) - { - foreach (var networkConnection in connections) - { - networkConnection?.SetMaxDelay(seconds); - } - - m_MaxDelay = seconds; - } - internal int AddLocalClient(QLocalClient localClient) { int result; @@ -653,40 +622,6 @@ namespace QuantumUNET public static void ClearSpawners() => QNetworkScene.ClearSpawners(); - public static void GetStatsOut(out int numMsgs, out int numBufferedMsgs, out int numBytes, out int lastBufferedPerSecond) - { - numMsgs = 0; - numBufferedMsgs = 0; - numBytes = 0; - lastBufferedPerSecond = 0; - foreach (var networkConnection in connections) - { - if (networkConnection != null) - { - networkConnection.GetStatsOut(out var num, out var num2, out var num3, out var num4); - numMsgs += num; - numBufferedMsgs += num2; - numBytes += num3; - lastBufferedPerSecond += num4; - } - } - } - - public static void GetStatsIn(out int numMsgs, out int numBytes) - { - numMsgs = 0; - numBytes = 0; - foreach (var networkConnection in connections) - { - if (networkConnection != null) - { - networkConnection.GetStatsIn(out var num, out var num2); - numMsgs += num; - numBytes += num2; - } - } - } - public static void SendToClientOfPlayer(GameObject player, short msgType, QMessageBase msg) { foreach (var networkConnection in connections) From f3aa0d2efac389833e8178cdffbb8ac99e44bb6d Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sun, 29 Aug 2021 16:47:22 +0100 Subject: [PATCH 067/118] cleanup qnetworkserversimple --- QuantumUNET/QNetworkServerSimple.cs | 69 +++++++++++------------------ 1 file changed, 27 insertions(+), 42 deletions(-) diff --git a/QuantumUNET/QNetworkServerSimple.cs b/QuantumUNET/QNetworkServerSimple.cs index b72be52e..62beef3e 100644 --- a/QuantumUNET/QNetworkServerSimple.cs +++ b/QuantumUNET/QNetworkServerSimple.cs @@ -13,26 +13,18 @@ namespace QuantumUNET public class QNetworkServerSimple { public QNetworkServerSimple() => connections = new ReadOnlyCollection(m_Connections); - public int listenPort { get; set; } - public int serverHostId { get; set; } = -1; - public HostTopology hostTopology { get; private set; } - - public bool useWebSockets { get; set; } - public ReadOnlyCollection connections { get; } - public Dictionary handlers => m_MessageHandlers.GetHandlers(); - public byte[] messageBuffer { get; private set; } - public NetworkReader messageReader { get; private set; } - public Type networkConnectionClass { get; private set; } = typeof(QNetworkConnection); - public void SetNetworkConnectionClass() where T : QNetworkConnection => networkConnectionClass = typeof(T); + public void SetNetworkConnectionClass() + where T : QNetworkConnection + => networkConnectionClass = typeof(T); public virtual void Initialize() { @@ -70,9 +62,7 @@ namespace QuantumUNET { Initialize(); listenPort = serverListenPort; - serverHostId = useWebSockets - ? NetworkTransport.AddWebsocketHost(hostTopology, serverListenPort, ipAddress) - : NetworkTransport.AddHost(hostTopology, serverListenPort, ipAddress); + serverHostId = NetworkTransport.AddHost(hostTopology, serverListenPort, ipAddress); bool result; if (serverHostId == -1) { @@ -87,16 +77,15 @@ namespace QuantumUNET return result; } - public bool Listen(int serverListenPort) => Listen(serverListenPort, hostTopology); + public bool Listen(int serverListenPort) + => Listen(serverListenPort, hostTopology); public bool Listen(int serverListenPort, HostTopology topology) { hostTopology = topology; Initialize(); listenPort = serverListenPort; - serverHostId = useWebSockets - ? NetworkTransport.AddWebsocketHost(hostTopology, serverListenPort) - : NetworkTransport.AddHost(hostTopology, serverListenPort); + serverHostId = NetworkTransport.AddHost(hostTopology, serverListenPort); bool result; if (serverHostId == -1) { @@ -111,17 +100,6 @@ namespace QuantumUNET return result; } - public void ListenRelay(string relayIp, int relayPort, NetworkID netGuid, SourceID sourceId, NodeID nodeId) - { - Initialize(); - serverHostId = NetworkTransport.AddHost(hostTopology, listenPort); - Debug.Log($"Server Host Slot Id: {serverHostId}"); - Update(); - NetworkTransport.ConnectAsNetworkHost(serverHostId, relayIp, relayPort, netGuid, sourceId, nodeId, out var b); - m_RelaySlotId = 0; - Debug.Log($"Relay Slot Id: {m_RelaySlotId}"); - } - public void Stop() { Debug.Log("NetworkServerSimple stop "); @@ -129,13 +107,17 @@ namespace QuantumUNET serverHostId = -1; } - internal void RegisterHandlerSafe(short msgType, QNetworkMessageDelegate handler) => m_MessageHandlers.RegisterHandlerSafe(msgType, handler); + internal void RegisterHandlerSafe(short msgType, QNetworkMessageDelegate handler) + => m_MessageHandlers.RegisterHandlerSafe(msgType, handler); - public void RegisterHandler(short msgType, QNetworkMessageDelegate handler) => m_MessageHandlers.RegisterHandler(msgType, handler); + public void RegisterHandler(short msgType, QNetworkMessageDelegate handler) + => m_MessageHandlers.RegisterHandler(msgType, handler); - public void UnregisterHandler(short msgType) => m_MessageHandlers.UnregisterHandler(msgType); + public void UnregisterHandler(short msgType) + => m_MessageHandlers.UnregisterHandler(msgType); - public void ClearHandlers() => m_MessageHandlers.ClearMessageHandlers(); + public void ClearHandlers() + => m_MessageHandlers.ClearMessageHandlers(); public void UpdateConnections() { @@ -360,20 +342,23 @@ namespace QuantumUNET } } - public virtual void OnConnectError(int connectionId, byte error) => Debug.LogError( - $"OnConnectError error:{error}"); + public virtual void OnConnectError(int connectionId, byte error) + => Debug.LogError($"OnConnectError error:{error}"); - public virtual void OnDataError(QNetworkConnection conn, byte error) => Debug.LogError( - $"OnDataError error:{error}"); + public virtual void OnDataError(QNetworkConnection conn, byte error) + => Debug.LogError($"OnDataError error:{error}"); - public virtual void OnDisconnectError(QNetworkConnection conn, byte error) => Debug.LogError( - $"OnDisconnectError error:{error}"); + public virtual void OnDisconnectError(QNetworkConnection conn, byte error) + => Debug.LogError($"OnDisconnectError error:{error}"); - public virtual void OnConnected(QNetworkConnection conn) => conn.InvokeHandlerNoData(32); + public virtual void OnConnected(QNetworkConnection conn) + => conn.InvokeHandlerNoData(32); - public virtual void OnDisconnected(QNetworkConnection conn) => conn.InvokeHandlerNoData(33); + public virtual void OnDisconnected(QNetworkConnection conn) + => conn.InvokeHandlerNoData(33); - public virtual void OnData(QNetworkConnection conn, int receivedSize, int channelId) => conn.TransportReceive(messageBuffer, receivedSize, channelId); + public virtual void OnData(QNetworkConnection conn, int receivedSize, int channelId) + => conn.TransportReceive(messageBuffer, receivedSize, channelId); private bool m_Initialized; private int m_RelaySlotId = -1; From 9fa543ff99988a82fbe52be796936e16acfe62ec Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sun, 29 Aug 2021 16:47:52 +0100 Subject: [PATCH 068/118] change _storedTransformSyncs to have uint instead of PlayerInfo --- QSB/Syncs/SyncBase.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/QSB/Syncs/SyncBase.cs b/QSB/Syncs/SyncBase.cs index 5a55267c..36708c2d 100644 --- a/QSB/Syncs/SyncBase.cs +++ b/QSB/Syncs/SyncBase.cs @@ -18,12 +18,12 @@ namespace QSB.Syncs public abstract class SyncBase : QNetworkTransform { - private static readonly Dictionary> _storedTransformSyncs = new Dictionary>(); + private static readonly Dictionary> _storedTransformSyncs = new Dictionary>(); public static T GetPlayers(PlayerInfo player) where T : SyncBase { - var dictOfOwnedSyncs = _storedTransformSyncs[player]; + var dictOfOwnedSyncs = _storedTransformSyncs[player.PlayerId]; var wantedSync = dictOfOwnedSyncs[typeof(T)]; if (wantedSync == default) { @@ -114,12 +114,12 @@ namespace QSB.Syncs return; } - if (!_storedTransformSyncs.ContainsKey(Player)) + if (!_storedTransformSyncs.ContainsKey(PlayerId)) { - _storedTransformSyncs.Add(Player, new Dictionary()); + _storedTransformSyncs.Add(PlayerId, new Dictionary()); } - var playerDict = _storedTransformSyncs[Player]; + var playerDict = _storedTransformSyncs[PlayerId]; playerDict[GetType()] = this; } @@ -140,7 +140,7 @@ namespace QSB.Syncs return; } - var playerDict = _storedTransformSyncs[Player]; + var playerDict = _storedTransformSyncs[PlayerId]; playerDict.Remove(GetType()); } From 609bda1a50649a26652a505564c57543ae681954 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 2 Sep 2021 11:23:48 +0100 Subject: [PATCH 069/118] remove some old logs --- QSB/Menus/MenuManager.cs | 2 -- QSB/Player/Events/EnterLeaveEvent.cs | 1 - QSB/Player/TransformSync/PlayerTransformSync.cs | 1 - QSB/SectorSync/SectorSync.cs | 1 - 4 files changed, 5 deletions(-) diff --git a/QSB/Menus/MenuManager.cs b/QSB/Menus/MenuManager.cs index c22548da..e395c14f 100644 --- a/QSB/Menus/MenuManager.cs +++ b/QSB/Menus/MenuManager.cs @@ -139,8 +139,6 @@ namespace QSB.Menus private void OnConnected() { - DebugLog.DebugWrite($"ON CONNECTED"); - var text = QSBCore.IsHost ? "STOP HOSTING" : "DISCONNECT"; diff --git a/QSB/Player/Events/EnterLeaveEvent.cs b/QSB/Player/Events/EnterLeaveEvent.cs index c92eb975..ccd7c639 100644 --- a/QSB/Player/Events/EnterLeaveEvent.cs +++ b/QSB/Player/Events/EnterLeaveEvent.cs @@ -49,7 +49,6 @@ namespace QSB.Player.Events public override void OnReceiveRemote(bool server, EnumWorldObjectMessage message) { var player = QSBPlayerManager.GetPlayer(message.FromId); - DebugLog.DebugWrite($"{message.FromId} {message.EnumValue}", OWML.Common.MessageType.Info); switch (message.EnumValue) { case EnterLeaveType.EnterMoon: diff --git a/QSB/Player/TransformSync/PlayerTransformSync.cs b/QSB/Player/TransformSync/PlayerTransformSync.cs index 38cb4f9e..8d117c4e 100644 --- a/QSB/Player/TransformSync/PlayerTransformSync.cs +++ b/QSB/Player/TransformSync/PlayerTransformSync.cs @@ -108,7 +108,6 @@ namespace QSB.Player.TransformSync _visibleStickPivot = pivot; _visibleStickTip = pivot.Find("Stick_Tip"); - DebugLog.DebugWrite("PlayerTransformSync init done - Request state!"); QSBEventManager.FireEvent(EventNames.QSBRequestStateResync); return player; diff --git a/QSB/SectorSync/SectorSync.cs b/QSB/SectorSync/SectorSync.cs index fa86b488..b1219248 100644 --- a/QSB/SectorSync/SectorSync.cs +++ b/QSB/SectorSync/SectorSync.cs @@ -32,7 +32,6 @@ namespace QSB.SectorSync public void Init(SectorDetector detector, TargetType type) { - DebugLog.DebugWrite($"INIT SECTOR SYNC detector:{detector.name}"); if (_sectorDetector != null) { _sectorDetector.OnEnterSector -= AddSector; From bba830c144a5ad1d98ee2ea392e6816a5c72ed3e Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 2 Sep 2021 11:24:13 +0100 Subject: [PATCH 070/118] clarify some error logs --- QSB/QuantumSync/QuantumManager.cs | 2 +- QSB/SectorSync/SectorSync.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/QSB/QuantumSync/QuantumManager.cs b/QSB/QuantumSync/QuantumManager.cs index 422acc48..aa8a5d30 100644 --- a/QSB/QuantumSync/QuantumManager.cs +++ b/QSB/QuantumSync/QuantumManager.cs @@ -87,7 +87,7 @@ namespace QSB.QuantumSync var playersWithCameras = QSBPlayerManager.GetPlayersWithCameras(!ignoreLocalCamera); if (playersWithCameras.Count == 0) { - DebugLog.ToConsole($"Warning - Trying to run IsVisibleUsingCameraFrustum when there are no players!", MessageType.Warning); + DebugLog.ToConsole($"Warning - Could not find any players with cameras!", MessageType.Warning); return new Tuple>(false, new List()); } diff --git a/QSB/SectorSync/SectorSync.cs b/QSB/SectorSync/SectorSync.cs index b1219248..457e6421 100644 --- a/QSB/SectorSync/SectorSync.cs +++ b/QSB/SectorSync/SectorSync.cs @@ -120,7 +120,7 @@ namespace QSB.SectorSync if (!IsReady) { - DebugLog.ToConsole($"Warning - Tried to use GetClosestSector before it was initialized. Transform:{trans.name} Stacktrace:{Environment.StackTrace}", MessageType.Warning); + DebugLog.ToConsole($"Warning - Tried to use GetClosestSector() before this SectorSync is ready. Transform:{trans.name} Stacktrace:\r\n{Environment.StackTrace}", MessageType.Warning); return null; } From f5cad9f0cd2368bf5a1291d26a472171e79ae5c1 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 2 Sep 2021 11:24:54 +0100 Subject: [PATCH 071/118] add error checks to GetPlayersWithCameras --- QSB/Player/QSBPlayerManager.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/QSB/Player/QSBPlayerManager.cs b/QSB/Player/QSBPlayerManager.cs index cd77b94a..0c262511 100644 --- a/QSB/Player/QSBPlayerManager.cs +++ b/QSB/Player/QSBPlayerManager.cs @@ -113,6 +113,16 @@ namespace QSB.Player { cameraList.Add(LocalPlayer); } + else if (includeLocalCamera && (LocalPlayer == default || LocalPlayer.Camera == null)) + { + if (LocalPlayer == default) + { + DebugLog.ToConsole($"Error - LocalPlayer is null.", MessageType.Error); + return cameraList; + } + + DebugLog.DebugWrite($"Error - LocalPlayer.Camera is null.", MessageType.Error); + } return cameraList; } From 2b2d6c2814259aebae5dd1f1d35e952fb7c6ecc1 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 2 Sep 2021 11:25:07 +0100 Subject: [PATCH 072/118] disable meditation stopping (for now) --- QSB/TimeSync/PreserveTimeScale.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/QSB/TimeSync/PreserveTimeScale.cs b/QSB/TimeSync/PreserveTimeScale.cs index 23b8d1bb..9b9f8bcb 100644 --- a/QSB/TimeSync/PreserveTimeScale.cs +++ b/QSB/TimeSync/PreserveTimeScale.cs @@ -7,7 +7,8 @@ namespace QSB.TimeSync { public void Start() { - QSBCore.Helper.Menus.PauseMenu.GetTitleButton("Button-EndCurrentLoop").Hide(); // Remove the meditation button + // BUG : Get this working for the new menu system. Can't use OWML's anymore. + //QSBCore.Helper.Menus.PauseMenu.GetTitleButton("Button-EndCurrentLoop").Hide(); // Remove the meditation button // Allow server to sleep at campfires if (IsServer) From 265be6ba98830979f28870e47c7ce511721dc09e Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 2 Sep 2021 11:25:41 +0100 Subject: [PATCH 073/118] don't try using reference transform in updatetransform if it's null --- .../TransformSync/PlayerProbeSync.cs | 12 ++++++++-- .../TransformSync/ShipTransformSync.cs | 22 ++++++++++++++----- .../Transforms/SectoredTransformSync.cs | 13 +++++++++-- 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/QSB/ProbeSync/TransformSync/PlayerProbeSync.cs b/QSB/ProbeSync/TransformSync/PlayerProbeSync.cs index 31e29038..96d95f0c 100644 --- a/QSB/ProbeSync/TransformSync/PlayerProbeSync.cs +++ b/QSB/ProbeSync/TransformSync/PlayerProbeSync.cs @@ -88,8 +88,16 @@ namespace QSB.ProbeSync.TransformSync probeOWRigidbody.SetPosition(launcherTransform.position); probeOWRigidbody.SetRotation(launcherTransform.rotation); - _intermediaryTransform.EncodePosition(AttachedObject.transform.position); - _intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation); + if (ReferenceTransform != null) + { + _intermediaryTransform.EncodePosition(AttachedObject.transform.position); + _intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation); + } + else + { + _intermediaryTransform.SetPosition(Vector3.zero); + _intermediaryTransform.SetRotation(Quaternion.identity); + } var currentReferenceSector = ReferenceSector; var playerReferenceSector = Player.TransformSync.ReferenceSector; diff --git a/QSB/ShipSync/TransformSync/ShipTransformSync.cs b/QSB/ShipSync/TransformSync/ShipTransformSync.cs index 3d827d72..6f0c2293 100644 --- a/QSB/ShipSync/TransformSync/ShipTransformSync.cs +++ b/QSB/ShipSync/TransformSync/ShipTransformSync.cs @@ -2,6 +2,7 @@ using QSB.SectorSync; using QSB.Syncs.Sectored.Rigidbodies; using QSB.Utility; using QSB.WorldSync; +using UnityEngine; namespace QSB.ShipSync.TransformSync { @@ -40,11 +41,22 @@ namespace QSB.ShipSync.TransformSync { if (HasAuthority) { - _intermediaryTransform.EncodePosition(AttachedObject.transform.position); - _intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation); - _relativeVelocity = GetRelativeVelocity(); - _relativeAngularVelocity = (AttachedObject as OWRigidbody).GetRelativeAngularVelocity(ReferenceTransform.GetAttachedOWRigidbody()); - return true; + if (ReferenceTransform != null) + { + _intermediaryTransform.EncodePosition(AttachedObject.transform.position); + _intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation); + _relativeVelocity = GetRelativeVelocity(); + _relativeAngularVelocity = (AttachedObject as OWRigidbody).GetRelativeAngularVelocity(ReferenceTransform.GetAttachedOWRigidbody()); + return true; + } + else + { + _intermediaryTransform.SetPosition(Vector3.zero); + _intermediaryTransform.SetRotation(Quaternion.identity); + _relativeVelocity = Vector3.zero; + _relativeAngularVelocity = Vector3.zero; + return true; + } } _updateCount++; diff --git a/QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs b/QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs index f6412e14..5ed974c7 100644 --- a/QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs +++ b/QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs @@ -69,8 +69,17 @@ namespace QSB.Syncs.Sectored.Transforms if (HasAuthority) { - _intermediaryTransform.EncodePosition(AttachedObject.transform.position); - _intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation); + if (ReferenceTransform != null) + { + _intermediaryTransform.EncodePosition(AttachedObject.transform.position); + _intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation); + } + else + { + _intermediaryTransform.SetPosition(Vector3.zero); + _intermediaryTransform.SetRotation(Quaternion.identity); + } + return true; } From 6aad05e84232868d7b35e25f94ff1aba87662d7c Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 2 Sep 2021 11:26:04 +0100 Subject: [PATCH 074/118] stop trying to get closest sector if sectorsync isnt ready --- QSB/Syncs/Sectored/BaseSectoredSync.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/QSB/Syncs/Sectored/BaseSectoredSync.cs b/QSB/Syncs/Sectored/BaseSectoredSync.cs index f7c11919..e7d865de 100644 --- a/QSB/Syncs/Sectored/BaseSectoredSync.cs +++ b/QSB/Syncs/Sectored/BaseSectoredSync.cs @@ -174,7 +174,7 @@ namespace QSB.Syncs.Sectored OWML.Common.MessageType.Warning); } - DebugLog.DebugWrite($"[BaseSectoredSync] {_logName} : Sector Manager not ready."); + DebugLog.DebugWrite($"{_logName} : Sector Manager not ready."); return false; } @@ -183,7 +183,7 @@ namespace QSB.Syncs.Sectored return true; } - if (referenceNull) + if (referenceNull && SectorSync.IsReady) { var closestSector = SectorSync.GetClosestSector(AttachedObject.transform); if (closestSector != null) @@ -195,10 +195,10 @@ namespace QSB.Syncs.Sectored if (SectorSync.IsReady) { DebugLog.ToConsole($"Error - No closest sector found to {PlayerId}.{GetType().Name}!", OWML.Common.MessageType.Error); + return false; } - DebugLog.DebugWrite($"[BaseSectoredSync] {_logName} : No sector found."); - return false; + return true; } } From 742a3d2a175821e2a3563b5dffae712a4b79d589 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 4 Sep 2021 13:15:31 +0100 Subject: [PATCH 075/118] fixed player conversation text being too high --- QSB/ConversationSync/ConversationManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QSB/ConversationSync/ConversationManager.cs b/QSB/ConversationSync/ConversationManager.cs index bd00586f..3171b3f8 100644 --- a/QSB/ConversationSync/ConversationManager.cs +++ b/QSB/ConversationSync/ConversationManager.cs @@ -91,7 +91,7 @@ namespace QSB.ConversationSync Destroy(playerBox); } - QSBPlayerManager.GetPlayer(playerId).CurrentDialogueBox = CreateBox(player.Body.transform, 25, text); + QSBPlayerManager.GetPlayer(playerId).CurrentDialogueBox = CreateBox(player.Body.transform, 2, text); } public void DisplayCharacterConversationBox(int index, string text) From d16b06107848fad06086ce8218a39ca968f8f34e Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 4 Sep 2021 13:15:48 +0100 Subject: [PATCH 076/118] fixed player holograms floating above pools --- QSB/PoolSync/CustomNomaiRemoteCameraPlatform.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/QSB/PoolSync/CustomNomaiRemoteCameraPlatform.cs b/QSB/PoolSync/CustomNomaiRemoteCameraPlatform.cs index d1116c38..d7ac3218 100644 --- a/QSB/PoolSync/CustomNomaiRemoteCameraPlatform.cs +++ b/QSB/PoolSync/CustomNomaiRemoteCameraPlatform.cs @@ -360,7 +360,8 @@ namespace QSB.PoolSync continue; } - var hologram = item.Value.transform.GetChild(0); + //var hologram = item.Value.transform.GetChild(0); + var hologram = item.Value.transform; hologram.position = TransformPoint(item.Key.Body.transform.position, this, _slavePlatform); hologram.rotation = TransformRotation(item.Key.Body.transform.rotation, this, _slavePlatform); } From ae18276fa46481fda1c79894038a786d14389707 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sun, 5 Sep 2021 13:12:42 +0100 Subject: [PATCH 077/118] change some ints into their QMsgTypes --- QuantumUNET/QNetworkClient.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/QuantumUNET/QNetworkClient.cs b/QuantumUNET/QNetworkClient.cs index 292b84f1..2bae1a80 100644 --- a/QuantumUNET/QNetworkClient.cs +++ b/QuantumUNET/QNetworkClient.cs @@ -408,7 +408,7 @@ namespace QuantumUNET } m_AsyncConnect = ConnectState.Connected; - m_Connection.InvokeHandlerNoData(32); + m_Connection.InvokeHandlerNoData(QMsgType.Connect); break; case NetworkEventType.DisconnectEvent: @@ -423,7 +423,7 @@ namespace QuantumUNET } QClientScene.HandleClientDisconnect(m_Connection); - m_Connection?.InvokeHandlerNoData(33); + m_Connection?.InvokeHandlerNoData(QMsgType.Disconnect); break; case NetworkEventType.Nothing: @@ -487,8 +487,8 @@ namespace QuantumUNET private void GenerateError(int error) { - var handler = m_MessageHandlers.GetHandler(34) - ?? m_MessageHandlers.GetHandler(34); + var handler = m_MessageHandlers.GetHandler(QMsgType.Error) + ?? m_MessageHandlers.GetHandler(QMsgType.Error); if (handler != null) { var errorMessage = new QErrorMessage @@ -501,7 +501,7 @@ namespace QuantumUNET var reader = new QNetworkReader(buffer); handler(new QNetworkMessage { - MsgType = 34, + MsgType = QMsgType.Error, Reader = reader, Connection = m_Connection, ChannelId = 0 From 20c27cfd103ad1107dcdd918a6a442af72361d3e Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sun, 5 Sep 2021 13:13:06 +0100 Subject: [PATCH 078/118] added support for more general client errors (like dns faliures) --- QSB/Menus/MenuManager.cs | 30 ++++++++++++++++++++++++++++-- QSB/QSBNetworkManager.cs | 4 ++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/QSB/Menus/MenuManager.cs b/QSB/Menus/MenuManager.cs index e395c14f..4caddabc 100644 --- a/QSB/Menus/MenuManager.cs +++ b/QSB/Menus/MenuManager.cs @@ -25,6 +25,7 @@ namespace QSB.Menus QSBSceneManager.OnSceneLoaded += OnSceneLoaded; QSBNetworkManager.Instance.OnClientConnected += OnConnected; QSBNetworkManager.Instance.OnClientDisconnected += OnDisconnected; + QSBNetworkManager.Instance.OnClientErrorThrown += OnClientError; } void OnSceneLoaded(OWScene oldScene, OWScene newScene, bool isUniverse) @@ -182,10 +183,10 @@ namespace QSB.Menus switch (error) { case NetworkError.Timeout: - text = "Connection timed out. Either the server does not exist, or it has stopped responding."; + text = "Client disconnected with error!\r\nConnection timed out."; break; default: - text = $"Disconnected due to error. NetworkError:{error}"; + text = $"Client disconnected with error!\r\nNetworkError:{error}"; break; } @@ -195,5 +196,30 @@ namespace QSB.Menus ClientButton.SetActive(true); HostButton.gameObject.SetActive(true); } + + private void OnClientError(NetworkError error) + { + if (error == NetworkError.Ok) + { + // lol wut + return; + } + + string text; + switch (error) + { + 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); + break; + default: + text = $"Internal QNet client error!\n\nNetworkError:{error}"; + break; + } + + OpenInfoPopup(text, "OK"); + } } } \ No newline at end of file diff --git a/QSB/QSBNetworkManager.cs b/QSB/QSBNetworkManager.cs index c574e63d..9f3fdc71 100644 --- a/QSB/QSBNetworkManager.cs +++ b/QSB/QSBNetworkManager.cs @@ -32,6 +32,7 @@ namespace QSB 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; } @@ -154,6 +155,9 @@ namespace QSB 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); From 08b1f5bdd13b881e1907c88a8b6969b32d75201c Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Wed, 8 Sep 2021 09:29:48 +0100 Subject: [PATCH 079/118] make pause menu show correct buttons on scene load --- QSB/Menus/MenuManager.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/QSB/Menus/MenuManager.cs b/QSB/Menus/MenuManager.cs index 4caddabc..d378fcfe 100644 --- a/QSB/Menus/MenuManager.cs +++ b/QSB/Menus/MenuManager.cs @@ -91,8 +91,20 @@ namespace QSB.Menus DisconnectButton = MenuApi.PauseMenu_MakeSimpleButton("DISCONNECT"); DisconnectButton.onClick.AddListener(Disconnect); - DisconnectButton.gameObject.SetActive(false); - DisconnectButton.GetComponent().alpha = 1f; + + + if (QSBCore.IsInMultiplayer) + { + ClientButton.SetActive(false); + HostButton.gameObject.SetActive(false); + DisconnectButton.gameObject.SetActive(true); + DisconnectButton.GetComponent().alpha = 1f; + } + else + { + DisconnectButton.gameObject.SetActive(false); + DisconnectButton.GetComponent().alpha = 1f; + } } private void MakeTitleMenus() From 6b51347644626b5c0f3938eafc516d70683cbddc Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Wed, 8 Sep 2021 09:30:10 +0100 Subject: [PATCH 080/118] Update default-config.json --- QSB/default-config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QSB/default-config.json b/QSB/default-config.json index c8b873bd..fedee1d0 100644 --- a/QSB/default-config.json +++ b/QSB/default-config.json @@ -4,6 +4,6 @@ "defaultServerIP": "localhost", "port": 7777, "debugMode": true, - "showLinesInDebug": true + "showLinesInDebug": false } } \ No newline at end of file From 913ec6e676ac57f2e422edfdf7a814ee63009868 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Mon, 13 Sep 2021 15:07:36 +0100 Subject: [PATCH 081/118] fixed ship ignoring when UpdateTransform had failed - and added check to stop getting owrigidbody from null reference --- QSB/ShipSync/TransformSync/ShipTransformSync.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/QSB/ShipSync/TransformSync/ShipTransformSync.cs b/QSB/ShipSync/TransformSync/ShipTransformSync.cs index 6f0c2293..b9c7b312 100644 --- a/QSB/ShipSync/TransformSync/ShipTransformSync.cs +++ b/QSB/ShipSync/TransformSync/ShipTransformSync.cs @@ -39,6 +39,11 @@ namespace QSB.ShipSync.TransformSync protected override bool UpdateTransform() { + if (!base.UpdateTransform()) + { + return false; + } + if (HasAuthority) { if (ReferenceTransform != null) @@ -67,6 +72,11 @@ namespace QSB.ShipSync.TransformSync ForcePosition(); } + if (ReferenceTransform == null) + { + return true; + } + var targetPos = _intermediaryTransform.GetTargetPosition_Unparented(); var targetVelocity = ReferenceTransform.GetAttachedOWRigidbody().GetPointVelocity(targetPos) + _relativeVelocity; From d6b5cadeac33fb2be7ed542fe9fbd74cb6c06ec1 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Mon, 13 Sep 2021 19:21:40 +0100 Subject: [PATCH 082/118] remove "updatetransform fail" message --- QSB/Syncs/SyncBase.cs | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/QSB/Syncs/SyncBase.cs b/QSB/Syncs/SyncBase.cs index 36708c2d..5171b2cf 100644 --- a/QSB/Syncs/SyncBase.cs +++ b/QSB/Syncs/SyncBase.cs @@ -223,28 +223,7 @@ namespace QSB.Syncs return; } - var state = UpdateTransform(); - if (!state) - { - DebugLog.ToConsole($"{_logName} UpdateTransform() fail.", MessageType.Error); - base.Update(); - return; - } - - /* - var expectedPosition = _intermediaryTransform.GetTargetPosition_Unparented(); - var actualPosition = AttachedObject.transform.position; - var distance = Vector3.Distance(expectedPosition, actualPosition); - if (distance > 20) - { - var intermediaryReference = _intermediaryTransform.GetReferenceTransform(); - DebugLog.ToConsole($"Warning - {_logName}'s AttachedObject ({AttachedObject?.name}) is far away from it's expected position! Info:" + - $"\r\n AttachedObject's parent : {AttachedObject?.transform.parent?.name}" + - $"\r\n Distance : {distance}" + - $"\r\n ReferenceTransform : {(ReferenceTransform == null ? "NULL" : ReferenceTransform.name)}" + - $"\r\n Intermediary's ReferenceTransform : {(intermediaryReference == null ? "NULL" : intermediaryReference.name)}", MessageType.Warning); - } - */ + UpdateTransform(); base.Update(); } From 56aca4179fa6660c65f09543ee510d6c308df962 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Mon, 13 Sep 2021 19:21:54 +0100 Subject: [PATCH 083/118] add stacktraces to intermediarytransform --- QSB/Syncs/IntermediaryTransform.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/QSB/Syncs/IntermediaryTransform.cs b/QSB/Syncs/IntermediaryTransform.cs index fbd1dda0..1db9e142 100644 --- a/QSB/Syncs/IntermediaryTransform.cs +++ b/QSB/Syncs/IntermediaryTransform.cs @@ -1,5 +1,6 @@ using OWML.Common; using QSB.Utility; +using System; using System.Reflection; using UnityEngine; @@ -58,7 +59,7 @@ namespace QSB.Syncs { if (_referenceTransform == null) { - DebugLog.ToConsole($"Error - _referenceTransform has not been set for {_attachedTransform.name} ({MethodBase.GetCurrentMethod().Name})", MessageType.Error); + DebugLog.ToConsole($"Error - _referenceTransform has not been set for {_attachedTransform.name} \r\n{Environment.StackTrace}", MessageType.Error); return; } @@ -73,7 +74,7 @@ namespace QSB.Syncs { if (_referenceTransform == null) { - DebugLog.ToConsole($"Error - _referenceTransform has not been set for {_attachedTransform.name} ({MethodBase.GetCurrentMethod().Name})", MessageType.Error); + DebugLog.ToConsole($"Error - _referenceTransform has not been set for {_attachedTransform.name} \r\n{Environment.StackTrace}", MessageType.Error); return; } @@ -99,7 +100,7 @@ namespace QSB.Syncs { if (_referenceTransform == null) { - DebugLog.ToConsole($"Error - _referenceTransform has not been set for {_attachedTransform.name} ({MethodBase.GetCurrentMethod().Name})", MessageType.Error); + DebugLog.ToConsole($"Error - _referenceTransform has not been set for {_attachedTransform.name} \r\n{Environment.StackTrace}", MessageType.Error); return Vector3.zero; } @@ -113,7 +114,7 @@ namespace QSB.Syncs { if (_referenceTransform == null) { - DebugLog.ToConsole($"Error - _referenceTransform has not been set for {_attachedTransform.name} ({MethodBase.GetCurrentMethod().Name})", MessageType.Error); + DebugLog.ToConsole($"Error - _referenceTransform has not been set for {_attachedTransform.name} \r\n{Environment.StackTrace}", MessageType.Error); return Quaternion.identity; } From 178dea07229e9bc67bf509b70b133f9552ee750a Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Mon, 13 Sep 2021 19:22:07 +0100 Subject: [PATCH 084/118] remove some debug logs --- QSB/Player/Events/RequestStateResyncEvent.cs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/QSB/Player/Events/RequestStateResyncEvent.cs b/QSB/Player/Events/RequestStateResyncEvent.cs index 523be17c..9350e835 100644 --- a/QSB/Player/Events/RequestStateResyncEvent.cs +++ b/QSB/Player/Events/RequestStateResyncEvent.cs @@ -20,11 +20,7 @@ namespace QSB.Player.Events public override void SetupListener() => GlobalMessenger.AddListener(EventNames.QSBRequestStateResync, Handler); public override void CloseListener() => GlobalMessenger.RemoveListener(EventNames.QSBRequestStateResync, Handler); - private void Handler() - { - DebugLog.DebugWrite($"Sending QSBRequestStateResync"); - SendEvent(CreateMessage()); - } + private void Handler() => SendEvent(CreateMessage()); private PlayerMessage CreateMessage() => new PlayerMessage { @@ -33,16 +29,11 @@ namespace QSB.Player.Events public override void OnReceiveRemote(bool isHost, PlayerMessage message) { - DebugLog.DebugWrite($"OnReceiveRemote RequestStateResyncEvent"); - // if host, send worldobject and server states if (isHost) { - DebugLog.DebugWrite($"SENDING SERVER STATE"); QSBEventManager.FireEvent(EventNames.QSBServerState, ServerStateManager.Instance.GetServerState()); - - DebugLog.DebugWrite($"SENDING PLAYER INFORMATION"); QSBEventManager.FireEvent(EventNames.QSBPlayerInformation); SendWorldObjectInfo(); @@ -51,15 +42,11 @@ namespace QSB.Player.Events } // if client, send player and client states - - DebugLog.DebugWrite($"SENDING PLAYER INFORMATION"); QSBEventManager.FireEvent(EventNames.QSBPlayerInformation); } private void SendWorldObjectInfo() { - DebugLog.DebugWrite($"SENDING WORLDOBJECT INFORMATION"); - QSBWorldSync.DialogueConditions.ForEach(condition => QSBEventManager.FireEvent(EventNames.DialogueCondition, condition.Key, condition.Value)); From 0611286301ce8f7993c46b18b285613468132add Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Mon, 13 Sep 2021 19:22:39 +0100 Subject: [PATCH 085/118] extract out some code in basesectoredsync and sectoredrigidbodysync --- QSB/Syncs/Sectored/BaseSectoredSync.cs | 28 +++++++++++-------- .../Rigidbodies/SectoredRigidbodySync.cs | 13 ++++++--- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/QSB/Syncs/Sectored/BaseSectoredSync.cs b/QSB/Syncs/Sectored/BaseSectoredSync.cs index e7d865de..f52a6239 100644 --- a/QSB/Syncs/Sectored/BaseSectoredSync.cs +++ b/QSB/Syncs/Sectored/BaseSectoredSync.cs @@ -160,7 +160,7 @@ namespace QSB.Syncs.Sectored } } - protected override bool UpdateTransform() + protected bool UpdateSectors() { var referenceNull = ReferenceTransform == null || ReferenceSector == null || _intermediaryTransform.GetReferenceTransform() == null; var sectorManagerReady = QSBSectorManager.Instance.IsReady; @@ -183,28 +183,34 @@ namespace QSB.Syncs.Sectored return true; } - if (referenceNull && SectorSync.IsReady) + if (referenceNull) { - var closestSector = SectorSync.GetClosestSector(AttachedObject.transform); - if (closestSector != null) + if (SectorSync.IsReady) { - SetReferenceTransform(closestSector.Transform); - } - else - { - if (SectorSync.IsReady) + var closestSector = SectorSync.GetClosestSector(AttachedObject.transform); + if (closestSector != null) + { + SetReferenceTransform(closestSector.Transform); + return true; + } + else { DebugLog.ToConsole($"Error - No closest sector found to {PlayerId}.{GetType().Name}!", OWML.Common.MessageType.Error); return false; } - - return true; + } + else + { + return false; } } return true; } + protected override bool UpdateTransform() + => UpdateSectors(); + public void SetReferenceSector(QSBSector sector) { ReferenceSector = sector; diff --git a/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs b/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs index 6262a503..1dfadd64 100644 --- a/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs +++ b/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs @@ -97,6 +97,14 @@ namespace QSB.Syncs.Sectored.Rigidbodies } } + protected void SetValuesToSync() + { + _intermediaryTransform.EncodePosition(AttachedObject.transform.position); + _intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation); + _relativeVelocity = GetRelativeVelocity(); + _relativeAngularVelocity = (AttachedObject as OWRigidbody).GetRelativeAngularVelocity(ReferenceTransform.GetAttachedOWRigidbody()); + } + protected override bool UpdateTransform() { if (!base.UpdateTransform()) @@ -106,10 +114,7 @@ namespace QSB.Syncs.Sectored.Rigidbodies if (HasAuthority) { - _intermediaryTransform.EncodePosition(AttachedObject.transform.position); - _intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation); - _relativeVelocity = GetRelativeVelocity(); - _relativeAngularVelocity = (AttachedObject as OWRigidbody).GetRelativeAngularVelocity(ReferenceTransform.GetAttachedOWRigidbody()); + SetValuesToSync(); return true; } From 2712bbe60379719182ab3a00af966cafc0508886 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Mon, 13 Sep 2021 19:22:48 +0100 Subject: [PATCH 086/118] fix ship again --- .../TransformSync/ShipTransformSync.cs | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/QSB/ShipSync/TransformSync/ShipTransformSync.cs b/QSB/ShipSync/TransformSync/ShipTransformSync.cs index b9c7b312..235072e4 100644 --- a/QSB/ShipSync/TransformSync/ShipTransformSync.cs +++ b/QSB/ShipSync/TransformSync/ShipTransformSync.cs @@ -30,6 +30,11 @@ namespace QSB.ShipSync.TransformSync private void ForcePosition() { + if (ReferenceTransform == null) + { + return; + } + var targetPos = _intermediaryTransform.GetTargetPosition_Unparented(); var targetRot = _intermediaryTransform.GetTargetRotation_Unparented(); @@ -39,29 +44,17 @@ namespace QSB.ShipSync.TransformSync protected override bool UpdateTransform() { - if (!base.UpdateTransform()) + if (!UpdateSectors()) { return false; } + // Dont do base... this is a replacement! + if (HasAuthority) { - if (ReferenceTransform != null) - { - _intermediaryTransform.EncodePosition(AttachedObject.transform.position); - _intermediaryTransform.EncodeRotation(AttachedObject.transform.rotation); - _relativeVelocity = GetRelativeVelocity(); - _relativeAngularVelocity = (AttachedObject as OWRigidbody).GetRelativeAngularVelocity(ReferenceTransform.GetAttachedOWRigidbody()); - return true; - } - else - { - _intermediaryTransform.SetPosition(Vector3.zero); - _intermediaryTransform.SetRotation(Quaternion.identity); - _relativeVelocity = Vector3.zero; - _relativeAngularVelocity = Vector3.zero; - return true; - } + SetValuesToSync(); + return true; } _updateCount++; From 063a6d0705a24a457cceac514f101d7ab0b2ac3f Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Mon, 13 Sep 2021 19:44:47 +0100 Subject: [PATCH 087/118] set player to alive when respawning --- QSB/ClientServerStateSync/ClientStateManager.cs | 13 +++++++++++++ QSB/DeathSync/Events/PlayerRespawnEvent.cs | 4 +++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/QSB/ClientServerStateSync/ClientStateManager.cs b/QSB/ClientServerStateSync/ClientStateManager.cs index a9d662e0..71ed8813 100644 --- a/QSB/ClientServerStateSync/ClientStateManager.cs +++ b/QSB/ClientServerStateSync/ClientStateManager.cs @@ -98,6 +98,19 @@ namespace QSB.ClientServerStateSync } } + public void OnRespawn() + { + var currentScene = QSBSceneManager.CurrentScene; + if (currentScene == OWScene.SolarSystem) + { + QSBEventManager.FireEvent(EventNames.QSBClientState, ClientState.AliveInSolarSystem); + } + else + { + DebugLog.ToConsole($"Error - Player tried to respawn in scene {currentScene}", OWML.Common.MessageType.Error); + } + } + private ClientState ForceGetCurrentState() { var currentScene = LoadManager.GetCurrentScene(); diff --git a/QSB/DeathSync/Events/PlayerRespawnEvent.cs b/QSB/DeathSync/Events/PlayerRespawnEvent.cs index 0a88fca8..83e7e44b 100644 --- a/QSB/DeathSync/Events/PlayerRespawnEvent.cs +++ b/QSB/DeathSync/Events/PlayerRespawnEvent.cs @@ -1,4 +1,5 @@ -using QSB.Events; +using QSB.ClientServerStateSync; +using QSB.Events; using QSB.Messaging; using QSB.Player; @@ -29,6 +30,7 @@ namespace QSB.DeathSync.Events if (message.AboutId == LocalPlayerId) { RespawnManager.Instance.Respawn(); + ClientStateManager.Instance.OnRespawn(); } RespawnManager.Instance.OnPlayerRespawn(QSBPlayerManager.GetPlayer(message.AboutId)); From ec9a5cdbb995a28066efb7fe53971650486a10cd Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Tue, 14 Sep 2021 20:59:48 +0100 Subject: [PATCH 088/118] dont do probe events if not loaded --- QSB/ProbeSync/Events/PlayerProbeEvent.cs | 5 +++++ QSB/ProbeSync/Events/ProbeStartRetrieveEvent.cs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/QSB/ProbeSync/Events/PlayerProbeEvent.cs b/QSB/ProbeSync/Events/PlayerProbeEvent.cs index b5a1bc04..174645fa 100644 --- a/QSB/ProbeSync/Events/PlayerProbeEvent.cs +++ b/QSB/ProbeSync/Events/PlayerProbeEvent.cs @@ -25,6 +25,11 @@ namespace QSB.ProbeSync.Events public override void OnReceiveRemote(bool server, EnumMessage message) { var player = QSBPlayerManager.GetPlayer(message.AboutId); + if (!player.PlayerStates.IsReady || player.Probe == null) + { + return; + } + var probe = player.Probe; probe.HandleEvent(message.EnumValue); diff --git a/QSB/ProbeSync/Events/ProbeStartRetrieveEvent.cs b/QSB/ProbeSync/Events/ProbeStartRetrieveEvent.cs index b1a788a6..8f3f73e7 100644 --- a/QSB/ProbeSync/Events/ProbeStartRetrieveEvent.cs +++ b/QSB/ProbeSync/Events/ProbeStartRetrieveEvent.cs @@ -25,6 +25,11 @@ namespace QSB.ProbeSync.Events public override void OnReceiveRemote(bool server, FloatMessage message) { var player = QSBPlayerManager.GetPlayer(message.AboutId); + if (!player.PlayerStates.IsReady || player.Probe == null) + { + return; + } + var probe = player.Probe; probe.OnStartRetrieve(message.Value); } From 1e82521b5444e21808cbf00f4468e7fc24c9df49 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Wed, 15 Sep 2021 17:10:02 +0100 Subject: [PATCH 089/118] fix probe snapshots --- QSB/Tools/PlayerToolsManager.cs | 42 +++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/QSB/Tools/PlayerToolsManager.cs b/QSB/Tools/PlayerToolsManager.cs index 4c0ea2f4..321e9c42 100644 --- a/QSB/Tools/PlayerToolsManager.cs +++ b/QSB/Tools/PlayerToolsManager.cs @@ -300,21 +300,35 @@ namespace QSB.Tools private static void CreateProbeLauncher(Transform cameraBody) { - var REMOTE_ProbeLauncher = Object.Instantiate(GameObject.Find("PlayerCamera/ProbeLauncher")); + var ProbeLauncher = GameObject.Find("PlayerCamera/ProbeLauncher"); - Object.Destroy(REMOTE_ProbeLauncher.GetComponent()); - Object.Destroy(REMOTE_ProbeLauncher.GetComponent()); - Object.Destroy(REMOTE_ProbeLauncher.transform.Find("preLaunchCamera").gameObject); - Object.Destroy(REMOTE_ProbeLauncher.transform.Find("Props_HEA_ProbeLauncher_ProbeCamera").gameObject); // the correctly placed one... + // Create new ProbeLauncher + var REMOTE_ProbeLauncher = new GameObject("REMOTE_ProbeLauncher"); + REMOTE_ProbeLauncher.SetActive(false); - var prop = REMOTE_ProbeLauncher.transform.Find("Props_HEA_ProbeLauncher"); - var recallEffect = prop.Find("RecallEffect"); + // Copy children of ProbeLauncher + var Props_HEA_ProbeLauncher = ProbeLauncher.transform.Find("Props_HEA_ProbeLauncher"); + var REMOTE_Props_HEA_ProbeLauncher = Object.Instantiate(Props_HEA_ProbeLauncher, REMOTE_ProbeLauncher.transform, false); - prop.Find("PressureGauge_Arrow").GetComponent().material = Props_HEA_PlayerTool_mat; - prop.Find("ProbeLauncherChassis").GetComponent().material = Props_HEA_PlayerTool_mat; - Object.Destroy(prop.Find("Props_HEA_ProbeLauncher_Prepass").gameObject); + var LaunchParticleEffect_Underwater = ProbeLauncher.transform.Find("LaunchParticleEffect_Underwater"); + var REMOTE_LaunchParticleEffect_Underwater = Object.Instantiate(LaunchParticleEffect_Underwater, REMOTE_ProbeLauncher.transform, false); - var preLaunchProbe = prop.Find("Props_HEA_Probe_Prelaunch"); + var LaunchParticleEffect = ProbeLauncher.transform.Find("LaunchParticleEffect"); + var REMOTE_LaunchParticleEffect = Object.Instantiate(LaunchParticleEffect, REMOTE_ProbeLauncher.transform, false); + + // Set up effects + var effects = REMOTE_ProbeLauncher.AddComponent(); + effects.SetValue("_launchParticles", REMOTE_LaunchParticleEffect.GetComponent()); + effects.SetValue("_underwaterLaunchParticles", REMOTE_LaunchParticleEffect_Underwater.GetComponent()); + effects.SetValue("_owAudioSource", ProbeLauncher.GetComponent().GetValue("_owAudioSource")); + + var recallEffect = REMOTE_Props_HEA_ProbeLauncher.Find("RecallEffect"); + + REMOTE_Props_HEA_ProbeLauncher.Find("PressureGauge_Arrow").GetComponent().material = Props_HEA_PlayerTool_mat; + REMOTE_Props_HEA_ProbeLauncher.Find("ProbeLauncherChassis").GetComponent().material = Props_HEA_PlayerTool_mat; + Object.Destroy(REMOTE_Props_HEA_ProbeLauncher.Find("Props_HEA_ProbeLauncher_Prepass").gameObject); + + var preLaunchProbe = REMOTE_Props_HEA_ProbeLauncher.Find("Props_HEA_Probe_Prelaunch"); Object.Destroy(preLaunchProbe.Find("Props_HEA_Probe_Prelaunch_Prepass").gameObject); // fuck you unity @@ -340,13 +354,15 @@ namespace QSB.Tools tool.HoldTransform = _toolHoldTransform; tool.ArrivalDegrees = 5f; tool.Type = ToolType.ProbeLauncher; - tool.ToolGameObject = prop.gameObject; + tool.ToolGameObject = REMOTE_Props_HEA_ProbeLauncher.gameObject; tool.PreLaunchProbeProxy = preLaunchProbe.gameObject; tool.ProbeRetrievalEffect = recallEffect.GetComponent(); - tool.Effects = REMOTE_ProbeLauncher.GetComponent(); + tool.Effects = effects; REMOTE_ProbeLauncher.transform.parent = cameraBody; REMOTE_ProbeLauncher.transform.localPosition = ProbeLauncherOffset; + + //QSBCore.UnityEvents.FireInNUpdates(() => REMOTE_ProbeLauncher.SetActive(true), 5); REMOTE_ProbeLauncher.SetActive(true); } From 4b50a5ca89a8da89a44bb14577f27f73d0be6d0a Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Wed, 15 Sep 2021 17:10:09 +0100 Subject: [PATCH 090/118] add a null check --- QSB/Animation/NPC/Patches/CharacterAnimationPatches.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QSB/Animation/NPC/Patches/CharacterAnimationPatches.cs b/QSB/Animation/NPC/Patches/CharacterAnimationPatches.cs index cddf6461..2d797fe0 100644 --- a/QSB/Animation/NPC/Patches/CharacterAnimationPatches.cs +++ b/QSB/Animation/NPC/Patches/CharacterAnimationPatches.cs @@ -39,7 +39,7 @@ namespace QSB.Animation.NPC.Patches Animator ____animator, CharacterDialogueTree ____dialogueTree) { - if (!WorldObjectManager.AllReady) + if (!WorldObjectManager.AllReady || ConversationManager.Instance == null) { return false; } From e505f53ab431a49ba2a487943f8b5d8a631f3f0c Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 25 Sep 2021 09:47:07 +0100 Subject: [PATCH 091/118] add credits client state --- QSB/ClientServerStateSync/ClientState.cs | 4 +- .../ClientStateManager.cs | 147 ++++++++++++------ 2 files changed, 105 insertions(+), 46 deletions(-) diff --git a/QSB/ClientServerStateSync/ClientState.cs b/QSB/ClientServerStateSync/ClientState.cs index 9622102a..a2b65586 100644 --- a/QSB/ClientServerStateSync/ClientState.cs +++ b/QSB/ClientServerStateSync/ClientState.cs @@ -8,6 +8,8 @@ DeadInSolarSystem, AliveInEye, WaitingForOthersToDieInSolarSystem, - WaitingForOthersToReadyInSolarSystem + WaitingForOthersToReadyInSolarSystem, + WatchingLongCredits, + WatchingShortCredits } } diff --git a/QSB/ClientServerStateSync/ClientStateManager.cs b/QSB/ClientServerStateSync/ClientStateManager.cs index 71ed8813..50b4458d 100644 --- a/QSB/ClientServerStateSync/ClientStateManager.cs +++ b/QSB/ClientServerStateSync/ClientStateManager.cs @@ -38,46 +38,97 @@ namespace QSB.ClientServerStateSync { var serverState = ServerStateManager.Instance.GetServerState(); + ClientState newState; + if (QSBCore.IsHost) { - if (newScene == OWScene.SolarSystem && oldScene != OWScene.SolarSystem) + + switch (newScene) { - DebugLog.DebugWrite($"Server is loading SolarSystem just after creating server."); - QSBEventManager.FireEvent(EventNames.QSBClientState, ClientState.AliveInSolarSystem); - } - - if (newScene == OWScene.SolarSystem && oldScene == OWScene.SolarSystem) - { - DebugLog.DebugWrite($"Server is reloading SolarSystem"); - QSBEventManager.FireEvent(EventNames.QSBClientState, ClientState.WaitingForOthersToReadyInSolarSystem); - } - - if (newScene == OWScene.TitleScreen) - { - DebugLog.DebugWrite($"Server has gone back to title screen"); - QSBEventManager.FireEvent(EventNames.QSBClientState, ClientState.InTitleScreen); + case OWScene.TitleScreen: + DebugLog.DebugWrite($"SERVER LOAD TITLESCREEN"); + newState = ClientState.InTitleScreen; + break; + case OWScene.Credits_Fast: + DebugLog.DebugWrite($"SERVER LOAD SHORT CREDITS"); + newState = ClientState.WatchingShortCredits; + break; + case OWScene.Credits_Final: + case OWScene.PostCreditsScene: + DebugLog.DebugWrite($"SERVER LOAD LONG CREDITS"); + newState = ClientState.WatchingLongCredits; + break; + case OWScene.SolarSystem: + if (oldScene == OWScene.SolarSystem) + { + // reloading scene + DebugLog.DebugWrite($"SERVER RELOAD SOLARSYSTEM"); + newState = ClientState.WaitingForOthersToReadyInSolarSystem; + } + else + { + // loading in from title screen + DebugLog.DebugWrite($"SERVER LOAD SOLARSYSTEM"); + newState = ClientState.AliveInSolarSystem; + } + break; + default: + newState = ClientState.NotLoaded; + break; } } else { - if (newScene == OWScene.SolarSystem && oldScene != OWScene.SolarSystem && serverState != ServerState.AwaitingPlayConfirmation) + switch (newScene) { - DebugLog.DebugWrite($"Client is loading SolarSystem just after connecting."); - QSBEventManager.FireEvent(EventNames.QSBClientState, ClientState.AliveInSolarSystem); - } + case OWScene.TitleScreen: + DebugLog.DebugWrite($"CLIENT LOAD TITLESCREEN"); + newState = ClientState.InTitleScreen; + break; + case OWScene.Credits_Fast: + DebugLog.DebugWrite($"CLIENT LOAD SHORT CREDITS"); + newState = ClientState.WatchingShortCredits; + break; + case OWScene.Credits_Final: + case OWScene.PostCreditsScene: + DebugLog.DebugWrite($"CLIENT LOAD LONG CREDITS"); + newState = ClientState.WatchingLongCredits; + break; + case OWScene.SolarSystem: + if (serverState == ServerState.WaitingForAllPlayersToDie) + { + DebugLog.DebugWrite($"SEVER IN DEATH PHASE - WAIT"); + newState = ClientState.WaitingForOthersToReadyInSolarSystem; + break; + } - if (newScene == OWScene.SolarSystem && oldScene == OWScene.SolarSystem) - { - DebugLog.DebugWrite($"Client is reloading SolarSystem"); - QSBEventManager.FireEvent(EventNames.QSBClientState, ClientState.WaitingForOthersToReadyInSolarSystem); - } - - if (serverState == ServerState.WaitingForDeath) - { - DebugLog.DebugWrite($"Client loaded new scene while server is waiting for all players to die"); - QSBEventManager.FireEvent(EventNames.QSBClientState, ClientState.WaitingForOthersToReadyInSolarSystem); + if (oldScene == OWScene.SolarSystem) + { + // reloading scene + DebugLog.DebugWrite($"CLIENT RELOAD SOLARSYSTEM"); + newState = ClientState.WaitingForOthersToReadyInSolarSystem; + } + else + { + // loading in from title screen + DebugLog.DebugWrite($"CLIENT LOAD SOLARSYSTEM"); + if (serverState == ServerState.WaitingForAllPlayersToReady) + { + newState = ClientState.WaitingForOthersToReadyInSolarSystem; + } + else + { + newState = ClientState.AliveInSolarSystem; + } + } + break; + default: + newState = ClientState.NotLoaded; + break; } } + + QSBEventManager.FireEvent(EventNames.QSBClientState, newState); } public void OnDeath() @@ -103,6 +154,7 @@ namespace QSB.ClientServerStateSync var currentScene = QSBSceneManager.CurrentScene; if (currentScene == OWScene.SolarSystem) { + DebugLog.DebugWrite($"RESPAWN!"); QSBEventManager.FireEvent(EventNames.QSBClientState, ClientState.AliveInSolarSystem); } else @@ -113,27 +165,32 @@ namespace QSB.ClientServerStateSync private ClientState ForceGetCurrentState() { + DebugLog.DebugWrite($"ForceGetCurrentState"); var currentScene = LoadManager.GetCurrentScene(); var lastScene = LoadManager.GetPreviousScene(); - if (currentScene == OWScene.TitleScreen || currentScene == OWScene.Credits_Fast || currentScene == OWScene.Credits_Final) + switch (currentScene) { - return ClientState.InTitleScreen; + case OWScene.TitleScreen: + DebugLog.DebugWrite($"- TitleScreen"); + return ClientState.InTitleScreen; + case OWScene.Credits_Fast: + DebugLog.DebugWrite($"- Short Credits"); + return ClientState.WatchingShortCredits; + case OWScene.Credits_Final: + case OWScene.PostCreditsScene: + DebugLog.DebugWrite($"- Long Credits"); + return ClientState.WatchingLongCredits; + case OWScene.SolarSystem: + DebugLog.DebugWrite($"- SolarSystem"); + return ClientState.AliveInSolarSystem; + case OWScene.EyeOfTheUniverse: + DebugLog.DebugWrite($"- Eye"); + return ClientState.AliveInEye; + default: + DebugLog.DebugWrite($"- Not Loaded"); + return ClientState.NotLoaded; } - - // cant join while dead... - - if (currentScene == OWScene.SolarSystem) - { - return ClientState.AliveInSolarSystem; - } - - if (currentScene == OWScene.EyeOfTheUniverse) - { - return ClientState.AliveInEye; - } - - return ClientState.NotLoaded; } } } From 87c36598b7c82186f1bb3bb64f65616aacd057db Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 25 Sep 2021 09:47:30 +0100 Subject: [PATCH 092/118] removed some logs --- QSB/ClientServerStateSync/Events/ClientStateEvent.cs | 1 - QSB/DeathSync/Events/PlayerDeathEvent.cs | 2 -- QSB/DeathSync/Patches/DeathPatches.cs | 3 --- QSB/Player/QSBPlayerManager.cs | 1 - 4 files changed, 7 deletions(-) diff --git a/QSB/ClientServerStateSync/Events/ClientStateEvent.cs b/QSB/ClientServerStateSync/Events/ClientStateEvent.cs index e96ac30d..3e9b0890 100644 --- a/QSB/ClientServerStateSync/Events/ClientStateEvent.cs +++ b/QSB/ClientServerStateSync/Events/ClientStateEvent.cs @@ -28,7 +28,6 @@ namespace QSB.ClientServerStateSync.Events public override void OnReceiveRemote(bool server, EnumMessage message) { - DebugLog.DebugWrite($"Remote receive id:{message.AboutId} state:{message.EnumValue}"); if (message.AboutId == uint.MaxValue) { DebugLog.DebugWrite($"Error - ID is uint.MaxValue!", OWML.Common.MessageType.Error); diff --git a/QSB/DeathSync/Events/PlayerDeathEvent.cs b/QSB/DeathSync/Events/PlayerDeathEvent.cs index 9703fc43..00234ade 100644 --- a/QSB/DeathSync/Events/PlayerDeathEvent.cs +++ b/QSB/DeathSync/Events/PlayerDeathEvent.cs @@ -23,7 +23,6 @@ namespace QSB.DeathSync.Events public override void OnReceiveLocal(bool server, PlayerDeathMessage message) { - DebugLog.DebugWrite($"RECEIVE LOCAL PLAYER DEATH"); var player = QSBPlayerManager.GetPlayer(message.AboutId); RespawnManager.Instance.OnPlayerDeath(player); ClientStateManager.Instance.OnDeath(); @@ -31,7 +30,6 @@ namespace QSB.DeathSync.Events public override void OnReceiveRemote(bool server, PlayerDeathMessage message) { - DebugLog.DebugWrite($"RECEIVE REMOTE PLAYER DEATH"); var player = QSBPlayerManager.GetPlayer(message.AboutId); var playerName = player.Name; var deathMessage = Necronomicon.GetPhrase(message.EnumValue, message.NecronomiconIndex); diff --git a/QSB/DeathSync/Patches/DeathPatches.cs b/QSB/DeathSync/Patches/DeathPatches.cs index 2415c50c..eaf34985 100644 --- a/QSB/DeathSync/Patches/DeathPatches.cs +++ b/QSB/DeathSync/Patches/DeathPatches.cs @@ -4,7 +4,6 @@ using QSB.Patches; using QSB.Player; using QSB.ShipSync; using QSB.Utility; -using System; using System.Collections.Generic; using System.Linq; using System.Reflection.Emit; @@ -188,7 +187,6 @@ namespace QSB.DeathSync.Patches public static bool DeathManager_KillPlayer_Prefix(DeathType deathType) { - DebugLog.DebugWrite($"KILL PLAYER PREFIX stacetrace : \r\n {Environment.StackTrace}"); if (RespawnOnDeath.Instance == null) { return true; @@ -207,7 +205,6 @@ namespace QSB.DeathSync.Patches public static void DeathManager_KillPlayer_Postfix(DeathType deathType) { - DebugLog.DebugWrite($"KILL PLAYER POSTFIX"); QSBEventManager.FireEvent(EventNames.QSBPlayerDeath, deathType); } diff --git a/QSB/Player/QSBPlayerManager.cs b/QSB/Player/QSBPlayerManager.cs index 0c262511..7cfc2560 100644 --- a/QSB/Player/QSBPlayerManager.cs +++ b/QSB/Player/QSBPlayerManager.cs @@ -79,7 +79,6 @@ namespace QSB.Player public static void HandleFullStateMessage(PlayerInformationMessage message) { - DebugLog.DebugWrite($"Handle full state message of {message.AboutId}"); var player = GetPlayer(message.AboutId); player.Name = message.PlayerName; player.PlayerStates = message.PlayerState; From 4365d8911995f9834d00672fe4983bb48b0fd522 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 25 Sep 2021 09:47:50 +0100 Subject: [PATCH 093/118] added "none" fast forward / pause reasons --- QSB/TimeSync/FastForwardReason.cs | 1 + QSB/TimeSync/PauseReason.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/QSB/TimeSync/FastForwardReason.cs b/QSB/TimeSync/FastForwardReason.cs index 2d80cfcc..a9a813ad 100644 --- a/QSB/TimeSync/FastForwardReason.cs +++ b/QSB/TimeSync/FastForwardReason.cs @@ -2,6 +2,7 @@ { public enum FastForwardReason { + None, TooFarBehind } } diff --git a/QSB/TimeSync/PauseReason.cs b/QSB/TimeSync/PauseReason.cs index d184a2d6..06e52ef0 100644 --- a/QSB/TimeSync/PauseReason.cs +++ b/QSB/TimeSync/PauseReason.cs @@ -2,6 +2,7 @@ { public enum PauseReason { + None, TooFarAhead, ServerNotStarted, WaitingForAllPlayersToDie, From 56538cd18123926161a1090285faa8a1a2d5ba52 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 25 Sep 2021 09:48:11 +0100 Subject: [PATCH 094/118] dont start timesyncui when not in universe --- QSB/TimeSync/TimeSyncUI.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/QSB/TimeSync/TimeSyncUI.cs b/QSB/TimeSync/TimeSyncUI.cs index 6a0743c7..ba0d0ac2 100644 --- a/QSB/TimeSync/TimeSyncUI.cs +++ b/QSB/TimeSync/TimeSyncUI.cs @@ -1,4 +1,5 @@ using OWML.Utils; +using QSB.Utility; using System; using UnityEngine; using UnityEngine.UI; @@ -52,6 +53,11 @@ namespace QSB.TimeSync private void StartTimeSync(TimeSyncType type, Enum reason) { + if (!QSBSceneManager.IsInUniverse) + { + DebugLog.ToConsole("Error - Tried to start time sync UI when not in universe!", OWML.Common.MessageType.Error); + return; + } _currentType = type; _currentReason = reason; _startTime = Time.timeSinceLevelLoad; From fd655c7511f0672f60fa7cc5749c2926e6eaac96 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 25 Sep 2021 09:49:03 +0100 Subject: [PATCH 095/118] check current scene before pausing & update serverstate names --- QSB/ClientServerStateSync/ServerState.cs | 4 ++-- .../ServerStateManager.cs | 18 +++++++++++++----- QSB/DeathSync/Events/EndLoopEvent.cs | 4 ++-- QSB/TimeSync/WakeUpSync.cs | 9 +++++---- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/QSB/ClientServerStateSync/ServerState.cs b/QSB/ClientServerStateSync/ServerState.cs index f2d1d91c..828371b5 100644 --- a/QSB/ClientServerStateSync/ServerState.cs +++ b/QSB/ClientServerStateSync/ServerState.cs @@ -15,10 +15,10 @@ InEye, // At end of loop, waiting for everyone to be ready to reload the scene - WaitingForDeath, + WaitingForAllPlayersToDie, // At start of loop, waiting for everybody to be ready to start playing - AwaitingPlayConfirmation, + WaitingForAllPlayersToReady, // When the statue has been activated InStatueCutscene diff --git a/QSB/ClientServerStateSync/ServerStateManager.cs b/QSB/ClientServerStateSync/ServerStateManager.cs index 33cfc873..70722429 100644 --- a/QSB/ClientServerStateSync/ServerStateManager.cs +++ b/QSB/ClientServerStateSync/ServerStateManager.cs @@ -39,7 +39,6 @@ namespace QSB.ClientServerStateSync return; } - DebugLog.DebugWrite($"CHANGE SERVER STATE FROM {_currentState} to {newState}"); _currentState = newState; OnChangeState?.Invoke(newState); } @@ -54,17 +53,20 @@ namespace QSB.ClientServerStateSync case OWScene.Credits_Fast: case OWScene.Credits_Final: case OWScene.PostCreditsScene: + DebugLog.DebugWrite($"SERVER LOAD CREDITS"); QSBEventManager.FireEvent(EventNames.QSBServerState, ServerState.Credits); break; case OWScene.TitleScreen: + DebugLog.DebugWrite($"SERVER LOAD TITLE SCREEN"); QSBEventManager.FireEvent(EventNames.QSBServerState, ServerState.NotLoaded); break; case OWScene.SolarSystem: + DebugLog.DebugWrite($"SERVER LOAD SOLARSYSTEM"); if (oldScene == OWScene.SolarSystem) { - QSBEventManager.FireEvent(EventNames.QSBServerState, ServerState.AwaitingPlayConfirmation); + QSBEventManager.FireEvent(EventNames.QSBServerState, ServerState.WaitingForAllPlayersToReady); } else { @@ -74,7 +76,8 @@ namespace QSB.ClientServerStateSync break; case OWScene.EyeOfTheUniverse: - QSBEventManager.FireEvent(EventNames.QSBServerState, ServerState.AwaitingPlayConfirmation); + DebugLog.DebugWrite($"EYE"); + QSBEventManager.FireEvent(EventNames.QSBServerState, ServerState.WaitingForAllPlayersToReady); break; case OWScene.None: @@ -89,21 +92,26 @@ namespace QSB.ClientServerStateSync private void OnTriggerSupernova() { DebugLog.DebugWrite($"TriggerSupernova"); - QSBEventManager.FireEvent(EventNames.QSBServerState, ServerState.WaitingForDeath); + QSBEventManager.FireEvent(EventNames.QSBServerState, ServerState.WaitingForAllPlayersToDie); } private ServerState ForceGetCurrentState() { + DebugLog.DebugWrite($"ForceGetCurrentState"); + var currentScene = LoadManager.GetCurrentScene(); var lastScene = LoadManager.GetPreviousScene(); switch (currentScene) { case OWScene.SolarSystem: + DebugLog.DebugWrite($"- SolarSystem"); return ServerState.InSolarSystem; case OWScene.EyeOfTheUniverse: + DebugLog.DebugWrite($"- Eye"); return ServerState.InEye; default: + DebugLog.DebugWrite($"- Not Loaded"); return ServerState.NotLoaded; } } @@ -115,7 +123,7 @@ namespace QSB.ClientServerStateSync return; } - if (_currentState == ServerState.AwaitingPlayConfirmation) + if (_currentState == ServerState.WaitingForAllPlayersToReady) { if (QSBPlayerManager.PlayerList.All(x => x.State == ClientState.WaitingForOthersToReadyInSolarSystem)) { diff --git a/QSB/DeathSync/Events/EndLoopEvent.cs b/QSB/DeathSync/Events/EndLoopEvent.cs index c99cbd50..0208c4ea 100644 --- a/QSB/DeathSync/Events/EndLoopEvent.cs +++ b/QSB/DeathSync/Events/EndLoopEvent.cs @@ -25,14 +25,14 @@ namespace QSB.DeathSync.Events public override void OnReceiveRemote(bool server, EnumMessage message) { + DebugLog.DebugWrite($" ~~~~ END LOOP - Reason:{message.EnumValue} ~~~~ "); switch (message.EnumValue) { case EndLoopReason.AllPlayersDead: - DebugLog.DebugWrite($"all players dead"); Locator.GetDeathManager().KillPlayer(DeathType.TimeLoop); if (QSBCore.IsHost) { - QSBEventManager.FireEvent(EventNames.QSBServerState, ServerState.WaitingForDeath); + QSBEventManager.FireEvent(EventNames.QSBServerState, ServerState.WaitingForAllPlayersToDie); } break; diff --git a/QSB/TimeSync/WakeUpSync.cs b/QSB/TimeSync/WakeUpSync.cs index 090bdef9..06cdc885 100644 --- a/QSB/TimeSync/WakeUpSync.cs +++ b/QSB/TimeSync/WakeUpSync.cs @@ -237,7 +237,7 @@ namespace QSB.TimeSync var serverState = ServerStateManager.Instance.GetServerState(); var clientState = QSBPlayerManager.LocalPlayer.State; - if (serverState == ServerState.AwaitingPlayConfirmation && clientState == ClientState.WaitingForOthersToReadyInSolarSystem) + if (serverState == ServerState.WaitingForAllPlayersToReady && clientState == ClientState.WaitingForOthersToReadyInSolarSystem) { if (CurrentState != State.Pausing) { @@ -274,6 +274,7 @@ namespace QSB.TimeSync var serverState = ServerStateManager.Instance.GetServerState(); var clientState = QSBPlayerManager.LocalPlayer.State; + var currentScene = QSBSceneManager.CurrentScene; // set fastforwarding timescale @@ -302,13 +303,13 @@ namespace QSB.TimeSync return; } - if (serverState == ServerState.NotLoaded && CurrentState != State.Pausing) + if (serverState == ServerState.NotLoaded && CurrentState != State.Pausing && QSBSceneManager.IsInUniverse) { DebugLog.DebugWrite($"Server Not Loaded"); StartPausing(PauseReason.ServerNotStarted); } - if (serverState == ServerState.AwaitingPlayConfirmation && CurrentState != State.Pausing && clientState == ClientState.WaitingForOthersToReadyInSolarSystem) + if (serverState == ServerState.WaitingForAllPlayersToReady && CurrentState != State.Pausing && clientState == ClientState.WaitingForOthersToReadyInSolarSystem) { DebugLog.DebugWrite($"Awaiting Play Confirmation"); StartPausing(PauseReason.WaitingForAllPlayersToBeReady); @@ -319,7 +320,7 @@ namespace QSB.TimeSync DebugLog.DebugWrite($"Server is still running game normally, but this player has died from an accepted death!", MessageType.Warning); } - if (serverState == ServerState.WaitingForDeath && clientState == ClientState.WaitingForOthersToReadyInSolarSystem) + if (serverState == ServerState.WaitingForAllPlayersToDie && clientState == ClientState.WaitingForOthersToReadyInSolarSystem) { DebugLog.DebugWrite($"Wait for others to load new scene"); StartPausing(PauseReason.WaitingForAllPlayersToBeReady); From af69b46656e576c02e0c4ee1a961d2584a7b7388 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 25 Sep 2021 09:49:15 +0100 Subject: [PATCH 096/118] add pause/fastforward reasons to gui --- QSB/Utility/DebugGUI.cs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/QSB/Utility/DebugGUI.cs b/QSB/Utility/DebugGUI.cs index 3328f63f..bf688a0b 100644 --- a/QSB/Utility/DebugGUI.cs +++ b/QSB/Utility/DebugGUI.cs @@ -35,7 +35,26 @@ namespace QSB.Utility { GUI.Label(new Rect(220, offset, 200f, 20f), $"Server State : {ServerStateManager.Instance.GetServerState()}", guiStyle); offset += _debugLineSpacing; - GUI.Label(new Rect(220, offset, 200f, 20f), $"WakeUpSync State : {WakeUpSync.LocalInstance.CurrentState}", guiStyle); + var currentState = WakeUpSync.LocalInstance.CurrentState; + GUI.Label(new Rect(220, offset, 200f, 20f), $"WakeUpSync State : {currentState}", guiStyle); + offset += _debugLineSpacing; + var reason = WakeUpSync.LocalInstance.CurrentReason; + if (currentState == WakeUpSync.State.FastForwarding && reason != null) + { + + GUI.Label(new Rect(220, offset, 200f, 20f), $"Reason : {(FastForwardReason)reason}", guiStyle); + offset += _debugLineSpacing; + } + else if (currentState == WakeUpSync.State.Pausing && reason != null) + { + GUI.Label(new Rect(220, offset, 200f, 20f), $"Reason : {(PauseReason)reason}", guiStyle); + offset += _debugLineSpacing; + } + else if (currentState != WakeUpSync.State.Loaded && currentState != WakeUpSync.State.NotLoaded && reason == null) + { + GUI.Label(new Rect(220, offset, 200f, 20f), $"Reason : NULL", guiStyle); + offset += _debugLineSpacing; + } offset += _debugLineSpacing; GUI.Label(new Rect(220, offset, 200f, 20f), $"Time Difference : {WakeUpSync.LocalInstance.GetTimeDifference()}", guiStyle); offset += _debugLineSpacing; From d9e9e2b5cf816880f8728bb8cf022cb41db4e21d Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 25 Sep 2021 09:49:53 +0100 Subject: [PATCH 097/118] correctly change menu names and options when loading new scenes --- QSB/Menus/MenuManager.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/QSB/Menus/MenuManager.cs b/QSB/Menus/MenuManager.cs index d378fcfe..1aa53ff1 100644 --- a/QSB/Menus/MenuManager.cs +++ b/QSB/Menus/MenuManager.cs @@ -105,6 +105,8 @@ namespace QSB.Menus DisconnectButton.gameObject.SetActive(false); DisconnectButton.GetComponent().alpha = 1f; } + + OnConnected(); } private void MakeTitleMenus() @@ -118,8 +120,21 @@ namespace QSB.Menus DisconnectButton = MenuApi.TitleScreen_MakeSimpleButton("DISCONNECT"); DisconnectButton.onClick.AddListener(Disconnect); - DisconnectButton.gameObject.SetActive(false); - DisconnectButton.GetComponent().alpha = 1f; + + if (QSBCore.IsInMultiplayer) + { + ClientButton.SetActive(false); + HostButton.gameObject.SetActive(false); + DisconnectButton.gameObject.SetActive(true); + DisconnectButton.GetComponent().alpha = 1f; + } + else + { + DisconnectButton.gameObject.SetActive(false); + DisconnectButton.GetComponent().alpha = 1f; + } + + OnConnected(); } private void Disconnect() From 7732a63b5aba38060ff6db14600d71b577b91161 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Mon, 27 Sep 2021 16:54:15 +0100 Subject: [PATCH 098/118] Fixes #314 --- QSB/ConversationSync/CameraFacingBillboard.cs | 30 +++++++++++++++++++ QSB/ConversationSync/ConversationManager.cs | 6 +--- QSB/QSB.csproj | 1 + QSB/Utility/DebugBoxManager.cs | 6 +--- 4 files changed, 33 insertions(+), 10 deletions(-) create mode 100644 QSB/ConversationSync/CameraFacingBillboard.cs diff --git a/QSB/ConversationSync/CameraFacingBillboard.cs b/QSB/ConversationSync/CameraFacingBillboard.cs new file mode 100644 index 00000000..74b0630c --- /dev/null +++ b/QSB/ConversationSync/CameraFacingBillboard.cs @@ -0,0 +1,30 @@ +using UnityEngine; + +public class CameraFacingBillboard : MonoBehaviour +{ + private OWCamera _activeCam; + + private void Awake() + => GlobalMessenger.AddListener("SwitchActiveCamera", OnSwitchActiveCamera); + + private void Start() + { + _activeCam = Locator.GetActiveCamera(); + UpdateRotation(); + } + + private void OnDestroy() + => GlobalMessenger.RemoveListener("SwitchActiveCamera", OnSwitchActiveCamera); + + private void OnSwitchActiveCamera(OWCamera activeCamera) + { + _activeCam = activeCamera; + UpdateRotation(); + } + + void LateUpdate() + => UpdateRotation(); + + private void UpdateRotation() + => transform.LookAt(transform.position + (_activeCam.transform.rotation * Vector3.forward), _activeCam.transform.rotation * Vector3.up); +} \ No newline at end of file diff --git a/QSB/ConversationSync/ConversationManager.cs b/QSB/ConversationSync/ConversationManager.cs index 3171b3f8..8baa343f 100644 --- a/QSB/ConversationSync/ConversationManager.cs +++ b/QSB/ConversationSync/ConversationManager.cs @@ -1,5 +1,4 @@ using OWML.Common; -using OWML.Utils; using QSB.Events; using QSB.Player; using QSB.Utility; @@ -120,10 +119,7 @@ namespace QSB.ConversationSync newBox.transform.SetParent(parent); newBox.transform.localPosition = new Vector3(0, vertOffset, 0); newBox.transform.rotation = parent.rotation; - var lookAt = newBox.AddComponent(); - lookAt.SetValue("_useLookAt", false); - lookAt.SetValue("_localFacingVector", Vector3.back); - lookAt.SetValue("_localRotationAxis", Vector3.up); + newBox.AddComponent(); newBox.GetComponent().text = text; newBox.AddComponent(); newBox.SetActive(true); diff --git a/QSB/QSB.csproj b/QSB/QSB.csproj index 9858b117..a9a5f5fa 100644 --- a/QSB/QSB.csproj +++ b/QSB/QSB.csproj @@ -134,6 +134,7 @@ + diff --git a/QSB/Utility/DebugBoxManager.cs b/QSB/Utility/DebugBoxManager.cs index 37921e3c..8d2481c6 100644 --- a/QSB/Utility/DebugBoxManager.cs +++ b/QSB/Utility/DebugBoxManager.cs @@ -1,5 +1,4 @@ using OWML.Common; -using OWML.Utils; using UnityEngine; using UnityEngine.UI; @@ -29,10 +28,7 @@ namespace QSB.Utility newBox.transform.SetParent(parent); newBox.transform.localPosition = new Vector3(0, vertOffset, 0); newBox.transform.rotation = parent.rotation; - var lookAt = newBox.AddComponent(); - lookAt.SetValue("_useLookAt", true); - lookAt.SetValue("_localFacingVector", Vector3.back); - lookAt.SetValue("_localRotationAxis", Vector3.up); + newBox.AddComponent(); newBox.GetComponent().text = text; newBox.AddComponent(); newBox.SetActive(true); From efb000c5249d40162d063d6c3d21a60339c8681c Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Tue, 12 Oct 2021 15:31:02 +0100 Subject: [PATCH 099/118] update OW code references --- QSB/Animation/Player/CrouchSync.cs | 2 +- QSB/Animation/Player/Patches/PlayerAnimationPatches.cs | 2 +- QSB/DeathSync/Patches/MapPatches.cs | 6 +++--- QSB/Instruments/QSBCamera/CameraController.cs | 2 +- QSB/RoastingSync/Patches/RoastingPatches.cs | 2 +- QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/QSB/Animation/Player/CrouchSync.cs b/QSB/Animation/Player/CrouchSync.cs index 47e2a233..9cf9cb66 100644 --- a/QSB/Animation/Player/CrouchSync.cs +++ b/QSB/Animation/Player/CrouchSync.cs @@ -41,7 +41,7 @@ namespace QSB.Animation.Player return; } - var jumpChargeFraction = _playerController.GetJumpChargeFraction(); + var jumpChargeFraction = _playerController.GetJumpCrouchFraction(); _crouchValue = jumpChargeFraction; } diff --git a/QSB/Animation/Player/Patches/PlayerAnimationPatches.cs b/QSB/Animation/Player/Patches/PlayerAnimationPatches.cs index 404c5a55..7db275a1 100644 --- a/QSB/Animation/Player/Patches/PlayerAnimationPatches.cs +++ b/QSB/Animation/Player/Patches/PlayerAnimationPatches.cs @@ -66,7 +66,7 @@ namespace QSB.Animation.Patches ____animator.SetFloat("RunSpeedY", movementVector.z / 3f); ____animator.SetFloat("TurnSpeed", ____playerController.GetTurning()); ____animator.SetBool("Grounded", isGrounded || isAttached || PlayerState.IsRecentlyDetached()); - ____animator.SetLayerWeight(1, ____playerController.GetJumpChargeFraction()); + ____animator.SetLayerWeight(1, ____playerController.GetJumpCrouchFraction()); ____animator.SetFloat("FreefallSpeed", freefallMagnitude / 15f * (timeInFreefall / 3f)); ____animator.SetBool("InZeroG", isInZeroG || isFlying); ____animator.SetBool("UsingJetpack", isInZeroG && PlayerState.IsWearingSuit()); diff --git a/QSB/DeathSync/Patches/MapPatches.cs b/QSB/DeathSync/Patches/MapPatches.cs index 7a901d16..7bc11ca2 100644 --- a/QSB/DeathSync/Patches/MapPatches.cs +++ b/QSB/DeathSync/Patches/MapPatches.cs @@ -164,9 +164,9 @@ namespace QSB.DeathSync.Patches var zoomInput = 0f; if (canInteractWith) { - XZinput = OWInput.GetValue(InputLibrary.moveXZ, InputMode.All); - lookInput = InputLibrary.look.GetValue(false); - zoomInput = OWInput.GetValue(InputLibrary.mapZoom, InputMode.All); + XZinput = OWInput.GetAxisValue(InputLibrary.moveXZ, InputMode.All); + lookInput = InputLibrary.look.GetAxisValue(false); + zoomInput = OWInput.GetValue(InputLibrary.mapZoomIn, InputMode.All) - OWInput.GetValue(InputLibrary.mapZoomOut, InputMode.All); lookInput.y *= -1f; zoomInput *= -1f; } diff --git a/QSB/Instruments/QSBCamera/CameraController.cs b/QSB/Instruments/QSBCamera/CameraController.cs index 24394c80..2d8f6f6f 100644 --- a/QSB/Instruments/QSBCamera/CameraController.cs +++ b/QSB/Instruments/QSBCamera/CameraController.cs @@ -55,7 +55,7 @@ namespace QSB.Instruments.QSBCamera private void UpdateInput() { - var input = OWInput.GetValue(InputLibrary.look, false); + var input = InputLibrary.look.GetAxisValue(false); _degreesX += input.x * 180f * Time.fixedDeltaTime; _degreesY += input.y * 180f * Time.fixedDeltaTime; } diff --git a/QSB/RoastingSync/Patches/RoastingPatches.cs b/QSB/RoastingSync/Patches/RoastingPatches.cs index d4adaa84..2fa6da8b 100644 --- a/QSB/RoastingSync/Patches/RoastingPatches.cs +++ b/QSB/RoastingSync/Patches/RoastingPatches.cs @@ -96,7 +96,7 @@ namespace QSB.RoastingSync.Patches if (____marshmallow.IsBurned()) { showRemovePrompt = true; - if (OWInput.IsNewlyPressed(InputLibrary.cancel, true, InputMode.Roasting)) + if (OWInput.IsNewlyPressed(InputLibrary.cancel, InputMode.Roasting)) { ____marshmallow.Remove(); Locator.GetPlayerAudioController().PlayMarshmallowToss(); diff --git a/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs b/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs index 1dfadd64..dba1c782 100644 --- a/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs +++ b/QSB/Syncs/Sectored/Rigidbodies/SectoredRigidbodySync.cs @@ -228,12 +228,12 @@ namespace QSB.Syncs.Sectored.Rigidbodies if (isRunningKinematic) { var kinematicRigidbody = rigidbody.GetValue("_kinematicRigidbody"); - kinematicRigidbody.velocity = relativeVelocity + Locator.GetCenterOfTheUniverse().GetStaticFrameWorldVelocity(); + kinematicRigidbody.velocity = relativeVelocity + Locator.GetCenterOfTheUniverse().GetStaticFrameVelocity_Internal(); } else { var normalRigidbody = rigidbody.GetValue("_rigidbody"); - normalRigidbody.velocity = relativeVelocity + Locator.GetCenterOfTheUniverse().GetStaticFrameWorldVelocity(); + normalRigidbody.velocity = relativeVelocity + Locator.GetCenterOfTheUniverse().GetStaticFrameVelocity_Internal(); } rigidbody.SetValue("_lastVelocity", currentVelocity); From 75f4ac38364718bfb2580eb0133659ea5f9b0d44 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Tue, 12 Oct 2021 15:32:24 +0100 Subject: [PATCH 100/118] update harmony references --- QSB/DeathSync/Patches/DeathPatches.cs | 2 +- QSB/Patches/QSBPatch.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/QSB/DeathSync/Patches/DeathPatches.cs b/QSB/DeathSync/Patches/DeathPatches.cs index eaf34985..50d596d9 100644 --- a/QSB/DeathSync/Patches/DeathPatches.cs +++ b/QSB/DeathSync/Patches/DeathPatches.cs @@ -1,4 +1,4 @@ -using Harmony; +using HarmonyLib; using QSB.Events; using QSB.Patches; using QSB.Player; diff --git a/QSB/Patches/QSBPatch.cs b/QSB/Patches/QSBPatch.cs index 45632a99..da2d2a8f 100644 --- a/QSB/Patches/QSBPatch.cs +++ b/QSB/Patches/QSBPatch.cs @@ -1,4 +1,4 @@ -using Harmony; +using HarmonyLib; using OWML.Common; using OWML.Utils; using QSB.Utility; From 506ea6d894ab007493b810b96f7d708178d899b7 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Tue, 12 Oct 2021 15:32:47 +0100 Subject: [PATCH 101/118] use tuple --- QSB/QuantumSync/Patches/QuantumPatches.cs | 4 ++-- QSB/QuantumSync/Patches/QuantumVisibilityPatches.cs | 6 +++--- QSB/QuantumSync/Patches/ServerQuantumPatches.cs | 4 ++-- QSB/QuantumSync/QuantumManager.cs | 3 ++- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/QSB/QuantumSync/Patches/QuantumPatches.cs b/QSB/QuantumSync/Patches/QuantumPatches.cs index a0f2152c..bfe4e469 100644 --- a/QSB/QuantumSync/Patches/QuantumPatches.cs +++ b/QSB/QuantumSync/Patches/QuantumPatches.cs @@ -429,7 +429,7 @@ namespace QSB.QuantumSync.Patches fogAlpha = Mathf.InverseLerp(____fogThickness + ____fogRolloffDistance, ____fogThickness, distanceFromFog); if (distanceFromFog < 0f) { - if ((bool)__instance.GetType().GetMethod("IsLockedByProbeSnapshot", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(__instance, null) || QuantumManager.IsVisibleUsingCameraFrustum((ShapeVisibilityTracker)____visibilityTracker, true).First) + if ((bool)__instance.GetType().GetMethod("IsLockedByProbeSnapshot", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(__instance, null) || QuantumManager.IsVisibleUsingCameraFrustum((ShapeVisibilityTracker)____visibilityTracker, true).Item1) { ____isPlayerInside = true; __instance.GetType().GetMethod("SetSurfaceState", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(__instance, new object[] { ____stateIndex }); @@ -450,7 +450,7 @@ namespace QSB.QuantumSync.Patches if (____stateIndex != 5) { ____isPlayerInside = false; - if (!(bool)__instance.GetType().GetMethod("IsLockedByProbeSnapshot", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(__instance, null) && !QuantumManager.IsVisibleUsingCameraFrustum((ShapeVisibilityTracker)____visibilityTracker, true).First) + if (!(bool)__instance.GetType().GetMethod("IsLockedByProbeSnapshot", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(__instance, null) && !QuantumManager.IsVisibleUsingCameraFrustum((ShapeVisibilityTracker)____visibilityTracker, true).Item1) { __instance.GetType().GetMethod("Collapse", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(__instance, new object[] { true }); } diff --git a/QSB/QuantumSync/Patches/QuantumVisibilityPatches.cs b/QSB/QuantumSync/Patches/QuantumVisibilityPatches.cs index 6fab80da..fafadb80 100644 --- a/QSB/QuantumSync/Patches/QuantumVisibilityPatches.cs +++ b/QSB/QuantumSync/Patches/QuantumVisibilityPatches.cs @@ -31,7 +31,7 @@ namespace QSB.QuantumSync.Patches public static bool ShapeVisibilityTracker_IsVisibleUsingCameraFrustum(ShapeVisibilityTracker __instance, ref bool __result) { - __result = QuantumManager.IsVisibleUsingCameraFrustum(__instance, false).First; + __result = QuantumManager.IsVisibleUsingCameraFrustum(__instance, false).Item1; return false; } @@ -66,8 +66,8 @@ namespace QSB.QuantumSync.Patches var point = __instance.transform.TransformPoint(____localIlluminationOffset); var tupleFlashlights = QSBPlayerManager.GetPlayerFlashlights(); - var localFlashlight = tupleFlashlights.First; - var playerFlashlights = tupleFlashlights.Second; + var localFlashlight = tupleFlashlights.Item1; + var playerFlashlights = tupleFlashlights.Item2; // local player flashlight if (localFlashlight.CheckIlluminationAtPoint(point, ____illuminationRadius)) diff --git a/QSB/QuantumSync/Patches/ServerQuantumPatches.cs b/QSB/QuantumSync/Patches/ServerQuantumPatches.cs index e312f571..c6572f85 100644 --- a/QSB/QuantumSync/Patches/ServerQuantumPatches.cs +++ b/QSB/QuantumSync/Patches/ServerQuantumPatches.cs @@ -45,7 +45,7 @@ namespace QSB.QuantumSync.Patches var isVisibleOutput = QuantumManager.IsVisibleUsingCameraFrustum((ShapeVisibilityTracker)____visibilityTracker, skipInstantVisibilityCheck); //var moonVisible = isVisibleOutput.First; - var moonVisiblePlayers = isVisibleOutput.Second; + var moonVisiblePlayers = isVisibleOutput.Item2; var inMoonPlayers = QSBPlayerManager.PlayerList.Where(x => x.IsInMoon); if (inMoonPlayers == null) { @@ -153,7 +153,7 @@ namespace QSB.QuantumSync.Patches Physics.SyncTransforms(); } - if (__instance.IsPlayerEntangled() || !QuantumManager.IsVisibleUsingCameraFrustum((ShapeVisibilityTracker)____visibilityTracker, skipInstantVisibilityCheck).First) + if (__instance.IsPlayerEntangled() || !QuantumManager.IsVisibleUsingCameraFrustum((ShapeVisibilityTracker)____visibilityTracker, skipInstantVisibilityCheck).Item1) { ____moonBody.transform.position = position; if (!Physics.autoSyncTransforms) diff --git a/QSB/QuantumSync/QuantumManager.cs b/QSB/QuantumSync/QuantumManager.cs index aa8a5d30..abf2bf20 100644 --- a/QSB/QuantumSync/QuantumManager.cs +++ b/QSB/QuantumSync/QuantumManager.cs @@ -4,6 +4,7 @@ using QSB.Player; using QSB.QuantumSync.WorldObjects; using QSB.Utility; using QSB.WorldSync; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -120,7 +121,7 @@ namespace QSB.QuantumSync } public static bool IsVisible(ShapeVisibilityTracker tracker, bool ignoreLocalCamera) => tracker.gameObject.activeInHierarchy - && IsVisibleUsingCameraFrustum(tracker, ignoreLocalCamera).First + && IsVisibleUsingCameraFrustum(tracker, ignoreLocalCamera).Item1 && QSBPlayerManager.GetPlayersWithCameras(!ignoreLocalCamera) .Any(x => VisibilityOccluder.CanYouSee(tracker, x.Camera.mainCamera.transform.position)); From e65bd0d6570958af9d26deb460e0c8fc6feb40ea Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Tue, 12 Oct 2021 15:32:59 +0100 Subject: [PATCH 102/118] delete old tuple --- QSB/Utility/Tuple.cs | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 QSB/Utility/Tuple.cs diff --git a/QSB/Utility/Tuple.cs b/QSB/Utility/Tuple.cs deleted file mode 100644 index 7112f8d5..00000000 --- a/QSB/Utility/Tuple.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace QSB.Utility -{ - public class Tuple - { - public T1 First { get; private set; } - public T2 Second { get; private set; } - - internal Tuple(T1 first, T2 second) - { - First = first; - Second = second; - } - } -} From c1704b53f0b6ba8aea293bd442c548ba2e873869 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Tue, 12 Oct 2021 16:56:07 +0100 Subject: [PATCH 103/118] fix item patch --- QSB/ItemSync/Patches/ItemPatches.cs | 18 +- QSB/Patches/QSBPatch.cs | 2 + QSB/QSB.csproj | 210 ++++++++++++------- QSB/QSBCore.cs | 5 +- QSB/Utility/DebugActions.cs | 2 + QSB/app.config | 11 + QSB/lib/UnityEngine.Networking.dll | Bin 0 -> 255488 bytes QSB/manifest.json | 3 +- QSB/packages.config | 7 + QSBTests/QSBTests.csproj | 1 + QSBTests/QSBTests.csproj.user | 2 +- QSBTests/app.config | 11 + QuantumUNET/Components/QNetworkManagerHUD.cs | 131 ++++++++++++ QuantumUNET/QuantumUNET.csproj | 1 + UnityEngine.Networking.dll | Bin 0 -> 255488 bytes 15 files changed, 314 insertions(+), 90 deletions(-) create mode 100644 QSB/app.config create mode 100644 QSB/lib/UnityEngine.Networking.dll create mode 100644 QSB/packages.config create mode 100644 QSBTests/app.config create mode 100644 QuantumUNET/Components/QNetworkManagerHUD.cs create mode 100644 UnityEngine.Networking.dll diff --git a/QSB/ItemSync/Patches/ItemPatches.cs b/QSB/ItemSync/Patches/ItemPatches.cs index 314cbca3..b47a0826 100644 --- a/QSB/ItemSync/Patches/ItemPatches.cs +++ b/QSB/ItemSync/Patches/ItemPatches.cs @@ -49,7 +49,7 @@ namespace QSB.ItemSync.Patches return true; } - public static bool ItemTool_DropItem(RaycastHit hit, OWRigidbody targetRigidbody, DetachableFragment detachableFragment, ref OWItem ____heldItem) + public static bool ItemTool_DropItem(RaycastHit hit, OWRigidbody targetRigidbody, IItemDropTarget customDropTarget, ref OWItem ____heldItem) { Locator.GetPlayerAudioController().PlayDropItem(____heldItem.GetItemType()); var hitGameObject = hit.collider.gameObject; @@ -65,13 +65,21 @@ namespace QSB.ItemSync.Patches if (sectorGroup != null) { sector = sectorGroup.GetSector(); + if (sector == null && sectorGroup is SectorCullGroup) +{ + SectorProxy controllingProxy = (sectorGroup as SectorCullGroup).GetControllingProxy(); + if (controllingProxy != null) + { + sector = controllingProxy.GetSector(); + } + } } - var parent = (detachableFragment != null) - ? detachableFragment.transform - : targetRigidbody.transform; + var parent = (customDropTarget == null) + ? targetRigidbody.transform + : customDropTarget.GetItemDropTargetTransform(hit.collider.gameObject); var objectId = QSBWorldSync.GetIdFromTypeSubset(ItemManager.GetObject(____heldItem)); - ____heldItem.DropItem(hit.point, hit.normal, parent, sector, detachableFragment); + ____heldItem.DropItem(hit.point, hit.normal, parent, sector, customDropTarget); ____heldItem = null; Locator.GetToolModeSwapper().UnequipTool(); var parentSector = parent.GetComponentInChildren(); diff --git a/QSB/Patches/QSBPatch.cs b/QSB/Patches/QSBPatch.cs index da2d2a8f..49d2e894 100644 --- a/QSB/Patches/QSBPatch.cs +++ b/QSB/Patches/QSBPatch.cs @@ -130,6 +130,7 @@ namespace QSB.Patches private void Unpatch(MethodInfo method) { + /* var dictionary = typeof(HarmonySharedState).Invoke>("GetState", new object[0]); var methodBase = dictionary.Keys.First(m => m.DeclaringType == method.DeclaringType @@ -142,6 +143,7 @@ namespace QSB.Patches PatchFunctions.UpdateWrapper(methodBase, patchInfo, QSBCore.Helper.Manifest.UniqueName); dictionary[methodBase] = patchInfo.Serialize(); + */ } } } \ No newline at end of file diff --git a/QSB/QSB.csproj b/QSB/QSB.csproj index a9a5f5fa..aa0e9cb0 100644 --- a/QSB/QSB.csproj +++ b/QSB/QSB.csproj @@ -9,9 +9,10 @@ Properties QSB QSB - v3.5 + v4.0 512 true + true @@ -33,77 +34,6 @@ OnBuildSuccess - - - $(GameDir)\OuterWilds_Data\Managed\Assembly-CSharp.dll - False - - - False - $(GameDir)\OuterWilds_Data\Managed\Assembly-CSharp-firstpass.dll - False - - - - - - - - - False - $(GameDir)\OuterWilds_Data\Managed\UnityEngine.dll - False - - - False - $(GameDir)\OuterWilds_Data\Managed\UnityEngine.AnimationModule.dll - False - - - $(GameDir)\OuterWilds_Data\Managed\UnityEngine.AudioModule.dll - False - - - $(GameDir)\OuterWilds_Data\Managed\UnityEngine.CoreModule.dll - False - - - False - $(GameDir)\OuterWilds_Data\Managed\UnityEngine.IMGUIModule.dll - False - - - $(GameDir)\OuterWilds_Data\Managed\UnityEngine.Networking.dll - False - - - $(GameDir)\OuterWilds_Data\Managed\UnityEngine.ParticleSystemModule.dll - - - False - $(GameDir)\OuterWilds_Data\Managed\UnityEngine.PhysicsModule.dll - False - - - False - $(GameDir)\OuterWilds_Data\Managed\UnityEngine.TextRenderingModule.dll - False - - - False - $(GameDir)\OuterWilds_Data\Managed\UnityEngine.UI.dll - False - - - False - $(GameDir)\OuterWilds_Data\Managed\UnityEngine.UIModule.dll - False - - - $(GameDir)\OuterWilds_Data\Managed\UnityEngine.UNETModule.dll - False - - @@ -361,7 +291,6 @@ - @@ -401,25 +330,144 @@ + Always Always + + + + + ..\packages\HarmonyX.2.5.5\lib\net35\0Harmony.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\Assembly-CSharp.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\Assembly-CSharp-firstpass.dll + + + ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.dll + + + ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.Mdb.dll + + + ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.Pdb.dll + + + ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.Rocks.dll + + + ..\packages\MonoMod.RuntimeDetour.21.8.19.1\lib\net40\MonoMod.RuntimeDetour.dll + + + ..\packages\MonoMod.Utils.21.8.19.1\lib\net40\MonoMod.Utils.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\netstandard.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.Abstractions.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.Common.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.Logging.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.ModHelper.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.ModHelper.Assets.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.ModHelper.Events.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.ModHelper.Input.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.ModHelper.Interaction.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.ModHelper.Menus.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.ModLoader.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.Utils.dll + + + ..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.dll + + + False + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\Unity.InputSystem.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.AnimationModule.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.AssetBundleModule.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.AudioModule.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.CoreModule.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.IMGUIModule.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.InputLegacyModule.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.InputModule.dll + + + D:\EpicGames\OuterWilds\OuterWilds_Data\Managed\UnityEngine.Networking.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.ParticleSystemModule.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.PhysicsModule.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.TextCoreModule.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.TextRenderingModule.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.UI.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.UIModule.dll + + + D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.UNETModule.dll + - {C8C53004-1508-4F86-A419-4292C188DC2A} + {c8c53004-1508-4f86-a419-4292c188dc2a} QuantumUNET - - 1.1.8 - + - md "$(OwmlDir)\Mods\$(ProjectName)" @@ -430,9 +478,11 @@ copy /y "$(ProjectDir)\default-config.json" "$(OwmlDir)\Mods\$(ProjectName)" copy /y "$(SolutionDir)\AssetBundles" "$(OwmlDir)\Mods\$(ProjectName)\assets" copy /y "$(ProjectDir)\manifest.json" "$(OwmlDir)\Mods\$(ProjectName)" -"$(SolutionDir)\QNetWeaver\bin\Debug\QNetWeaver.exe" "$(GameDir)\OuterWilds_Data\Managed\UnityEngine.CoreModule.dll" "$(OwmlDir)\Mods\$(ProjectName)\QuantumUNET.dll" "$(GameDir)\OuterWilds_Data\Managed\UnityEngine.Networking.dll" "$(SolutionDir)\WeavedFiles" "$(TargetPath)" +"$(SolutionDir)\QNetWeaver\bin\Debug\QNetWeaver.exe" "$(GameDir)\OuterWilds_Data\Managed\UnityEngine.CoreModule.dll" "$(OwmlDir)\Mods\$(ProjectName)\QuantumUNET.dll" "$(ProjectDir)\lib\UnityEngine.Networking.dll" "$(SolutionDir)\WeavedFiles" "$(TargetPath)" -copy /y "$(SolutionDir)\WeavedFiles\QSB.dll" "$(OwmlDir)\Mods\$(ProjectName)" +copy /y "$(SolutionDir)\WeavedFiles\QSB.dll" "$(OwmlDir)\Mods\$(ProjectName)" + +xcopy /y "$(ProjectDir)\lib" "$(OwmlDir)\Mods\$(ProjectName)" diff --git a/QSB/QSBCore.cs b/QSB/QSBCore.cs index 51e3ebbb..8f58c7b2 100644 --- a/QSB/QSBCore.cs +++ b/QSB/QSBCore.cs @@ -79,7 +79,7 @@ namespace QSB Helper = ModHelper; DebugLog.ToConsole($"* Start of QSB version {QSBVersion} - authored by {Helper.Manifest.Author}", MessageType.Info); - MenuApi = ModHelper.Interaction.GetModApi("_nebula.MenuFramework"); + //MenuApi = ModHelper.Interaction.GetModApi("_nebula.MenuFramework"); NetworkAssetBundle = Helper.Assets.LoadBundle("assets/network"); InstrumentAssetBundle = Helper.Assets.LoadBundle("assets/instruments"); @@ -95,8 +95,9 @@ namespace QSB gameObject.AddComponent(); gameObject.AddComponent(); gameObject.AddComponent(); - gameObject.AddComponent(); + //gameObject.AddComponent(); gameObject.AddComponent(); + gameObject.AddComponent(); // WorldObject managers gameObject.AddComponent(); diff --git a/QSB/Utility/DebugActions.cs b/QSB/Utility/DebugActions.cs index 5e613f1b..f0a5b5e8 100644 --- a/QSB/Utility/DebugActions.cs +++ b/QSB/Utility/DebugActions.cs @@ -29,6 +29,8 @@ namespace QSB.Utility public void Update() { + return; + if (!QSBCore.DebugMode) { return; diff --git a/QSB/app.config b/QSB/app.config new file mode 100644 index 00000000..2a4bf252 --- /dev/null +++ b/QSB/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/QSB/lib/UnityEngine.Networking.dll b/QSB/lib/UnityEngine.Networking.dll new file mode 100644 index 0000000000000000000000000000000000000000..4db9ce9db7d026ae40b44737742a57e922b0dede GIT binary patch literal 255488 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 !HNb4Dd?eW^q^jud~N}>V3%kKQE!R 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 + + + + + + \ No newline at end of file diff --git a/QSBTests/QSBTests.csproj b/QSBTests/QSBTests.csproj index fa834982..273e4303 100644 --- a/QSBTests/QSBTests.csproj +++ b/QSBTests/QSBTests.csproj @@ -58,6 +58,7 @@ + diff --git a/QSBTests/QSBTests.csproj.user b/QSBTests/QSBTests.csproj.user index c8a11e84..3f79bad7 100644 --- a/QSBTests/QSBTests.csproj.user +++ b/QSBTests/QSBTests.csproj.user @@ -1,7 +1,7 @@  - D:\EpicGames\OuterWilds + D:\SteamLibrary\steamapps\common\Outer Wilds C:\Users\Henry\AppData\Roaming\OuterWildsModManager\OWML ShowAllFiles diff --git a/QSBTests/app.config b/QSBTests/app.config new file mode 100644 index 00000000..2a4bf252 --- /dev/null +++ b/QSBTests/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/QuantumUNET/Components/QNetworkManagerHUD.cs b/QuantumUNET/Components/QNetworkManagerHUD.cs new file mode 100644 index 00000000..7ef531bf --- /dev/null +++ b/QuantumUNET/Components/QNetworkManagerHUD.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using UnityEngine; +using UnityEngine.Networking.Match; + +namespace QuantumUNET.Components +{ + [EditorBrowsable(EditorBrowsableState.Never)] + public class QNetworkManagerHUD : MonoBehaviour + { + private void Awake() + { + this.manager = base.GetComponent(); + } + + private void OnGUI() + { + if (this.showGUI) + { + int num = 10 + this.offsetX; + int num2 = 40 + this.offsetY; + bool flag = this.manager.client == null || this.manager.client.connection == null || this.manager.client.connection.connectionId == -1; + if (!this.manager.IsClientConnected() && !QNetworkServer.active) + { + if (flag) + { + if (Application.platform != RuntimePlatform.WebGLPlayer) + { + if (GUI.Button(new Rect((float)num, (float)num2, 200f, 20f), "LAN Host(H)")) + { + this.manager.StartHost(); + } + num2 += 24; + } + if (GUI.Button(new Rect((float)num, (float)num2, 105f, 20f), "LAN Client(C)")) + { + this.manager.StartClient(); + } + this.manager.networkAddress = GUI.TextField(new Rect((float)(num + 100), (float)num2, 95f, 20f), this.manager.networkAddress); + num2 += 24; + if (Application.platform == RuntimePlatform.WebGLPlayer) + { + GUI.Box(new Rect((float)num, (float)num2, 200f, 25f), "( WebGL cannot be server )"); + num2 += 24; + } + else + { + if (GUI.Button(new Rect((float)num, (float)num2, 200f, 20f), "LAN Server Only(S)")) + { + this.manager.StartServer(); + } + num2 += 24; + } + } + else + { + GUI.Label(new Rect((float)num, (float)num2, 200f, 20f), string.Concat(new object[] + { + "Connecting to ", + this.manager.networkAddress, + ":", + this.manager.networkPort, + ".." + })); + num2 += 24; + if (GUI.Button(new Rect((float)num, (float)num2, 200f, 20f), "Cancel Connection Attempt")) + { + this.manager.StopClient(); + } + } + } + else + { + if (QNetworkServer.active) + { + string text = "Server: port=" + this.manager.networkPort; + GUI.Label(new Rect((float)num, (float)num2, 300f, 20f), text); + num2 += 24; + } + if (this.manager.IsClientConnected()) + { + GUI.Label(new Rect((float)num, (float)num2, 300f, 20f), string.Concat(new object[] + { + "Client: address=", + this.manager.networkAddress, + " port=", + this.manager.networkPort + })); + num2 += 24; + } + } + + if (this.manager.IsClientConnected() && !QClientScene.ready) + { + if (GUI.Button(new Rect((float)num, (float)num2, 200f, 20f), "Client Ready")) + { + QClientScene.Ready(this.manager.client.connection); + if (QClientScene.localPlayers.Count == 0) + { + QClientScene.AddPlayer(0); + } + } + num2 += 24; + } + + if (QNetworkServer.active || this.manager.IsClientConnected()) + { + if (GUI.Button(new Rect((float)num, (float)num2, 200f, 20f), "Stop (X)")) + { + this.manager.StopHost(); + } + num2 += 24; + } + } + } + + public QNetworkManager manager; + + [SerializeField] + public bool showGUI = true; + + [SerializeField] + public int offsetX; + + [SerializeField] + public int offsetY; + + private bool m_ShowServer; + } +} diff --git a/QuantumUNET/QuantumUNET.csproj b/QuantumUNET/QuantumUNET.csproj index 74f0f29a..861b1034 100644 --- a/QuantumUNET/QuantumUNET.csproj +++ b/QuantumUNET/QuantumUNET.csproj @@ -65,6 +65,7 @@ + diff --git a/UnityEngine.Networking.dll b/UnityEngine.Networking.dll new file mode 100644 index 0000000000000000000000000000000000000000..4db9ce9db7d026ae40b44737742a57e922b0dede GIT binary patch literal 255488 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 !HNb4Dd?eW^q^jud~N}>V3%kKQE!R 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 Date: Tue, 12 Oct 2021 21:03:21 +0100 Subject: [PATCH 104/118] use owml package --- QSB/Patches/QSBPatch.cs | 4 +-- QSB/QSB.csproj | 63 ++++++----------------------------------- QSB/packages.config | 7 ----- 3 files changed, 9 insertions(+), 65 deletions(-) delete mode 100644 QSB/packages.config diff --git a/QSB/Patches/QSBPatch.cs b/QSB/Patches/QSBPatch.cs index 49d2e894..20844c25 100644 --- a/QSB/Patches/QSBPatch.cs +++ b/QSB/Patches/QSBPatch.cs @@ -1,6 +1,4 @@ -using HarmonyLib; -using OWML.Common; -using OWML.Utils; +using OWML.Common; using QSB.Utility; using System; using System.Collections.Generic; diff --git a/QSB/QSB.csproj b/QSB/QSB.csproj index aa0e9cb0..0fdf46cd 100644 --- a/QSB/QSB.csproj +++ b/QSB/QSB.csproj @@ -337,72 +337,17 @@ Always - - - ..\packages\HarmonyX.2.5.5\lib\net35\0Harmony.dll - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\Assembly-CSharp.dll D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\Assembly-CSharp-firstpass.dll - - ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.dll - - - ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.Mdb.dll - - - ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.Pdb.dll - - - ..\packages\Mono.Cecil.0.11.4\lib\net40\Mono.Cecil.Rocks.dll - - - ..\packages\MonoMod.RuntimeDetour.21.8.19.1\lib\net40\MonoMod.RuntimeDetour.dll - - - ..\packages\MonoMod.Utils.21.8.19.1\lib\net40\MonoMod.Utils.dll - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\netstandard.dll - - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.Abstractions.dll - - - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.Common.dll - - - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.Logging.dll - - - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.ModHelper.dll - - - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.ModHelper.Assets.dll - - - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.ModHelper.Events.dll - - - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.ModHelper.Input.dll - - - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.ModHelper.Interaction.dll - - - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.ModHelper.Menus.dll - - - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.ModLoader.dll - - - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\OWML.Utils.dll - ..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.dll @@ -468,6 +413,14 @@ + + + 2.5.5 + + + 2.0.0 + + md "$(OwmlDir)\Mods\$(ProjectName)" diff --git a/QSB/packages.config b/QSB/packages.config deleted file mode 100644 index b22a921b..00000000 --- a/QSB/packages.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file From d88785038003d85ca79997cd4faee12a7b5b6247 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Wed, 13 Oct 2021 18:06:14 +0100 Subject: [PATCH 105/118] update unity project --- UnityProject/Assets/Resources.meta | 8 ++ .../Assets/Resources/BillingMode.json | 1 + .../Assets/Resources/BillingMode.json.meta | 7 ++ .../Assets/UnityEngine.Networking.dll | Bin 0 -> 255488 bytes .../Assets/UnityEngine.Networking.dll.meta | 69 ++++++++++++++++++ .../ProjectSettings/GraphicsSettings.asset | 5 +- .../PackageManagerSettings.asset | 38 ++++++++++ .../ProjectSettings/PresetManager.asset | 7 ++ .../ProjectSettings/ProjectVersion.txt | 3 +- UnityProject/ProjectSettings/VFXManager.asset | 12 +++ UnityProject/ProjectSettings/XRSettings.asset | 10 +++ .../UnityPackageManager/manifest.json | 4 - 12 files changed, 158 insertions(+), 6 deletions(-) create mode 100644 UnityProject/Assets/Resources.meta create mode 100644 UnityProject/Assets/Resources/BillingMode.json create mode 100644 UnityProject/Assets/Resources/BillingMode.json.meta create mode 100644 UnityProject/Assets/UnityEngine.Networking.dll create mode 100644 UnityProject/Assets/UnityEngine.Networking.dll.meta create mode 100644 UnityProject/ProjectSettings/PackageManagerSettings.asset create mode 100644 UnityProject/ProjectSettings/PresetManager.asset create mode 100644 UnityProject/ProjectSettings/VFXManager.asset create mode 100644 UnityProject/ProjectSettings/XRSettings.asset delete mode 100644 UnityProject/UnityPackageManager/manifest.json diff --git a/UnityProject/Assets/Resources.meta b/UnityProject/Assets/Resources.meta new file mode 100644 index 00000000..4e0fc546 --- /dev/null +++ b/UnityProject/Assets/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6defefa8ffddeff4881a8fbdeaa56b0d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Resources/BillingMode.json b/UnityProject/Assets/Resources/BillingMode.json new file mode 100644 index 00000000..6f4bfb71 --- /dev/null +++ b/UnityProject/Assets/Resources/BillingMode.json @@ -0,0 +1 @@ +{"androidStore":"GooglePlay"} \ No newline at end of file diff --git a/UnityProject/Assets/Resources/BillingMode.json.meta b/UnityProject/Assets/Resources/BillingMode.json.meta new file mode 100644 index 00000000..ba2a1232 --- /dev/null +++ b/UnityProject/Assets/Resources/BillingMode.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a7ad16210ad1ba94187ddafb875b51cf +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/UnityEngine.Networking.dll b/UnityProject/Assets/UnityEngine.Networking.dll new file mode 100644 index 0000000000000000000000000000000000000000..4db9ce9db7d026ae40b44737742a57e922b0dede GIT binary patch literal 255488 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 !HNb4Dd?eW^q^jud~N}>V3%kKQE!R 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 Date: Wed, 13 Oct 2021 18:06:35 +0100 Subject: [PATCH 106/118] update assetbundles --- AssetBundles/conversation | Bin 3143 -> 3217 bytes AssetBundles/conversation.manifest | 15 +- AssetBundles/debug | Bin 2859 -> 2876 bytes AssetBundles/debug.manifest | 11 +- AssetBundles/network | Bin 2867 -> 3001 bytes AssetBundles/network.manifest | 15 +- .../Assets/NETWORK_Player_Body.prefab | 349 +++++++++--------- UnityProject/Assets/NetworkOrb.prefab | 101 +++-- UnityProject/Assets/NetworkProbe.prefab | 73 ++-- UnityProject/Assets/NetworkShip.prefab | 77 ++-- 10 files changed, 316 insertions(+), 325 deletions(-) diff --git a/AssetBundles/conversation b/AssetBundles/conversation index 9dd2f86f8e35333054073221379d2133235b987b..b1cd41e839d231ce7a9770b56edcb1cc6e496a30 100644 GIT binary patch delta 3197 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 j>5VczzdNZq*H4 delta 3122 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 MDNSVxulu_}u9Qa5h5!Hn diff --git a/AssetBundles/conversation.manifest b/AssetBundles/conversation.manifest index a496a95a..2759cf64 100644 --- a/AssetBundles/conversation.manifest +++ b/AssetBundles/conversation.manifest @@ -1,24 +1,24 @@ ManifestFileVersion: 0 -CRC: 3469869292 +CRC: 1518538429 Hashes: AssetFileHash: serializedVersion: 2 - Hash: 55d90dfc169d4fd552679840c1474222 + Hash: cda9c91e090f27b94734bfca3a7cb9e2 TypeTreeHash: serializedVersion: 2 - Hash: 6ce89620af51ba381c9e4f226a2ebb1b + Hash: 90db08ff16f71e0f4005d38a4650eff2 HashAppended: 0 ClassTypes: - Class: 1 Script: {instanceID: 0} - Class: 114 - Script: {fileID: 1741964061, guid: f70555f144d8491a825f0804e09c671c, type: 3} + Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} - Class: 114 - Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} - Class: 114 - Script: {fileID: -900027084, guid: f70555f144d8491a825f0804e09c671c, type: 3} + Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3} - Class: 114 - Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + Script: {fileID: 11500000, guid: e19747de3f5aca642ab2be37e372fb86, type: 3} - Class: 115 Script: {instanceID: 0} - Class: 222 @@ -27,6 +27,7 @@ ClassTypes: Script: {instanceID: 0} - Class: 224 Script: {instanceID: 0} +SerializeReferenceClassIdentifiers: [] Assets: - Assets/DialogueBubble.prefab Dependencies: [] diff --git a/AssetBundles/debug b/AssetBundles/debug index f50e124a558a3d2799e62fb08d1ad104afebcf6b..8931c62999ed36f0bbc184c8cac9a5f0fca7b23d 100644 GIT binary patch delta 2853 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 diff --git a/AssetBundles/network.manifest b/AssetBundles/network.manifest index b21b059e..558463e8 100644 --- a/AssetBundles/network.manifest +++ b/AssetBundles/network.manifest @@ -1,12 +1,12 @@ ManifestFileVersion: 0 -CRC: 3621554448 +CRC: 4070870812 Hashes: AssetFileHash: serializedVersion: 2 - Hash: c9002a0b83220186e94179a065236b5c + Hash: 29833081f68126f69e50212247d21be7 TypeTreeHash: serializedVersion: 2 - Hash: 8b6abf066340f652e25eed06e6b72102 + Hash: 51df0ad50c79617520b68dc5fc2a08cf HashAppended: 0 ClassTypes: - Class: 1 @@ -14,13 +14,16 @@ ClassTypes: - Class: 4 Script: {instanceID: 0} - Class: 114 - Script: {fileID: 372142912, guid: dc443db3e92b4983b9738c1131f555cb, type: 3} + Script: {fileID: 372142912, guid: 93b08009869340045a8e7321508b6355, type: 3} - Class: 114 - Script: {fileID: -1768714887, guid: dc443db3e92b4983b9738c1131f555cb, type: 3} + Script: {fileID: -1768714887, guid: 93b08009869340045a8e7321508b6355, type: 3} - Class: 114 - Script: {fileID: -1267208747, guid: dc443db3e92b4983b9738c1131f555cb, type: 3} + Script: {fileID: -1267208747, guid: 93b08009869340045a8e7321508b6355, type: 3} +- Class: 114 + Script: {fileID: 11500000, guid: f8c40b79b6cc65d4e904a1e931638b0f, type: 3} - Class: 115 Script: {instanceID: 0} +SerializeReferenceClassIdentifiers: [] Assets: - Assets/NetworkProbe.prefab - Assets/NETWORK_Player_Body.prefab diff --git a/UnityProject/Assets/NETWORK_Player_Body.prefab b/UnityProject/Assets/NETWORK_Player_Body.prefab index 79815120..dcc96f86 100644 --- a/UnityProject/Assets/NETWORK_Player_Body.prefab +++ b/UnityProject/Assets/NETWORK_Player_Body.prefab @@ -1,22 +1,12 @@ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: ---- !u!1001 &100100000 -Prefab: - m_ObjectHideFlags: 1 - serializedVersion: 2 - m_Modification: - m_TransformParent: {fileID: 0} - m_Modifications: [] - m_RemovedComponents: [] - m_ParentPrefab: {fileID: 0} - m_RootGameObject: {fileID: 1994424166804230} - m_IsPrefabParent: 1 --- !u!1 &1063531587744832 GameObject: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} - serializedVersion: 5 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 m_Component: - component: {fileID: 4537338579920700} m_Layer: 0 @@ -26,12 +16,27 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 +--- !u!4 &4537338579920700 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1063531587744832} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4232815760244474} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1268162395208754 GameObject: m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} - serializedVersion: 5 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 m_Component: - component: {fileID: 4362517717386354} m_Layer: 0 @@ -41,12 +46,27 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 +--- !u!4 &4362517717386354 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1268162395208754} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0.8496093, z: 0.1500003} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4311429169693626} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1491989937895418 GameObject: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} - serializedVersion: 5 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 m_Component: - component: {fileID: 4232815760244474} m_Layer: 0 @@ -56,12 +76,28 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 +--- !u!4 &4232815760244474 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1491989937895418} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 2} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4537338579920700} + m_Father: {fileID: 4361850152643004} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1677454195273572 GameObject: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} - serializedVersion: 5 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 m_Component: - component: {fileID: 4361850152643004} m_Layer: 0 @@ -71,12 +107,28 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 +--- !u!4 &4361850152643004 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1677454195273572} + m_LocalRotation: {x: 0, y: -0.08715578, z: 0, w: 0.9961947} + m_LocalPosition: {x: 0.25, y: 0, z: 0.08} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4232815760244474} + m_Father: {fileID: 4773365960669392} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: -10, z: 0} --- !u!1 &1813708233350436 GameObject: m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} - serializedVersion: 5 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 m_Component: - component: {fileID: 4773365960669392} m_Layer: 0 @@ -86,12 +138,28 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 +--- !u!4 &4773365960669392 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1813708233350436} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.4, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4361850152643004} + m_Father: {fileID: 4311429169693626} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1994424166804230 GameObject: m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} - serializedVersion: 5 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 m_Component: - component: {fileID: 4311429169693626} - component: {fileID: 114683588823772616} @@ -107,25 +175,12 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &4232815760244474 -Transform: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} - m_GameObject: {fileID: 1491989937895418} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 2} - m_LocalScale: {x: 1, y: 1, z: 1} - m_Children: - - {fileID: 4537338579920700} - m_Father: {fileID: 4361850152643004} - m_RootOrder: 0 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!4 &4311429169693626 Transform: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1994424166804230} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} @@ -136,69 +191,49 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!4 &4361850152643004 -Transform: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} - m_GameObject: {fileID: 1677454195273572} - m_LocalRotation: {x: 0, y: -0.08715578, z: 0, w: 0.9961947} - m_LocalPosition: {x: 0.25, y: 0, z: 0.08} - m_LocalScale: {x: 1, y: 1, z: 1} - m_Children: - - {fileID: 4232815760244474} - m_Father: {fileID: 4773365960669392} - m_RootOrder: 0 - m_LocalEulerAnglesHint: {x: 0, y: -10, z: 0} ---- !u!4 &4362517717386354 -Transform: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} - m_GameObject: {fileID: 1268162395208754} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0.8496093, z: 0.1500003} - m_LocalScale: {x: 1, y: 1, z: 1} - m_Children: [] - m_Father: {fileID: 4311429169693626} - m_RootOrder: 0 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!4 &4537338579920700 -Transform: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} - m_GameObject: {fileID: 1063531587744832} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_Children: [] - m_Father: {fileID: 4232815760244474} - m_RootOrder: 0 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!4 &4773365960669392 -Transform: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} - m_GameObject: {fileID: 1813708233350436} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0.4, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_Children: - - {fileID: 4361850152643004} - m_Father: {fileID: 4311429169693626} - m_RootOrder: 1 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!114 &114047126095319432 +--- !u!114 &114683588823772616 MonoBehaviour: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1994424166804230} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -1768714887, guid: dc443db3e92b4983b9738c1131f555cb, type: 3} + m_Script: {fileID: 372142912, guid: 93b08009869340045a8e7321508b6355, type: 3} + m_Name: + m_EditorClassIdentifier: + m_SceneId: + m_Value: 0 + m_AssetId: + i0: 0 + i1: 0 + i2: 0 + i3: 0 + i4: 0 + i5: 0 + i6: 0 + i7: 0 + i8: 0 + i9: 0 + i10: 0 + i11: 0 + i12: 0 + i13: 0 + i14: 0 + i15: 1 + m_ServerOnly: 0 + m_LocalPlayerAuthority: 1 +--- !u!114 &114047126095319432 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1994424166804230} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -1768714887, guid: 93b08009869340045a8e7321508b6355, type: 3} m_Name: m_EditorClassIdentifier: m_TransformSyncMode: 1 @@ -213,13 +248,14 @@ MonoBehaviour: m_InterpolateMovement: 1 --- !u!114 &114320751842596664 MonoBehaviour: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1994424166804230} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -1267208747, guid: dc443db3e92b4983b9738c1131f555cb, type: 3} + m_Script: {fileID: -1267208747, guid: 93b08009869340045a8e7321508b6355, type: 3} m_Name: m_EditorClassIdentifier: m_Target: {fileID: 4362517717386354} @@ -230,34 +266,16 @@ MonoBehaviour: m_MovementThreshold: 0.001 m_InterpolateRotation: 0 m_InterpolateMovement: 0 ---- !u!114 &114340969760283228 -MonoBehaviour: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} - m_GameObject: {fileID: 1994424166804230} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: -1267208747, guid: dc443db3e92b4983b9738c1131f555cb, type: 3} - m_Name: - m_EditorClassIdentifier: - m_Target: {fileID: 4773365960669392} - m_ChildIndex: 3 - m_SendInterval: 0.0625 - m_SyncRotationAxis: 7 - m_RotationSyncCompression: 0 - m_MovementThreshold: 0.001 - m_InterpolateRotation: 0 - m_InterpolateMovement: 0 --- !u!114 &114492380483131480 MonoBehaviour: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1994424166804230} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -1267208747, guid: dc443db3e92b4983b9738c1131f555cb, type: 3} + m_Script: {fileID: -1267208747, guid: 93b08009869340045a8e7321508b6355, type: 3} m_Name: m_EditorClassIdentifier: m_Target: {fileID: 4232815760244474} @@ -268,47 +286,16 @@ MonoBehaviour: m_MovementThreshold: 0.001 m_InterpolateRotation: 0 m_InterpolateMovement: 0 ---- !u!114 &114683588823772616 -MonoBehaviour: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} - m_GameObject: {fileID: 1994424166804230} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 372142912, guid: dc443db3e92b4983b9738c1131f555cb, type: 3} - m_Name: - m_EditorClassIdentifier: - m_SceneId: - m_Value: 0 - m_AssetId: - i0: 20 - i1: 171 - i2: 184 - i3: 22 - i4: 34 - i5: 48 - i6: 235 - i7: 20 - i8: 219 - i9: 104 - i10: 203 - i11: 240 - i12: 254 - i13: 203 - i14: 228 - i15: 206 - m_ServerOnly: 0 - m_LocalPlayerAuthority: 1 --- !u!114 &114827007418351406 MonoBehaviour: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1994424166804230} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -1267208747, guid: dc443db3e92b4983b9738c1131f555cb, type: 3} + m_Script: {fileID: -1267208747, guid: 93b08009869340045a8e7321508b6355, type: 3} m_Name: m_EditorClassIdentifier: m_Target: {fileID: 4537338579920700} @@ -319,3 +306,23 @@ MonoBehaviour: m_MovementThreshold: 0.001 m_InterpolateRotation: 0 m_InterpolateMovement: 0 +--- !u!114 &114340969760283228 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1994424166804230} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -1267208747, guid: 93b08009869340045a8e7321508b6355, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Target: {fileID: 4773365960669392} + m_ChildIndex: 3 + m_SendInterval: 0.0625 + m_SyncRotationAxis: 7 + m_RotationSyncCompression: 0 + m_MovementThreshold: 0.001 + m_InterpolateRotation: 0 + m_InterpolateMovement: 0 diff --git a/UnityProject/Assets/NetworkOrb.prefab b/UnityProject/Assets/NetworkOrb.prefab index fbe3bdd0..0791274c 100644 --- a/UnityProject/Assets/NetworkOrb.prefab +++ b/UnityProject/Assets/NetworkOrb.prefab @@ -1,22 +1,12 @@ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: ---- !u!1001 &100100000 -Prefab: - m_ObjectHideFlags: 1 - serializedVersion: 2 - m_Modification: - m_TransformParent: {fileID: 0} - m_Modifications: [] - m_RemovedComponents: [] - m_ParentPrefab: {fileID: 0} - m_RootGameObject: {fileID: 1252242529042024} - m_IsPrefabParent: 1 --- !u!1 &1252242529042024 GameObject: m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} - serializedVersion: 5 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 m_Component: - component: {fileID: 4745039063230268} - component: {fileID: 114872848302743272} @@ -30,9 +20,10 @@ GameObject: m_IsActive: 1 --- !u!4 &4745039063230268 Transform: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1252242529042024} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} @@ -41,15 +32,49 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!114 &114240702864939342 +--- !u!114 &114872848302743272 MonoBehaviour: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1252242529042024} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -1768714887, guid: dc443db3e92b4983b9738c1131f555cb, type: 3} + m_Script: {fileID: 372142912, guid: 93b08009869340045a8e7321508b6355, type: 3} + m_Name: + m_EditorClassIdentifier: + m_SceneId: + m_Value: 0 + m_AssetId: + i0: 0 + i1: 0 + i2: 0 + i3: 0 + i4: 0 + i5: 0 + i6: 0 + i7: 0 + i8: 0 + i9: 0 + i10: 0 + i11: 0 + i12: 0 + i13: 0 + i14: 0 + i15: 0 + m_ServerOnly: 0 + m_LocalPlayerAuthority: 1 +--- !u!114 &114240702864939342 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1252242529042024} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -1768714887, guid: 93b08009869340045a8e7321508b6355, type: 3} m_Name: m_EditorClassIdentifier: m_TransformSyncMode: 1 @@ -62,35 +87,3 @@ MonoBehaviour: m_SnapThreshold: 5 m_InterpolateRotation: 1 m_InterpolateMovement: 1 ---- !u!114 &114872848302743272 -MonoBehaviour: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} - m_GameObject: {fileID: 1252242529042024} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 372142912, guid: dc443db3e92b4983b9738c1131f555cb, type: 3} - m_Name: - m_EditorClassIdentifier: - m_SceneId: - m_Value: 0 - m_AssetId: - i0: 16 - i1: 54 - i2: 85 - i3: 231 - i4: 191 - i5: 68 - i6: 98 - i7: 228 - i8: 25 - i9: 191 - i10: 115 - i11: 12 - i12: 196 - i13: 32 - i14: 194 - i15: 197 - m_ServerOnly: 0 - m_LocalPlayerAuthority: 1 diff --git a/UnityProject/Assets/NetworkProbe.prefab b/UnityProject/Assets/NetworkProbe.prefab index 5f5ec489..04e8c8c2 100644 --- a/UnityProject/Assets/NetworkProbe.prefab +++ b/UnityProject/Assets/NetworkProbe.prefab @@ -1,22 +1,12 @@ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: ---- !u!1001 &100100000 -Prefab: - m_ObjectHideFlags: 1 - serializedVersion: 2 - m_Modification: - m_TransformParent: {fileID: 0} - m_Modifications: [] - m_RemovedComponents: [] - m_ParentPrefab: {fileID: 0} - m_RootGameObject: {fileID: 1858162522537180} - m_IsPrefabParent: 1 --- !u!1 &1858162522537180 GameObject: m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} - serializedVersion: 5 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 m_Component: - component: {fileID: 4383151934826088} - component: {fileID: 114893399387826744} @@ -30,9 +20,10 @@ GameObject: m_IsActive: 1 --- !u!4 &4383151934826088 Transform: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1858162522537180} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} @@ -43,45 +34,47 @@ Transform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &114893399387826744 MonoBehaviour: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1858162522537180} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 372142912, guid: dc443db3e92b4983b9738c1131f555cb, type: 3} + m_Script: {fileID: 372142912, guid: 93b08009869340045a8e7321508b6355, type: 3} m_Name: m_EditorClassIdentifier: m_SceneId: m_Value: 0 m_AssetId: - i0: 50 - i1: 8 - i2: 170 - i3: 6 - i4: 228 - i5: 100 - i6: 140 - i7: 212 - i8: 139 - i9: 151 - i10: 254 + i0: 0 + i1: 0 + i2: 0 + i3: 0 + i4: 0 + i5: 0 + i6: 0 + i7: 0 + i8: 0 + i9: 0 + i10: 0 i11: 0 - i12: 186 - i13: 221 - i14: 21 - i15: 2 + i12: 0 + i13: 0 + i14: 0 + i15: 0 m_ServerOnly: 0 m_LocalPlayerAuthority: 1 --- !u!114 &114960003525639280 MonoBehaviour: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1858162522537180} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -1768714887, guid: dc443db3e92b4983b9738c1131f555cb, type: 3} + m_Script: {fileID: -1768714887, guid: 93b08009869340045a8e7321508b6355, type: 3} m_Name: m_EditorClassIdentifier: m_TransformSyncMode: 1 diff --git a/UnityProject/Assets/NetworkShip.prefab b/UnityProject/Assets/NetworkShip.prefab index d93ec68b..75785cfa 100644 --- a/UnityProject/Assets/NetworkShip.prefab +++ b/UnityProject/Assets/NetworkShip.prefab @@ -1,22 +1,12 @@ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: ---- !u!1001 &100100000 -Prefab: - m_ObjectHideFlags: 1 - serializedVersion: 2 - m_Modification: - m_TransformParent: {fileID: 0} - m_Modifications: [] - m_RemovedComponents: [] - m_ParentPrefab: {fileID: 0} - m_RootGameObject: {fileID: 1651223990902024} - m_IsPrefabParent: 1 --- !u!1 &1651223990902024 GameObject: m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} - serializedVersion: 5 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 m_Component: - component: {fileID: 4928834428794828} - component: {fileID: 114173126939784982} @@ -30,9 +20,10 @@ GameObject: m_IsActive: 1 --- !u!4 &4928834428794828 Transform: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1651223990902024} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} @@ -43,45 +34,47 @@ Transform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &114173126939784982 MonoBehaviour: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1651223990902024} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 372142912, guid: dc443db3e92b4983b9738c1131f555cb, type: 3} + m_Script: {fileID: 372142912, guid: 93b08009869340045a8e7321508b6355, type: 3} m_Name: m_EditorClassIdentifier: m_SceneId: m_Value: 0 m_AssetId: - i0: 140 - i1: 195 - i2: 85 - i3: 223 - i4: 109 - i5: 103 - i6: 105 - i7: 228 - i8: 74 - i9: 55 - i10: 186 - i11: 128 - i12: 89 - i13: 228 - i14: 145 - i15: 61 + i0: 0 + i1: 0 + i2: 0 + i3: 0 + i4: 0 + i5: 0 + i6: 0 + i7: 0 + i8: 0 + i9: 0 + i10: 0 + i11: 0 + i12: 0 + i13: 0 + i14: 0 + i15: 0 m_ServerOnly: 0 - m_LocalPlayerAuthority: 1 + m_LocalPlayerAuthority: 0 --- !u!114 &114412165868198726 MonoBehaviour: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1651223990902024} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: -1768714887, guid: dc443db3e92b4983b9738c1131f555cb, type: 3} + m_Script: {fileID: -1768714887, guid: 93b08009869340045a8e7321508b6355, type: 3} m_Name: m_EditorClassIdentifier: m_TransformSyncMode: 1 From bf14f089b2f0b13a4e63f82cb04de58d3967d234 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Wed, 13 Oct 2021 18:09:16 +0100 Subject: [PATCH 107/118] change assetid to int --- QuantumUNET/Components/QNetworkIdentity.cs | 8 ++++---- QuantumUNET/Messages/QObjectSpawnMessage.cs | 4 ++-- QuantumUNET/Messages/QSpawnDelegate.cs | 2 +- QuantumUNET/QClientScene.cs | 12 +++++------ QuantumUNET/QNetworkScene.cs | 22 ++++++++++----------- QuantumUNET/QNetworkServer.cs | 6 +++--- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/QuantumUNET/Components/QNetworkIdentity.cs b/QuantumUNET/Components/QNetworkIdentity.cs index 1ee300ff..040e5905 100644 --- a/QuantumUNET/Components/QNetworkIdentity.cs +++ b/QuantumUNET/Components/QNetworkIdentity.cs @@ -17,7 +17,7 @@ namespace QuantumUNET.Components public NetworkInstanceId NetId { get; private set; } public NetworkSceneId SceneId => m_SceneId; public QNetworkConnection ClientAuthorityOwner { get; private set; } - public NetworkHash128 AssetId => m_AssetId; + public int AssetId => m_AssetId; public bool IsLocalPlayer { get; private set; } public short PlayerControllerId { get; private set; } = -1; public QNetworkConnection ConnectionToServer { get; private set; } @@ -57,9 +57,9 @@ namespace QuantumUNET.Components internal void RemoveSubIdentity(QNetworkIdentity identityToRemove) => SubIdentities.Remove(identityToRemove); - internal void SetDynamicAssetId(NetworkHash128 newAssetId) + internal void SetDynamicAssetId(int newAssetId) { - if (!m_AssetId.IsValid() || m_AssetId.Equals(newAssetId)) + if (m_AssetId == 0 || m_AssetId.Equals(newAssetId)) { m_AssetId = newAssetId; } @@ -821,7 +821,7 @@ namespace QuantumUNET.Components private NetworkSceneId m_SceneId; [SerializeField] - private NetworkHash128 m_AssetId; + private int m_AssetId; [SerializeField] private bool m_ServerOnly; diff --git a/QuantumUNET/Messages/QObjectSpawnMessage.cs b/QuantumUNET/Messages/QObjectSpawnMessage.cs index 9376e042..77cfcc90 100644 --- a/QuantumUNET/Messages/QObjectSpawnMessage.cs +++ b/QuantumUNET/Messages/QObjectSpawnMessage.cs @@ -7,7 +7,7 @@ namespace QuantumUNET.Messages internal class QObjectSpawnMessage : QMessageBase { public NetworkInstanceId NetId; - public NetworkHash128 assetId; + public int assetId; public Vector3 Position; public byte[] Payload; public Quaternion Rotation; @@ -24,7 +24,7 @@ namespace QuantumUNET.Messages public override void Deserialize(QNetworkReader reader) { NetId = reader.ReadNetworkId(); - assetId = reader.ReadNetworkHash128(); + assetId = reader.ReadInt32(); Position = reader.ReadVector3(); Payload = reader.ReadBytesAndSize(); if (reader.Length - reader.Position >= 16U) diff --git a/QuantumUNET/Messages/QSpawnDelegate.cs b/QuantumUNET/Messages/QSpawnDelegate.cs index 066c5271..f607acef 100644 --- a/QuantumUNET/Messages/QSpawnDelegate.cs +++ b/QuantumUNET/Messages/QSpawnDelegate.cs @@ -3,5 +3,5 @@ using UnityEngine.Networking; namespace QuantumUNET { - public delegate GameObject QSpawnDelegate(Vector3 position, NetworkHash128 assetId); + public delegate GameObject QSpawnDelegate(Vector3 position, int assetId); } diff --git a/QuantumUNET/QClientScene.cs b/QuantumUNET/QClientScene.cs index eb57c89d..7ae26289 100644 --- a/QuantumUNET/QClientScene.cs +++ b/QuantumUNET/QClientScene.cs @@ -21,7 +21,7 @@ namespace QuantumUNET public static Dictionary Objects => s_NetworkScene.localObjects; - public static Dictionary Prefabs => QNetworkScene.guidToPrefab; + public static Dictionary Prefabs => QNetworkScene.guidToPrefab; public static Dictionary SpawnableObjects { get; private set; } @@ -305,7 +305,7 @@ namespace QuantumUNET client.RegisterHandlerSafe(QMsgType.AnimationTrigger, QNetworkAnimator.OnAnimationTriggerClientMessage); } - internal static string GetStringForAssetId(NetworkHash128 assetId) + internal static string GetStringForAssetId(int assetId) { if (QNetworkScene.GetPrefab(assetId, out var gameObject)) { @@ -317,7 +317,7 @@ namespace QuantumUNET : "unknown"; } - public static void RegisterPrefab(GameObject prefab, NetworkHash128 newAssetId) => QNetworkScene.RegisterPrefab(prefab, newAssetId); + public static void RegisterPrefab(GameObject prefab, int newAssetId) => QNetworkScene.RegisterPrefab(prefab, newAssetId); public static void RegisterPrefab(GameObject prefab) => QNetworkScene.RegisterPrefab(prefab); @@ -325,9 +325,9 @@ namespace QuantumUNET public static void UnregisterPrefab(GameObject prefab) => QNetworkScene.UnregisterPrefab(prefab); - public static void RegisterSpawnHandler(NetworkHash128 assetId, QSpawnDelegate spawnHandler, UnSpawnDelegate unspawnHandler) => QNetworkScene.RegisterSpawnHandler(assetId, spawnHandler, unspawnHandler); + public static void RegisterSpawnHandler(int assetId, QSpawnDelegate spawnHandler, UnSpawnDelegate unspawnHandler) => QNetworkScene.RegisterSpawnHandler(assetId, spawnHandler, unspawnHandler); - public static void UnregisterSpawnHandler(NetworkHash128 assetId) => QNetworkScene.UnregisterSpawnHandler(assetId); + public static void UnregisterSpawnHandler(int assetId) => QNetworkScene.UnregisterSpawnHandler(assetId); public static void ClearSpawners() => QNetworkScene.ClearSpawners(); @@ -367,7 +367,7 @@ namespace QuantumUNET private static void OnObjectSpawn(QNetworkMessage netMsg) { netMsg.ReadMessage(s_ObjectSpawnMessage); - if (!s_ObjectSpawnMessage.assetId.IsValid()) + if (s_ObjectSpawnMessage.assetId == 0) { Debug.LogError($"OnObjSpawn netId: {s_ObjectSpawnMessage.NetId} has invalid asset Id. {s_ObjectSpawnMessage.assetId}"); } diff --git a/QuantumUNET/QNetworkScene.cs b/QuantumUNET/QNetworkScene.cs index 8704b830..643d3ccb 100644 --- a/QuantumUNET/QNetworkScene.cs +++ b/QuantumUNET/QNetworkScene.cs @@ -7,9 +7,9 @@ namespace QuantumUNET { internal class QNetworkScene { - internal static Dictionary guidToPrefab { get; } = new Dictionary(); - internal static Dictionary spawnHandlers { get; } = new Dictionary(); - internal static Dictionary unspawnHandlers { get; } = new Dictionary(); + internal static Dictionary guidToPrefab { get; } = new Dictionary(); + internal static Dictionary spawnHandlers { get; } = new Dictionary(); + internal static Dictionary unspawnHandlers { get; } = new Dictionary(); internal Dictionary localObjects { get; } = new Dictionary(); internal void Shutdown() @@ -96,7 +96,7 @@ namespace QuantumUNET internal void ClearLocalObjects() => localObjects.Clear(); - internal static void RegisterPrefab(GameObject prefab, NetworkHash128 newAssetId) + internal static void RegisterPrefab(GameObject prefab, int newAssetId) { var component = prefab.GetComponent(); if (component) @@ -129,10 +129,10 @@ namespace QuantumUNET } } - internal static bool GetPrefab(NetworkHash128 assetId, out GameObject prefab) + internal static bool GetPrefab(int assetId, out GameObject prefab) { bool result; - if (!assetId.IsValid()) + if (assetId == 0) { prefab = null; result = false; @@ -158,13 +158,13 @@ namespace QuantumUNET unspawnHandlers.Clear(); } - public static void UnregisterSpawnHandler(NetworkHash128 assetId) + public static void UnregisterSpawnHandler(int assetId) { spawnHandlers.Remove(assetId); unspawnHandlers.Remove(assetId); } - internal static void RegisterSpawnHandler(NetworkHash128 assetId, QSpawnDelegate spawnHandler, UnSpawnDelegate unspawnHandler) + internal static void RegisterSpawnHandler(int assetId, QSpawnDelegate spawnHandler, UnSpawnDelegate unspawnHandler) { if (spawnHandler == null || unspawnHandler == null) { @@ -202,7 +202,7 @@ namespace QuantumUNET { Debug.LogError($"RegisterPrefab custom spawn function null for {component.AssetId}"); } - else if (!component.AssetId.IsValid()) + else if (component.AssetId == 0) { Debug.LogError($"RegisterPrefab game object {prefab.name} has no prefab. Use RegisterSpawnHandler() instead?"); } @@ -213,7 +213,7 @@ namespace QuantumUNET } } - internal static bool GetSpawnHandler(NetworkHash128 assetId, out QSpawnDelegate handler) + internal static bool GetSpawnHandler(int assetId, out QSpawnDelegate handler) { bool result; if (spawnHandlers.ContainsKey(assetId)) @@ -230,7 +230,7 @@ namespace QuantumUNET return result; } - internal static bool InvokeUnSpawnHandler(NetworkHash128 assetId, GameObject obj) + internal static bool InvokeUnSpawnHandler(int assetId, GameObject obj) { bool result; if (unspawnHandlers.ContainsKey(assetId) && unspawnHandlers[assetId] != null) diff --git a/QuantumUNET/QNetworkServer.cs b/QuantumUNET/QNetworkServer.cs index df98093f..b40e456b 100644 --- a/QuantumUNET/QNetworkServer.cs +++ b/QuantumUNET/QNetworkServer.cs @@ -1018,7 +1018,7 @@ namespace QuantumUNET if (conn != null) { - conn.Send(3, objectSpawnMessage); + conn.Send(QMsgType.ObjectSpawn, objectSpawnMessage); } else { @@ -1209,14 +1209,14 @@ namespace QuantumUNET return result; } - public static bool SpawnWithClientAuthority(GameObject obj, NetworkHash128 assetId, QNetworkConnection conn) + public static bool SpawnWithClientAuthority(GameObject obj, int assetId, QNetworkConnection conn) { Spawn(obj, assetId); var component = obj.GetComponent(); return !(component == null) && component.IsServer && component.AssignClientAuthority(conn); } - public static void Spawn(GameObject obj, NetworkHash128 assetId) + public static void Spawn(GameObject obj, int assetId) { if (VerifyCanSpawn(obj)) { From 1118082ecb5b751babfd4d5ad1de2a39c7d9ca59 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Wed, 13 Oct 2021 18:09:51 +0100 Subject: [PATCH 108/118] fix assetid bug?? --- QSB/QSBNetworkManager.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/QSB/QSBNetworkManager.cs b/QSB/QSBNetworkManager.cs index 9f3fdc71..62d23dc4 100644 --- a/QSB/QSBNetworkManager.cs +++ b/QSB/QSBNetworkManager.cs @@ -55,7 +55,7 @@ namespace QSB _assetBundle = QSBCore.NetworkAssetBundle; playerPrefab = _assetBundle.LoadAsset("assets/NETWORK_Player_Body.prefab"); - SetupNetworkId(playerPrefab); + SetupNetworkId(playerPrefab, 1); SetupNetworkTransform(playerPrefab); playerPrefab.AddComponent(); playerPrefab.AddComponent(); @@ -65,19 +65,19 @@ namespace QSB playerPrefab.AddComponent(); ShipPrefab = _assetBundle.LoadAsset("assets/networkship.prefab"); - SetupNetworkId(ShipPrefab); + SetupNetworkId(ShipPrefab, 2); SetupNetworkTransform(ShipPrefab); ShipPrefab.AddComponent(); spawnPrefabs.Add(ShipPrefab); _probePrefab = _assetBundle.LoadAsset("assets/networkprobe.prefab"); - SetupNetworkId(_probePrefab); + SetupNetworkId(_probePrefab, 3); SetupNetworkTransform(_probePrefab); _probePrefab.AddComponent(); spawnPrefabs.Add(_probePrefab); OrbPrefab = _assetBundle.LoadAsset("assets/networkorb.prefab"); - SetupNetworkId(OrbPrefab); + SetupNetworkId(OrbPrefab, 4); SetupNetworkTransform(OrbPrefab); OrbPrefab.AddComponent(); spawnPrefabs.Add(OrbPrefab); @@ -94,12 +94,13 @@ namespace QSB return profileName; } - private void SetupNetworkId(GameObject go) + private void SetupNetworkId(GameObject go, int assetId) { var ident = go.AddComponent(); ident.LocalPlayerAuthority = true; - ident.SetValue("m_AssetId", go.GetComponent().assetId); - ident.SetValue("m_SceneId", go.GetComponent().sceneId); + var networkIdentity = go.GetComponent(); + ident.SetValue("m_AssetId", assetId); + ident.SetValue("m_SceneId", networkIdentity.GetComponent().sceneId); } private void SetupNetworkTransform(GameObject go) From d4505b0fb306ffb165f16c65547ad3b823379291 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Thu, 14 Oct 2021 11:46:14 +0100 Subject: [PATCH 109/118] stop-gap remove meditation --- QSB/TimeSync/Patches/TimePatches.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/QSB/TimeSync/Patches/TimePatches.cs b/QSB/TimeSync/Patches/TimePatches.cs index 9692d9e1..a0d5f48a 100644 --- a/QSB/TimeSync/Patches/TimePatches.cs +++ b/QSB/TimeSync/Patches/TimePatches.cs @@ -10,6 +10,7 @@ namespace QSB.TimeSync.Patches { Prefix(nameof(PlayerCameraEffectController_OnStartOfTimeLoop)); Empty("OWTime_Pause"); + Empty("SubmitActionSkipToNextLoop_AdvanceToNextLoop"); // TODO : remove this, remove meditation button instead } public static bool PlayerCameraEffectController_OnStartOfTimeLoop() From 565f0b7f8eae1a25ef8ce9447f93d4210e965800 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Fri, 15 Oct 2021 21:06:51 +0100 Subject: [PATCH 110/118] update patches, use publicized assembly --- .../NPC/Patches/CharacterAnimationPatches.cs | 43 +++--- .../Player/Patches/PlayerAnimationPatches.cs | 37 ++--- QSB/CampfireSync/Patches/CampfirePatches.cs | 12 +- .../Patches/ConversationPatches.cs | 20 +-- QSB/DeathSync/Patches/DeathPatches.cs | 42 ++--- QSB/DeathSync/Patches/MapPatches.cs | 10 +- QSB/DeathSync/Patches/RespawnPatches.cs | 18 ++- QSB/ElevatorSync/Patches/ElevatorPatches.cs | 9 +- QSB/FrequencySync/Patches/FrequencyPatches.cs | 18 +-- QSB/GeyserSync/Patches/GeyserPatches.cs | 9 +- QSB/Inputs/Patches/InputPatches.cs | 9 +- QSB/ItemSync/Patches/ItemPatches.cs | 23 +-- QSB/LogSync/Patches/LogPatches.cs | 8 +- QSB/OrbSync/Patches/OrbPatches.cs | 14 +- QSB/Patches/QSBPatch.cs | 145 ++---------------- QSB/Patches/QSBPatchManager.cs | 25 ++- QSB/Player/Patches/PlayerPatches.cs | 17 +- QSB/Player/PlayerHUDMarker.cs | 2 +- .../CustomNomaiRemoteCameraStreaming.cs | 6 +- QSB/PoolSync/Patches/PoolPatches.cs | 47 ++++-- QSB/QSB.csproj | 43 +++--- QSB/QSBCore.cs | 2 +- .../Patches/ClientQuantumPatches.cs | 14 +- QSB/QuantumSync/Patches/QuantumPatches.cs | 44 ++++-- .../Patches/QuantumVisibilityPatches.cs | 26 ++-- .../Patches/ServerQuantumPatches.cs | 9 +- QSB/RoastingSync/Patches/RoastingPatches.cs | 30 ++-- QSB/SectorSync/FakeSector.cs | 2 +- QSB/ShipSync/Patches/ShipPatches.cs | 101 ++++++++---- QSB/StatueSync/Patches/StatuePatches.cs | 9 +- QSB/TimeSync/Patches/TimePatches.cs | 23 ++- .../Patches/LauncherPatches.cs | 17 +- QSB/TranslationSync/Patches/SpiralPatches.cs | 17 +- QSB/Utility/Extensions.cs | 21 --- QSBTests/PatchTests.cs | 48 +++--- 35 files changed, 456 insertions(+), 464 deletions(-) diff --git a/QSB/Animation/NPC/Patches/CharacterAnimationPatches.cs b/QSB/Animation/NPC/Patches/CharacterAnimationPatches.cs index 2d797fe0..fffa8883 100644 --- a/QSB/Animation/NPC/Patches/CharacterAnimationPatches.cs +++ b/QSB/Animation/NPC/Patches/CharacterAnimationPatches.cs @@ -1,4 +1,5 @@ -using OWML.Common; +using HarmonyLib; +using OWML.Common; using QSB.Animation.NPC.WorldObjects; using QSB.ConversationSync; using QSB.Events; @@ -12,22 +13,14 @@ using UnityEngine; namespace QSB.Animation.NPC.Patches { + [HarmonyPatch] public class CharacterAnimationPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; - public override void DoPatches() - { - Prefix(nameof(CharacterAnimController_OnAnimatorIK)); - Prefix(nameof(CharacterAnimController_OnZoneEntry)); - Prefix(nameof(CharacterAnimController_OnZoneExit)); - Prefix(nameof(FacePlayerWhenTalking_OnStartConversation)); - Prefix(nameof(CharacterDialogueTree_StartConversation)); - Prefix(nameof(CharacterDialogueTree_EndConversation)); - Prefix(nameof(KidRockController_Update)); - } - - public static bool CharacterAnimController_OnAnimatorIK( + [HarmonyPrefix] + [HarmonyPatch(typeof(CharacterAnimController), nameof(CharacterAnimController.OnAnimatorIK))] + public static bool AnimatorIKReplacement( CharacterAnimController __instance, float ___headTrackingWeight, bool ___lookOnlyWhenTalking, @@ -103,21 +96,27 @@ namespace QSB.Animation.NPC.Patches } - public static bool CharacterAnimController_OnZoneExit(CharacterAnimController __instance) + [HarmonyPrefix] + [HarmonyPatch(typeof(CharacterAnimController), nameof(CharacterAnimController.OnZoneExit))] + public static bool HeadZoneExit(CharacterAnimController __instance) { var qsbObj = QSBWorldSync.GetWorldFromUnity(__instance); QSBEventManager.FireEvent(EventNames.QSBExitHeadZone, qsbObj.ObjectId); return false; } - public static bool CharacterAnimController_OnZoneEntry(CharacterAnimController __instance) + [HarmonyPrefix] + [HarmonyPatch(typeof(CharacterAnimController), nameof(CharacterAnimController.OnZoneEntry))] + public static bool HeadZoneEntry(CharacterAnimController __instance) { var qsbObj = QSBWorldSync.GetWorldFromUnity(__instance); QSBEventManager.FireEvent(EventNames.QSBEnterHeadZone, qsbObj.ObjectId); return false; } - public static bool FacePlayerWhenTalking_OnStartConversation( + [HarmonyPrefix] + [HarmonyPatch(typeof(FacePlayerWhenTalking), nameof(FacePlayerWhenTalking.OnStartConversation))] + public static bool OnStartConversation( FacePlayerWhenTalking __instance, CharacterDialogueTree ____dialogueTree) { @@ -140,7 +139,9 @@ namespace QSB.Animation.NPC.Patches return false; } - public static bool CharacterDialogueTree_StartConversation(CharacterDialogueTree __instance) + [HarmonyPrefix] + [HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.StartConversation))] + public static bool StartConversation(CharacterDialogueTree __instance) { var allNpcAnimControllers = QSBWorldSync.GetWorldObjects(); var ownerOfThis = allNpcAnimControllers.FirstOrDefault(x => x.GetDialogueTree() == __instance); @@ -154,7 +155,9 @@ namespace QSB.Animation.NPC.Patches return true; } - public static bool CharacterDialogueTree_EndConversation(CharacterDialogueTree __instance) + [HarmonyPrefix] + [HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.EndConversation))] + public static bool EndConversation(CharacterDialogueTree __instance) { var allNpcAnimControllers = QSBWorldSync.GetWorldObjects(); var ownerOfThis = allNpcAnimControllers.FirstOrDefault(x => x.GetDialogueTree() == __instance); @@ -168,7 +171,9 @@ namespace QSB.Animation.NPC.Patches return true; } - public static bool KidRockController_Update( + [HarmonyPrefix] + [HarmonyPatch(typeof(KidRockController), nameof(KidRockController.Update))] + public static bool UpdateReplacement( KidRockController __instance, bool ____throwingRock, CharacterDialogueTree ____dialogueTree, diff --git a/QSB/Animation/Player/Patches/PlayerAnimationPatches.cs b/QSB/Animation/Player/Patches/PlayerAnimationPatches.cs index 7db275a1..b884c711 100644 --- a/QSB/Animation/Player/Patches/PlayerAnimationPatches.cs +++ b/QSB/Animation/Player/Patches/PlayerAnimationPatches.cs @@ -1,4 +1,5 @@ -using QSB.Events; +using HarmonyLib; +using QSB.Events; using QSB.Patches; using QSB.Player; using QSB.Utility; @@ -6,27 +7,27 @@ using UnityEngine; namespace QSB.Animation.Patches { + [HarmonyPatch] internal class PlayerAnimationPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; - public override void DoPatches() - => Prefix(nameof(PlayerAnimController_LateUpdate)); - - public static bool PlayerAnimController_LateUpdate( - PlayerAnimController __instance, - PlayerCharacterController ____playerController, - ThrusterModel ____playerJetpack, - ref float ____ungroundedTime, - Animator ____animator, - ref bool ____justBecameGrounded, - ref bool ____justTookFallDamage, - ref bool ____leftFootGrounded, - ref bool ____rightFootGrounded, - ref bool ____rightArmHidden, - GameObject[] ____rightArmObjects, - int ____defaultLayer, - int ____probeOnlyLayer) + [HarmonyPrefix] + [HarmonyPatch(typeof(PlayerAnimController), nameof(PlayerAnimController.LateUpdate))] + public static bool LateUpdateReplacement( + PlayerAnimController __instance, + PlayerCharacterController ____playerController, + ThrusterModel ____playerJetpack, + ref float ____ungroundedTime, + Animator ____animator, + ref bool ____justBecameGrounded, + ref bool ____justTookFallDamage, + ref bool ____leftFootGrounded, + ref bool ____rightFootGrounded, + ref bool ____rightArmHidden, + GameObject[] ____rightArmObjects, + int ____defaultLayer, + int ____probeOnlyLayer) { var isGrounded = ____playerController.IsGrounded(); var isAttached = PlayerState.IsAttached(); diff --git a/QSB/CampfireSync/Patches/CampfirePatches.cs b/QSB/CampfireSync/Patches/CampfirePatches.cs index 7914fb5b..5dc7f0e0 100644 --- a/QSB/CampfireSync/Patches/CampfirePatches.cs +++ b/QSB/CampfireSync/Patches/CampfirePatches.cs @@ -1,20 +1,22 @@ -using QSB.CampfireSync.WorldObjects; +using HarmonyLib; +using QSB.CampfireSync.WorldObjects; using QSB.Events; using QSB.Patches; using QSB.WorldSync; namespace QSB.CampfireSync.Patches { + [HarmonyPatch] internal class CampfirePatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; - public override void DoPatches() => Prefix(nameof(Campfire_OnPressInteract)); - - public static bool Campfire_OnPressInteract(Campfire __instance, Campfire.State ____state) + [HarmonyPrefix] + [HarmonyPatch(typeof(Campfire), nameof(Campfire.OnPressInteract))] + public static bool LightCampfireEvent(Campfire __instance) { var qsbCampfire = QSBWorldSync.GetWorldFromUnity(__instance); - if (____state == Campfire.State.LIT) + if (__instance._state == Campfire.State.LIT) { qsbCampfire.StartRoasting(); } diff --git a/QSB/ConversationSync/Patches/ConversationPatches.cs b/QSB/ConversationSync/Patches/ConversationPatches.cs index ba948aa2..c674cc15 100644 --- a/QSB/ConversationSync/Patches/ConversationPatches.cs +++ b/QSB/ConversationSync/Patches/ConversationPatches.cs @@ -1,4 +1,5 @@ -using OWML.Common; +using HarmonyLib; +using OWML.Common; using QSB.Patches; using QSB.Player; using QSB.Utility; @@ -7,18 +8,13 @@ using System.Collections.Generic; namespace QSB.ConversationSync.Patches { + [HarmonyPatch] public class ConversationPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; - public override void DoPatches() - { - Postfix(nameof(DialogueNode_GetNextPage)); - Prefix(nameof(CharacterDialogueTree_InputDialogueOption)); - Prefix(nameof(CharacterDialogueTree_StartConversation)); - Prefix(nameof(CharacterDialogueTree_EndConversation)); - } - + [HarmonyPrefix] + [HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.StartConversation))] public static void CharacterDialogueTree_StartConversation(CharacterDialogueTree __instance) { var index = QSBWorldSync.OldDialogueTrees.FindIndex(x => x == __instance); @@ -31,6 +27,8 @@ namespace QSB.ConversationSync.Patches ConversationManager.Instance.SendConvState(index, true); } + [HarmonyPrefix] + [HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.EndConversation))] public static bool CharacterDialogueTree_EndConversation(CharacterDialogueTree __instance) { if (!__instance.enabled) @@ -51,6 +49,8 @@ namespace QSB.ConversationSync.Patches return true; } + [HarmonyPrefix] + [HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.InputDialogueOption))] public static bool CharacterDialogueTree_InputDialogueOption(int optionIndex, DialogueBoxVer2 ____currentDialogueBox) { if (optionIndex < 0) @@ -65,6 +65,8 @@ namespace QSB.ConversationSync.Patches return true; } + [HarmonyPostfix] + [HarmonyPatch(typeof(DialogueNode), nameof(DialogueNode.GetNextPage))] public static void DialogueNode_GetNextPage(string ____name, List ____listPagesToDisplay, int ____currentPage) { var key = ____name + ____listPagesToDisplay[____currentPage]; diff --git a/QSB/DeathSync/Patches/DeathPatches.cs b/QSB/DeathSync/Patches/DeathPatches.cs index 50d596d9..d012b634 100644 --- a/QSB/DeathSync/Patches/DeathPatches.cs +++ b/QSB/DeathSync/Patches/DeathPatches.cs @@ -11,35 +11,37 @@ using UnityEngine; namespace QSB.DeathSync.Patches { + [HarmonyPatch] public class DeathPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; - public override void DoPatches() - { - Prefix(nameof(DeathManager_KillPlayer_Prefix)); - Postfix(nameof(DeathManager_KillPlayer_Postfix)); - Prefix(nameof(ShipDetachableLeg_Detach)); - Prefix(nameof(ShipDetachableModule_Detach)); - Empty("ShipEjectionSystem_OnPressInteract"); - Postfix(nameof(ShipDamageController_Awake)); - Prefix(nameof(DestructionVolume_VanishShip)); - Prefix(nameof(HighSpeedImpactSensor_FixedUpdate)); - Prefix(nameof(PlayerResources_OnImpact)); - } + // TODO : Remove with future functionality. + [HarmonyPrefix] + [HarmonyPatch(typeof(ShipEjectionSystem), nameof(ShipEjectionSystem.OnPressInteract))] + public static bool DisableEjection() + => false; + // TODO : Remove with future functionality. + [HarmonyPrefix] + [HarmonyPatch(typeof(ShipDetachableLeg), nameof(ShipDetachableLeg.Detach))] public static bool ShipDetachableLeg_Detach(ref OWRigidbody __result) { __result = null; return false; } + // TODO : Remove with future functionality. + [HarmonyPrefix] + [HarmonyPatch(typeof(ShipDetachableModule), nameof(ShipDetachableModule.Detach))] public static bool ShipDetachableModule_Detach(ref OWRigidbody __result) { __result = null; return false; } + [HarmonyPrefix] + [HarmonyPatch(typeof(PlayerResources), nameof(PlayerResources.OnImpact))] public static bool PlayerResources_OnImpact(ImpactData impact, PlayerResources __instance, float ____currentHealth) { if (PlayerState.IsInsideShip()) @@ -65,6 +67,8 @@ namespace QSB.DeathSync.Patches return false; } + [HarmonyPrefix] + [HarmonyPatch(typeof(HighSpeedImpactSensor), nameof(HighSpeedImpactSensor.FixedUpdate))] public static bool HighSpeedImpactSensor_FixedUpdate( HighSpeedImpactSensor __instance, bool ____isPlayer, @@ -185,6 +189,8 @@ namespace QSB.DeathSync.Patches return false; } + [HarmonyPrefix] + [HarmonyPatch(typeof(DeathManager), nameof(DeathManager.KillPlayer))] public static bool DeathManager_KillPlayer_Prefix(DeathType deathType) { if (RespawnOnDeath.Instance == null) @@ -203,20 +209,20 @@ namespace QSB.DeathSync.Patches return false; } + [HarmonyPostfix] + [HarmonyPatch(typeof(DeathManager), nameof(DeathManager.KillPlayer))] public static void DeathManager_KillPlayer_Postfix(DeathType deathType) { QSBEventManager.FireEvent(EventNames.QSBPlayerDeath, deathType); } + [HarmonyPostfix] + [HarmonyPatch(typeof(ShipDamageController), nameof(ShipDamageController.Awake))] public static void ShipDamageController_Awake(ref bool ____exploded) => ____exploded = true; - public static IEnumerable ReturnNull(IEnumerable instructions) => new List - { - new CodeInstruction(OpCodes.Ldnull), - new CodeInstruction(OpCodes.Ret) - }; - + [HarmonyPrefix] + [HarmonyPatch(typeof(DestructionVolume), nameof(DestructionVolume.VanishShip))] public static bool DestructionVolume_VanishShip(DeathType ____deathType) { if (RespawnOnDeath.Instance == null) diff --git a/QSB/DeathSync/Patches/MapPatches.cs b/QSB/DeathSync/Patches/MapPatches.cs index 7bc11ca2..cf233fd5 100644 --- a/QSB/DeathSync/Patches/MapPatches.cs +++ b/QSB/DeathSync/Patches/MapPatches.cs @@ -7,11 +7,11 @@ namespace QSB.DeathSync.Patches { public override QSBPatchTypes Type => QSBPatchTypes.RespawnTime; - public override void DoPatches() - { - Prefix(nameof(MapController_LateUpdate)); - Prefix(nameof(MapController_EnterMapView)); - } + //public override void DoPatches() + //{ + // Prefix(nameof(MapController.LateUpdate), nameof(MapController_LateUpdate)); + // Prefix(nameof(MapController.EnterMapView), nameof(MapController_EnterMapView)); + //} public static bool MapController_EnterMapView( MapController __instance, diff --git a/QSB/DeathSync/Patches/RespawnPatches.cs b/QSB/DeathSync/Patches/RespawnPatches.cs index 9365e6c8..34d96c63 100644 --- a/QSB/DeathSync/Patches/RespawnPatches.cs +++ b/QSB/DeathSync/Patches/RespawnPatches.cs @@ -1,18 +1,22 @@ -using OWML.Utils; +using HarmonyLib; +using OWML.Utils; using QSB.Patches; namespace QSB.DeathSync.Patches { + [HarmonyPatch] internal class RespawnPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; - public override void DoPatches() - { - Prefix(nameof(PlayerRecoveryPoint_OnGainFocus)); - Prefix(nameof(PlayerRecoveryPoint_OnPressInteract)); - } + //public override void DoPatches() + //{ + // Prefix(nameof(PlayerRecoveryPoint.OnGainFocus), nameof(PlayerRecoveryPoint_OnGainFocus)); + // Prefix(nameof(PlayerRecoveryPoint.OnPressInteract), nameof(PlayerRecoveryPoint_OnPressInteract)); + //} + [HarmonyPrefix] + [HarmonyPatch(typeof(PlayerRecoveryPoint), nameof(PlayerRecoveryPoint.OnGainFocus))] public static bool PlayerRecoveryPoint_OnGainFocus( PlayerResources ____playerResources, bool ____refuelsPlayer, @@ -100,6 +104,8 @@ namespace QSB.DeathSync.Patches return false; } + [HarmonyPrefix] + [HarmonyPatch(typeof(PlayerRecoveryPoint), nameof(PlayerRecoveryPoint.OnPressInteract))] public static bool PlayerRecoveryPoint_OnPressInteract( PlayerRecoveryPoint __instance, PlayerResources ____playerResources, diff --git a/QSB/ElevatorSync/Patches/ElevatorPatches.cs b/QSB/ElevatorSync/Patches/ElevatorPatches.cs index f1665b1a..2e12063a 100644 --- a/QSB/ElevatorSync/Patches/ElevatorPatches.cs +++ b/QSB/ElevatorSync/Patches/ElevatorPatches.cs @@ -1,4 +1,5 @@ -using OWML.Utils; +using HarmonyLib; +using OWML.Utils; using QSB.ElevatorSync.WorldObjects; using QSB.Events; using QSB.Patches; @@ -6,18 +7,18 @@ using QSB.WorldSync; namespace QSB.ElevatorSync.Patches { + [HarmonyPatch] public class ElevatorPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; + [HarmonyPostfix] + [HarmonyPatch(typeof(Elevator), nameof(Elevator.StartLift))] public static void Elevator_StartLift(Elevator __instance) { var isGoingUp = __instance.GetValue("_goingToTheEnd"); var id = QSBWorldSync.GetIdFromUnity(__instance); QSBEventManager.FireEvent(EventNames.QSBStartLift, id, isGoingUp); } - - public override void DoPatches() - => Postfix(nameof(Elevator_StartLift)); } } \ No newline at end of file diff --git a/QSB/FrequencySync/Patches/FrequencyPatches.cs b/QSB/FrequencySync/Patches/FrequencyPatches.cs index 3ad8eb48..a7ad6a5a 100644 --- a/QSB/FrequencySync/Patches/FrequencyPatches.cs +++ b/QSB/FrequencySync/Patches/FrequencyPatches.cs @@ -1,22 +1,22 @@ -using QSB.Events; +using HarmonyLib; +using QSB.Events; using QSB.Patches; namespace QSB.FrequencySync.Patches { + [HarmonyPatch] public class FrequencyPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; - public override void DoPatches() - { - Postfix(nameof(AudioSignal_IdentifyFrequency)); - Postfix(nameof(AudioSignal_IdentifySignal)); - } - - public static void AudioSignal_IdentifyFrequency(SignalFrequency ____frequency) + [HarmonyPostfix] + [HarmonyPatch(typeof(AudioSignal), nameof(AudioSignal.IdentifyFrequency))] + static void IdentifyFrequencyEvent(SignalFrequency ____frequency) => QSBEventManager.FireEvent(EventNames.QSBIdentifyFrequency, ____frequency); - public static void AudioSignal_IdentifySignal(SignalName ____name) + [HarmonyPostfix] + [HarmonyPatch(typeof(AudioSignal), nameof(AudioSignal.IdentifySignal))] + static void IdentifySignalEvent(SignalName ____name) => QSBEventManager.FireEvent(EventNames.QSBIdentifySignal, ____name); } } diff --git a/QSB/GeyserSync/Patches/GeyserPatches.cs b/QSB/GeyserSync/Patches/GeyserPatches.cs index 072eb8c5..b393b6fa 100644 --- a/QSB/GeyserSync/Patches/GeyserPatches.cs +++ b/QSB/GeyserSync/Patches/GeyserPatches.cs @@ -1,11 +1,16 @@ -using QSB.Patches; +using HarmonyLib; +using QSB.Patches; namespace QSB.GeyserSync.Patches { + [HarmonyPatch] internal class GeyserPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnNonServerClientConnect; - public override void DoPatches() => Empty("GeyserController_Update"); + [HarmonyPrefix] + [HarmonyPatch(typeof(GeyserController), nameof(GeyserController.Update))] + public static bool Empty() + => false; } } diff --git a/QSB/Inputs/Patches/InputPatches.cs b/QSB/Inputs/Patches/InputPatches.cs index ff9e0ce1..0b36b7ad 100644 --- a/QSB/Inputs/Patches/InputPatches.cs +++ b/QSB/Inputs/Patches/InputPatches.cs @@ -1,14 +1,15 @@ -using QSB.Patches; +using HarmonyLib; +using QSB.Patches; namespace QSB.Inputs.Patches { + [HarmonyPatch] internal class InputPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; - public override void DoPatches() - => Prefix(nameof(OWInput_Update)); - + [HarmonyPrefix] + [HarmonyPatch(typeof(OWInput), nameof(OWInput.Update))] public static bool OWInput_Update() => QSBInputManager.Instance.InputsEnabled; } diff --git a/QSB/ItemSync/Patches/ItemPatches.cs b/QSB/ItemSync/Patches/ItemPatches.cs index b47a0826..03667c05 100644 --- a/QSB/ItemSync/Patches/ItemPatches.cs +++ b/QSB/ItemSync/Patches/ItemPatches.cs @@ -1,4 +1,5 @@ -using OWML.Common; +using HarmonyLib; +using OWML.Common; using QSB.Events; using QSB.Patches; using QSB.Utility; @@ -7,19 +8,13 @@ using UnityEngine; namespace QSB.ItemSync.Patches { + [HarmonyPatch] internal class ItemPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; - public override void DoPatches() - { - Prefix(nameof(ItemTool_MoveItemToCarrySocket)); - Prefix(nameof(ItemTool_SocketItem)); - Prefix(nameof(ItemTool_StartUnsocketItem)); - Prefix(nameof(ItemTool_CompleteUnsocketItem)); - Prefix(nameof(ItemTool_DropItem)); - } - + [HarmonyPrefix] + [HarmonyPatch(typeof(ItemTool), nameof(ItemTool.MoveItemToCarrySocket))] public static bool ItemTool_MoveItemToCarrySocket(OWItem item) { var itemId = QSBWorldSync.GetIdFromTypeSubset(ItemManager.GetObject(item)); @@ -27,6 +22,8 @@ namespace QSB.ItemSync.Patches return true; } + [HarmonyPrefix] + [HarmonyPatch(typeof(ItemTool), nameof(ItemTool.SocketItem))] public static bool ItemTool_SocketItem(OWItem ____heldItem, OWItemSocket socket) { var socketId = QSBWorldSync.GetIdFromTypeSubset(ItemManager.GetObject(socket)); @@ -35,6 +32,8 @@ namespace QSB.ItemSync.Patches return true; } + [HarmonyPrefix] + [HarmonyPatch(typeof(ItemTool), nameof(ItemTool.StartUnsocketItem))] public static bool ItemTool_StartUnsocketItem(OWItemSocket socket) { var socketId = QSBWorldSync.GetIdFromTypeSubset(ItemManager.GetObject(socket)); @@ -42,6 +41,8 @@ namespace QSB.ItemSync.Patches return true; } + [HarmonyPrefix] + [HarmonyPatch(typeof(ItemTool), nameof(ItemTool.CompleteUnsocketItem))] public static bool ItemTool_CompleteUnsocketItem(OWItem ____heldItem) { var itemId = QSBWorldSync.GetIdFromTypeSubset(ItemManager.GetObject(____heldItem)); @@ -49,6 +50,8 @@ namespace QSB.ItemSync.Patches return true; } + [HarmonyPrefix] + [HarmonyPatch(typeof(ItemTool), nameof(ItemTool.DropItem))] public static bool ItemTool_DropItem(RaycastHit hit, OWRigidbody targetRigidbody, IItemDropTarget customDropTarget, ref OWItem ____heldItem) { Locator.GetPlayerAudioController().PlayDropItem(____heldItem.GetItemType()); diff --git a/QSB/LogSync/Patches/LogPatches.cs b/QSB/LogSync/Patches/LogPatches.cs index 361b82b0..c619d2fd 100644 --- a/QSB/LogSync/Patches/LogPatches.cs +++ b/QSB/LogSync/Patches/LogPatches.cs @@ -1,12 +1,16 @@ -using QSB.Events; +using HarmonyLib; +using QSB.Events; using QSB.Patches; namespace QSB.LogSync.Patches { + [HarmonyPatch] public class LogPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; + [HarmonyPostfix] + [HarmonyPatch(typeof(ShipLogManager), nameof(ShipLogManager.RevealFact))] public static void ShipLogManager_RevealFact(string id, bool saveGame, bool showNotification, bool __result) { if (!__result) @@ -16,7 +20,5 @@ namespace QSB.LogSync.Patches QSBEventManager.FireEvent(EventNames.QSBRevealFact, id, saveGame, showNotification); } - - public override void DoPatches() => Postfix(nameof(ShipLogManager_RevealFact)); } } \ No newline at end of file diff --git a/QSB/OrbSync/Patches/OrbPatches.cs b/QSB/OrbSync/Patches/OrbPatches.cs index 03832dca..360dadcf 100644 --- a/QSB/OrbSync/Patches/OrbPatches.cs +++ b/QSB/OrbSync/Patches/OrbPatches.cs @@ -1,4 +1,5 @@ -using QSB.Events; +using HarmonyLib; +using QSB.Events; using QSB.Patches; using QSB.Utility; using QSB.WorldSync; @@ -6,10 +7,13 @@ using UnityEngine; namespace QSB.OrbSync.Patches { + [HarmonyPatch] public class OrbPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; + [HarmonyPostfix] + [HarmonyPatch(typeof(NomaiInterfaceOrb), nameof(NomaiInterfaceOrb.StartDragFromPosition))] public static void NomaiInterfaceOrb_StartDragFromPosition(bool __result, NomaiInterfaceOrb __instance) { if (__result) @@ -18,6 +22,8 @@ namespace QSB.OrbSync.Patches } } + [HarmonyPrefix] + [HarmonyPatch(typeof(NomaiInterfaceSlot), nameof(NomaiInterfaceSlot.CheckOrbCollision))] public static bool NomaiInterfaceSlot_CheckOrbCollision(ref bool __result, NomaiInterfaceSlot __instance, NomaiInterfaceOrb orb, bool ____ignoreDraggedOrbs, float ____radius, float ____exitRadius, ref NomaiInterfaceOrb ____occupyingOrb) { @@ -60,11 +66,5 @@ namespace QSB.OrbSync.Patches __result = true; return false; } - - public override void DoPatches() - { - Postfix(nameof(NomaiInterfaceOrb_StartDragFromPosition)); - Prefix(nameof(NomaiInterfaceSlot_CheckOrbCollision)); - } } } \ No newline at end of file diff --git a/QSB/Patches/QSBPatch.cs b/QSB/Patches/QSBPatch.cs index 20844c25..4e3a4098 100644 --- a/QSB/Patches/QSBPatch.cs +++ b/QSB/Patches/QSBPatch.cs @@ -1,147 +1,24 @@ -using OWML.Common; -using QSB.Utility; -using System; -using System.Collections.Generic; +using QSB.Utility; using System.Linq; -using System.Reflection; namespace QSB.Patches { public abstract class QSBPatch { public abstract QSBPatchTypes Type { get; } - public abstract void DoPatches(); + + public virtual void DoPatches() + { + var oldMethods = QSBPatchManager.HarmonyInstance.GetPatchedMethods(); + QSBPatchManager.HarmonyInstance.PatchAll(GetType()); + foreach (var method in QSBPatchManager.HarmonyInstance.GetPatchedMethods().Except(oldMethods)) + { + DebugLog.DebugWrite($"- Patching {method.DeclaringType}.{method.Name}"); + } + } public void DoUnpatches() { - foreach (var item in _patchedMethods) - { - //DebugLog.DebugWrite($"[Unpatch] {item.DeclaringType}.{item.Name}", MessageType.Info); - Unpatch(item); - } - - _patchedMethods.Clear(); - } - - private List _patchedMethods = new List(); - - public void Empty(string patchName) - { - //DebugLog.DebugWrite($"[Empty] {patchName}", MessageType.Success); - var method = GetMethodInfo(patchName); - QSBCore.Helper.HarmonyHelper.EmptyMethod(method); - _patchedMethods.Add(method); - } - - public void Prefix(string patchName, params Type[] args) - => DoPrefixPostfix(true, patchName, args); - - public void Postfix(string patchName, params Type[] args) - => DoPrefixPostfix(false, patchName, args); - - private void DoPrefixPostfix(bool isPrefix, string patchName, params Type[] args) - { - var method = GetMethodInfo(patchName, args); - - if (method != null) - { - if (isPrefix) - { - QSBCore.Helper.HarmonyHelper.AddPrefix(method, GetType(), patchName); - } - else - { - QSBCore.Helper.HarmonyHelper.AddPostfix(method, GetType(), patchName); - } - - _patchedMethods.Add(method); - } - - //DebugLog.DebugWrite($"{(isPrefix ? "[Prefix]" : "[Postfix]")} {patchName}", method == null ? MessageType.Error : MessageType.Success); - } - - private MethodInfo GetMethodInfo(string patchName, params Type[] args) - { - var splitName = patchName.Split('_'); - var typeName = splitName[0]; - var methodName = splitName[1]; - - var type = GetFirstTypeByName(typeName); - if (type == null) - { - DebugLog.ToConsole($"Error - Couldn't find type for patch name {patchName}!", MessageType.Error); - return null; - } - - var allMethodsOfName = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy).Where(x => x.Name == methodName); - - if (allMethodsOfName.Count() == 0) - { - DebugLog.ToConsole($"Error - Could not find method {methodName} in type {typeName}.", MessageType.Error); - return null; - } - - if (allMethodsOfName.Count() == 1) - { - return allMethodsOfName.First(); - } - - DebugLog.DebugWrite($"More than one method found with name {methodName} in type {typeName}"); - - foreach (var method in allMethodsOfName) - { - DebugLog.DebugWrite($"checking {method.Name}"); - var paramList = method.GetParameters().Select(x => x.ParameterType); - if (Enumerable.SequenceEqual(args, paramList)) - { - DebugLog.DebugWrite($"match!"); - return method; - } - } - - DebugLog.DebugWrite($"nothing found"); - - DebugLog.ToConsole($"Error - Could not find method {methodName} in type {typeName} with parameter list of {string.Join(", ", args.Select(x => x.FullName).ToArray())}", MessageType.Error); - foreach (var method in allMethodsOfName) - { - var paramList = method.GetParameters().Select(x => x.ParameterType); - DebugLog.ToConsole($"- Found {method.Name}, but with params {string.Join(", ", paramList.Select(x => x.FullName).ToArray())}", MessageType.Error); - } - - return null; - } - - private Type GetFirstTypeByName(string typeName) - { - var a = typeof(OWRigidbody).Assembly; - var assemblyTypes = a.GetTypes(); - for (var j = 0; j < assemblyTypes.Length; j++) - { - if (assemblyTypes[j].Name == typeName) - { - return assemblyTypes[j]; - } - } - - return null; - } - - private void Unpatch(MethodInfo method) - { - /* - var dictionary = typeof(HarmonySharedState).Invoke>("GetState", new object[0]); - var methodBase = dictionary.Keys.First(m => - m.DeclaringType == method.DeclaringType - && m.Name == method.Name); - - var patchInfo = PatchInfoSerialization.Deserialize(dictionary.GetValueSafe(methodBase)); - patchInfo.RemovePostfix(QSBCore.Helper.Manifest.UniqueName); - patchInfo.RemovePrefix(QSBCore.Helper.Manifest.UniqueName); - patchInfo.RemoveTranspiler(QSBCore.Helper.Manifest.UniqueName); - - PatchFunctions.UpdateWrapper(methodBase, patchInfo, QSBCore.Helper.Manifest.UniqueName); - dictionary[methodBase] = patchInfo.Serialize(); - */ } } } \ No newline at end of file diff --git a/QSB/Patches/QSBPatchManager.cs b/QSB/Patches/QSBPatchManager.cs index 6aa7ad33..8559b627 100644 --- a/QSB/Patches/QSBPatchManager.cs +++ b/QSB/Patches/QSBPatchManager.cs @@ -1,4 +1,6 @@ -using OWML.Common; +using HarmonyLib; +using OWML.Common; +using OWML.Utils; using QSB.Animation.NPC.Patches; using QSB.Animation.Patches; using QSB.CampfireSync.Patches; @@ -34,6 +36,8 @@ namespace QSB.Patches private static List _patchList = new List(); + public static Harmony HarmonyInstance; + public static void Init() { _patchList = new List @@ -66,27 +70,36 @@ namespace QSB.Patches new LauncherPatches() }; + HarmonyInstance = QSBCore.Helper.HarmonyHelper.GetValue("_harmony"); + DebugLog.DebugWrite("Patch Manager ready.", MessageType.Success); } public static void DoPatchType(QSBPatchTypes type) { OnPatchType?.SafeInvoke(type); - //DebugLog.DebugWrite($"Patch block {Enum.GetName(typeof(QSBPatchTypes), type)}", MessageType.Info); + DebugLog.DebugWrite($"Patch block {Enum.GetName(typeof(QSBPatchTypes), type)}", MessageType.Info); foreach (var patch in _patchList.Where(x => x.Type == type)) { - //DebugLog.DebugWrite($" - Patching in {patch.GetType().Name}", MessageType.Info); - patch.DoPatches(); + DebugLog.DebugWrite($" - Patching in {patch.GetType().Name}", MessageType.Info); + try + { + patch.DoPatches(); + } + catch (Exception ex) + { + DebugLog.DebugWrite($"Error while patching {patch.GetType().Name} :\r\n{ex}", MessageType.Error); + } } } public static void DoUnpatchType(QSBPatchTypes type) { OnUnpatchType?.SafeInvoke(type); - //DebugLog.DebugWrite($"Unpatch block {Enum.GetName(typeof(QSBPatchTypes), type)}", MessageType.Info); + DebugLog.DebugWrite($"Unpatch block {Enum.GetName(typeof(QSBPatchTypes), type)}", MessageType.Info); foreach (var patch in _patchList.Where(x => x.Type == type)) { - //DebugLog.DebugWrite($" - Unpatching in {patch.GetType().Name}", MessageType.Info); + DebugLog.DebugWrite($" - Unpatching in {patch.GetType().Name}", MessageType.Info); patch.DoUnpatches(); } } diff --git a/QSB/Player/Patches/PlayerPatches.cs b/QSB/Player/Patches/PlayerPatches.cs index 581aef3f..383ebaba 100644 --- a/QSB/Player/Patches/PlayerPatches.cs +++ b/QSB/Player/Patches/PlayerPatches.cs @@ -1,17 +1,15 @@ -using QSB.Patches; +using HarmonyLib; +using QSB.Patches; namespace QSB.Player.Patches { + [HarmonyPatch] internal class PlayerPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; - public override void DoPatches() - { - Prefix(nameof(PlayerCrushedController_CrushPlayer)); - Prefix(nameof(PauseMenuManager_OnExitToMainMenu)); - } - + [HarmonyPrefix] + [HarmonyPatch(typeof(PlayerCrushedController), nameof(PlayerCrushedController.CrushPlayer))] public static bool PlayerCrushedController_CrushPlayer() { // #CrushIt https://www.twitch.tv/videos/846916781?t=00h03m51s @@ -20,6 +18,9 @@ namespace QSB.Player.Patches return false; } - public static void PauseMenuManager_OnExitToMainMenu() => QSBPlayerManager.LocalPlayer.PlayerStates.IsReady = false; + [HarmonyPrefix] + [HarmonyPatch(typeof(PauseMenuManager), nameof(PauseMenuManager.OnExitToMainMenu))] + public static void PauseMenuManager_OnExitToMainMenu() + => QSBPlayerManager.LocalPlayer.PlayerStates.IsReady = false; } } diff --git a/QSB/Player/PlayerHUDMarker.cs b/QSB/Player/PlayerHUDMarker.cs index 18913139..9fdf8047 100644 --- a/QSB/Player/PlayerHUDMarker.cs +++ b/QSB/Player/PlayerHUDMarker.cs @@ -9,7 +9,7 @@ namespace QSB.Player private bool _needsInitializing; private bool _isReady; - protected override void InitCanvasMarker() + public override void InitCanvasMarker() { _markerRadius = 2f; diff --git a/QSB/PoolSync/CustomNomaiRemoteCameraStreaming.cs b/QSB/PoolSync/CustomNomaiRemoteCameraStreaming.cs index d98f6b80..1178a036 100644 --- a/QSB/PoolSync/CustomNomaiRemoteCameraStreaming.cs +++ b/QSB/PoolSync/CustomNomaiRemoteCameraStreaming.cs @@ -13,7 +13,7 @@ namespace QSB.PoolSync private NomaiRemoteCameraStreaming _oldStreaming; private bool _hasLoadedAssets; - protected override void Awake() + public override void Awake() { base.Awake(); _oldStreaming = GetComponent(); @@ -48,7 +48,7 @@ namespace QSB.PoolSync } } - protected override void OnSectorOccupantAdded(SectorDetector sectorDetector) + public override void OnSectorOccupantAdded(SectorDetector sectorDetector) { if (sectorDetector.GetOccupantType() == DynamicOccupant.Player && StreamingManager.isStreamingEnabled) { @@ -56,7 +56,7 @@ namespace QSB.PoolSync } } - protected override void OnSectorOccupantRemoved(SectorDetector sectorDetector) + public override void OnSectorOccupantRemoved(SectorDetector sectorDetector) { if (sectorDetector.GetOccupantType() == DynamicOccupant.Player) { diff --git a/QSB/PoolSync/Patches/PoolPatches.cs b/QSB/PoolSync/Patches/PoolPatches.cs index 580e9387..41264063 100644 --- a/QSB/PoolSync/Patches/PoolPatches.cs +++ b/QSB/PoolSync/Patches/PoolPatches.cs @@ -1,34 +1,51 @@ -using QSB.Patches; +using HarmonyLib; +using QSB.Patches; namespace QSB.PoolSync.Patches { + [HarmonyPatch] internal class PoolPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; - public override void DoPatches() - { - Prefix(nameof(NomaiRemoteCameraPlatform_Awake)); - Prefix(nameof(NomaiRemoteCameraPlatform_Update)); - Prefix(nameof(NomaiRemoteCameraPlatform_OnSocketableRemoved)); - Prefix(nameof(NomaiRemoteCameraPlatform_OnSocketableDonePlacing)); - Prefix(nameof(NomaiRemoteCameraPlatform_OnPedestalContact)); - Prefix(nameof(NomaiRemoteCameraStreaming_FixedUpdate)); - Prefix(nameof(NomaiRemoteCameraStreaming_OnSectorOccupantAdded)); - Prefix(nameof(NomaiRemoteCameraStreaming_OnSectorOccupantRemoved)); - Prefix(nameof(NomaiRemoteCameraStreaming_OnEntry)); - Prefix(nameof(NomaiRemoteCameraStreaming_OnExit)); - } - + [HarmonyPrefix] + [HarmonyPatch(typeof(NomaiRemoteCameraPlatform), nameof(NomaiRemoteCameraPlatform.Awake))] public static bool NomaiRemoteCameraPlatform_Awake() => false; + + [HarmonyPrefix] + [HarmonyPatch(typeof(NomaiRemoteCameraPlatform), nameof(NomaiRemoteCameraPlatform.Update))] public static bool NomaiRemoteCameraPlatform_Update() => false; + + [HarmonyPrefix] + [HarmonyPatch(typeof(NomaiRemoteCameraPlatform), nameof(NomaiRemoteCameraPlatform.OnSocketableRemoved))] public static bool NomaiRemoteCameraPlatform_OnSocketableRemoved() => false; + + [HarmonyPrefix] + [HarmonyPatch(typeof(NomaiRemoteCameraPlatform), nameof(NomaiRemoteCameraPlatform.OnSocketableDonePlacing))] public static bool NomaiRemoteCameraPlatform_OnSocketableDonePlacing() => false; + + [HarmonyPrefix] + [HarmonyPatch(typeof(NomaiRemoteCameraPlatform), nameof(NomaiRemoteCameraPlatform.OnPedestalContact))] public static bool NomaiRemoteCameraPlatform_OnPedestalContact() => false; + + [HarmonyPrefix] + [HarmonyPatch(typeof(NomaiRemoteCameraStreaming), nameof(NomaiRemoteCameraStreaming.FixedUpdate))] public static bool NomaiRemoteCameraStreaming_FixedUpdate() => false; + + [HarmonyPrefix] + [HarmonyPatch(typeof(NomaiRemoteCameraStreaming), nameof(NomaiRemoteCameraStreaming.OnSectorOccupantAdded))] public static bool NomaiRemoteCameraStreaming_OnSectorOccupantAdded() => false; + + [HarmonyPrefix] + [HarmonyPatch(typeof(NomaiRemoteCameraStreaming), nameof(NomaiRemoteCameraStreaming.OnSectorOccupantRemoved))] public static bool NomaiRemoteCameraStreaming_OnSectorOccupantRemoved() => false; + + [HarmonyPrefix] + [HarmonyPatch(typeof(NomaiRemoteCameraStreaming), nameof(NomaiRemoteCameraStreaming.OnEntry))] public static bool NomaiRemoteCameraStreaming_OnEntry() => false; + + [HarmonyPrefix] + [HarmonyPatch(typeof(NomaiRemoteCameraStreaming), nameof(NomaiRemoteCameraStreaming.OnExit))] public static bool NomaiRemoteCameraStreaming_OnExit() => false; } } diff --git a/QSB/QSB.csproj b/QSB/QSB.csproj index 0fdf46cd..756cbd7c 100644 --- a/QSB/QSB.csproj +++ b/QSB/QSB.csproj @@ -22,6 +22,7 @@ DEBUG;TRACE prompt 4 + true pdbonly @@ -339,69 +340,69 @@ - - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\Assembly-CSharp.dll + + $(GameDir)\OuterWilds_Data\Managed\publicized_assemblies\Assembly-CSharp_publicized.dll - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\Assembly-CSharp-firstpass.dll + $(GameDir)\OuterWilds_Data\Managed\Assembly-CSharp-firstpass.dll - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\netstandard.dll + $(GameDir)\OuterWilds_Data\Managed\netstandard.dll ..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.dll False - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\Unity.InputSystem.dll + $(GameDir)\OuterWilds_Data\Managed\Unity.InputSystem.dll - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.dll + $(GameDir)\OuterWilds_Data\Managed\UnityEngine.dll - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.AnimationModule.dll + $(GameDir)\OuterWilds_Data\Managed\UnityEngine.AnimationModule.dll - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.AssetBundleModule.dll + $(GameDir)\OuterWilds_Data\Managed\UnityEngine.AssetBundleModule.dll - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.AudioModule.dll + $(GameDir)\OuterWilds_Data\Managed\UnityEngine.AudioModule.dll - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.CoreModule.dll + $(GameDir)\OuterWilds_Data\Managed\UnityEngine.CoreModule.dll - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.IMGUIModule.dll + $(GameDir)\OuterWilds_Data\Managed\UnityEngine.IMGUIModule.dll - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.InputLegacyModule.dll + $(GameDir)\OuterWilds_Data\Managed\UnityEngine.InputLegacyModule.dll - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.InputModule.dll + $(GameDir)\OuterWilds_Data\Managed\UnityEngine.InputModule.dll - D:\EpicGames\OuterWilds\OuterWilds_Data\Managed\UnityEngine.Networking.dll + $(GameDir)\OuterWilds_Data\Managed\UnityEngine.Networking.dll - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.ParticleSystemModule.dll + $(GameDir)\OuterWilds_Data\Managed\UnityEngine.ParticleSystemModule.dll - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.PhysicsModule.dll + $(GameDir)\OuterWilds_Data\Managed\UnityEngine.PhysicsModule.dll - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.TextCoreModule.dll + $(GameDir)\OuterWilds_Data\Managed\UnityEngine.TextCoreModule.dll - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.TextRenderingModule.dll + $(GameDir)\OuterWilds_Data\Managed\UnityEngine.TextRenderingModule.dll - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.UI.dll + $(GameDir)\OuterWilds_Data\Managed\UnityEngine.UI.dll - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.UIModule.dll + $(GameDir)\OuterWilds_Data\Managed\UnityEngine.UIModule.dll - D:\SteamLibrary\steamapps\common\Outer Wilds\OuterWilds_Data\Managed\UnityEngine.UNETModule.dll + $(GameDir)\OuterWilds_Data\Managed\UnityEngine.UNETModule.dll diff --git a/QSB/QSBCore.cs b/QSB/QSBCore.cs index 8f58c7b2..b210f046 100644 --- a/QSB/QSBCore.cs +++ b/QSB/QSBCore.cs @@ -69,7 +69,7 @@ namespace QSB public void Awake() { - var instance = TextTranslation.Get().GetValue("m_table"); + var instance = TextTranslation.Get().m_table; instance.theUITable[(int)UITextType.PleaseUseController] = "Quantum Space Buddies is best experienced with friends..."; } diff --git a/QSB/QuantumSync/Patches/ClientQuantumPatches.cs b/QSB/QuantumSync/Patches/ClientQuantumPatches.cs index a25933d4..0a018cc8 100644 --- a/QSB/QuantumSync/Patches/ClientQuantumPatches.cs +++ b/QSB/QuantumSync/Patches/ClientQuantumPatches.cs @@ -1,21 +1,21 @@ -using QSB.Patches; +using HarmonyLib; +using QSB.Patches; using System.Reflection; namespace QSB.QuantumSync.Patches { + [HarmonyPatch] public class ClientQuantumPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnNonServerClientConnect; - public override void DoPatches() - { - Prefix(nameof(QuantumMoon_ChangeQuantumState)); - Postfix(nameof(QuantumMoon_Start)); - } - + [HarmonyPostfix] + [HarmonyPatch(typeof(QuantumMoon), nameof(QuantumMoon.Start))] public static void QuantumMoon_Start(QuantumMoon __instance) => __instance.GetType().GetMethod("SetSurfaceState", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(__instance, new object[] { -1 }); + [HarmonyPrefix] + [HarmonyPatch(typeof(QuantumMoon), nameof(QuantumMoon.ChangeQuantumState))] public static bool QuantumMoon_ChangeQuantumState() => false; } diff --git a/QSB/QuantumSync/Patches/QuantumPatches.cs b/QSB/QuantumSync/Patches/QuantumPatches.cs index bfe4e469..ce0aaca5 100644 --- a/QSB/QuantumSync/Patches/QuantumPatches.cs +++ b/QSB/QuantumSync/Patches/QuantumPatches.cs @@ -1,4 +1,5 @@ -using OWML.Common; +using HarmonyLib; +using OWML.Common; using QSB.Events; using QSB.Patches; using QSB.Player; @@ -12,26 +13,13 @@ using UnityEngine; namespace QSB.QuantumSync.Patches { + [HarmonyPatch] public class QuantumPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; - public override void DoPatches() - { - Prefix(nameof(SocketedQuantumObject_ChangeQuantumState)); - Postfix(nameof(SocketedQuantumObject_MoveToSocket)); - Prefix(nameof(QuantumShuffleObject_ChangeQuantumState)); - Prefix(nameof(MultiStateQuantumObject_ChangeQuantumState)); - Postfix(nameof(QuantumState_SetVisible)); - Prefix(nameof(QuantumShrine_IsPlayerInDarkness)); - Prefix(nameof(QuantumShrine_ChangeQuantumState)); - Prefix(nameof(QuantumShrine_OnEntry)); - Prefix(nameof(QuantumShrine_OnExit)); - Prefix(nameof(QuantumMoon_CheckPlayerFogProximity)); - Prefix(nameof(QuantumObject_IsLockedByPlayerContact)); - Prefix(nameof(MultiStateQuantumObject_Start)); - } - + [HarmonyPrefix] + [HarmonyPatch(typeof(QuantumObject), nameof(QuantumObject.IsLockedByPlayerContact))] public static bool QuantumObject_IsLockedByPlayerContact(ref bool __result, QuantumObject __instance) { var playersEntangled = QuantumManager.GetEntangledPlayers(__instance); @@ -39,6 +27,8 @@ namespace QSB.QuantumSync.Patches return false; } + [HarmonyPrefix] + [HarmonyPatch(typeof(SocketedQuantumObject), nameof(SocketedQuantumObject.ChangeQuantumState))] public static bool SocketedQuantumObject_ChangeQuantumState( SocketedQuantumObject __instance, ref bool __result, @@ -149,6 +139,8 @@ namespace QSB.QuantumSync.Patches return false; } + [HarmonyPostfix] + [HarmonyPatch(typeof(SocketedQuantumObject), nameof(SocketedQuantumObject.MoveToSocket))] public static void SocketedQuantumObject_MoveToSocket(SocketedQuantumObject __instance, QuantumSocket socket) { if (!WorldObjectManager.AllReady) @@ -183,6 +175,8 @@ namespace QSB.QuantumSync.Patches __instance.transform.localRotation); } + [HarmonyPrefix] + [HarmonyPatch(typeof(QuantumShuffleObject), nameof(QuantumShuffleObject.ChangeQuantumState))] public static bool QuantumShuffleObject_ChangeQuantumState( QuantumShuffleObject __instance, ref List ____indexList, @@ -227,6 +221,8 @@ namespace QSB.QuantumSync.Patches return false; } + [HarmonyPrefix] + [HarmonyPatch(typeof(MultiStateQuantumObject), nameof(MultiStateQuantumObject.Start))] public static bool MultiStateQuantumObject_Start(MultiStateQuantumObject __instance, Sector ____sector, bool ____collapseOnStart) { if (!WorldObjectManager.AllReady) @@ -261,6 +257,8 @@ namespace QSB.QuantumSync.Patches return false; } + [HarmonyPrefix] + [HarmonyPatch(typeof(MultiStateQuantumObject), nameof(MultiStateQuantumObject.ChangeQuantumState))] public static bool MultiStateQuantumObject_ChangeQuantumState(MultiStateQuantumObject __instance) { if (!WorldObjectManager.AllReady) @@ -278,6 +276,8 @@ namespace QSB.QuantumSync.Patches return isInControl; } + [HarmonyPostfix] + [HarmonyPatch(typeof(QuantumState), nameof(QuantumState.SetVisible))] public static void QuantumState_SetVisible(QuantumState __instance, bool visible) { if (!WorldObjectManager.AllReady) @@ -305,6 +305,8 @@ namespace QSB.QuantumSync.Patches stateIndex); } + [HarmonyPrefix] + [HarmonyPatch(typeof(QuantumShrine), nameof(QuantumShrine.IsPlayerInDarkness))] public static bool QuantumShrine_IsPlayerInDarkness(ref bool __result, Light[] ____lamps, float ____fadeFraction, bool ____isProbeInside, NomaiGateway ____gate) { foreach (var lamp in ____lamps) @@ -351,6 +353,8 @@ namespace QSB.QuantumSync.Patches return false; } + [HarmonyPrefix] + [HarmonyPatch(typeof(QuantumShrine), nameof(QuantumShrine.ChangeQuantumState))] public static bool QuantumShrine_ChangeQuantumState(QuantumShrine __instance) { var shrineWorldObject = QSBWorldSync.GetWorldFromUnity(__instance); @@ -358,6 +362,8 @@ namespace QSB.QuantumSync.Patches return isInControl; } + [HarmonyPrefix] + [HarmonyPatch(typeof(QuantumShrine), nameof(QuantumShrine.OnEntry))] public static bool QuantumShrine_OnEntry( GameObject hitObj, ref bool ____isPlayerInside, @@ -380,6 +386,8 @@ namespace QSB.QuantumSync.Patches return false; } + [HarmonyPrefix] + [HarmonyPatch(typeof(QuantumShrine), nameof(QuantumShrine.OnExit))] public static bool QuantumShrine_OnExit( GameObject hitObj, ref bool ____isPlayerInside, @@ -402,6 +410,8 @@ namespace QSB.QuantumSync.Patches return false; } + [HarmonyPrefix] + [HarmonyPatch(typeof(QuantumMoon), nameof(QuantumMoon.CheckPlayerFogProximity))] public static bool QuantumMoon_CheckPlayerFogProximity( QuantumMoon __instance, int ____stateIndex, diff --git a/QSB/QuantumSync/Patches/QuantumVisibilityPatches.cs b/QSB/QuantumSync/Patches/QuantumVisibilityPatches.cs index fafadb80..184b6194 100644 --- a/QSB/QuantumSync/Patches/QuantumVisibilityPatches.cs +++ b/QSB/QuantumSync/Patches/QuantumVisibilityPatches.cs @@ -1,4 +1,5 @@ -using QSB.Patches; +using HarmonyLib; +using QSB.Patches; using QSB.Player; using QSB.Utility; using System.Linq; @@ -7,34 +8,33 @@ using UnityEngine; namespace QSB.QuantumSync.Patches { + [HarmonyPatch] public class QuantumVisibilityPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; - public override void DoPatches() - { - Prefix(nameof(ShapeVisibilityTracker_IsVisibleUsingCameraFrustum)); - Prefix(nameof(ShapeVisibilityTracker_IsVisible)); - Prefix(nameof(RendererVisibilityTracker_IsVisibleUsingCameraFrustum)); - Prefix(nameof(VisibilityObject_CheckIllumination)); - Postfix(nameof(Shape_OnEnable)); - Postfix(nameof(Shape_OnDisable)); - } - + [HarmonyPostfix] + [HarmonyPatch(typeof(Shape), nameof(Shape.OnEnable))] public static void Shape_OnEnable(Shape __instance) => __instance.RaiseEvent("OnShapeActivated", __instance); + [HarmonyPostfix] + [HarmonyPatch(typeof(Shape), nameof(Shape.OnDisable))] public static void Shape_OnDisable(Shape __instance) => __instance.RaiseEvent("OnShapeDeactivated", __instance); // ShapeVisibilityTracker patches + [HarmonyPrefix] + [HarmonyPatch(typeof(ShapeVisibilityTracker), nameof(ShapeVisibilityTracker.IsVisibleUsingCameraFrustum))] public static bool ShapeVisibilityTracker_IsVisibleUsingCameraFrustum(ShapeVisibilityTracker __instance, ref bool __result) { __result = QuantumManager.IsVisibleUsingCameraFrustum(__instance, false).Item1; return false; } + [HarmonyPrefix] + [HarmonyPatch(typeof(ShapeVisibilityTracker), nameof(ShapeVisibilityTracker.IsVisible))] public static bool ShapeVisibilityTracker_IsVisible(ShapeVisibilityTracker __instance, ref bool __result) { __result = QuantumManager.IsVisible(__instance, false); @@ -43,6 +43,8 @@ namespace QSB.QuantumSync.Patches // RendererVisibilityTracker patches - probably not needed as i don't think RendererVisibilityTracker is ever used? + [HarmonyPrefix] + [HarmonyPatch(typeof(RendererVisibilityTracker), nameof(RendererVisibilityTracker.IsVisibleUsingCameraFrustum))] public static bool RendererVisibilityTracker_IsVisibleUsingCameraFrustum(RendererVisibilityTracker __instance, ref bool __result, Renderer ____renderer, bool ____checkFrustumOcclusion) { __result = QSBPlayerManager.GetPlayersWithCameras() @@ -56,6 +58,8 @@ namespace QSB.QuantumSync.Patches // VisibilityObject + [HarmonyPrefix] + [HarmonyPatch(typeof(VisibilityObject), nameof(VisibilityObject.CheckIllumination))] public static bool VisibilityObject_CheckIllumination(VisibilityObject __instance, ref bool __result, bool ____checkIllumination, Vector3 ____localIlluminationOffset, float ____illuminationRadius, Light[] ____lightSources) { if (!____checkIllumination) diff --git a/QSB/QuantumSync/Patches/ServerQuantumPatches.cs b/QSB/QuantumSync/Patches/ServerQuantumPatches.cs index c6572f85..1c664104 100644 --- a/QSB/QuantumSync/Patches/ServerQuantumPatches.cs +++ b/QSB/QuantumSync/Patches/ServerQuantumPatches.cs @@ -1,4 +1,5 @@ -using OWML.Common; +using HarmonyLib; +using OWML.Common; using QSB.Events; using QSB.Patches; using QSB.Player; @@ -9,13 +10,13 @@ using UnityEngine; namespace QSB.QuantumSync.Patches { + [HarmonyPatch] public class ServerQuantumPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnServerClientConnect; - public override void DoPatches() - => Prefix(nameof(QuantumMoon_ChangeQuantumState)); - + [HarmonyPrefix] + [HarmonyPatch(typeof(QuantumMoon), nameof(QuantumMoon.ChangeQuantumState))] public static bool QuantumMoon_ChangeQuantumState( QuantumMoon __instance, ref bool __result, diff --git a/QSB/RoastingSync/Patches/RoastingPatches.cs b/QSB/RoastingSync/Patches/RoastingPatches.cs index 2fa6da8b..0e85f3d2 100644 --- a/QSB/RoastingSync/Patches/RoastingPatches.cs +++ b/QSB/RoastingSync/Patches/RoastingPatches.cs @@ -1,28 +1,34 @@ -using QSB.Events; +using HarmonyLib; +using QSB.Events; using QSB.Patches; using UnityEngine; namespace QSB.RoastingSync.Patches { + [HarmonyPatch] internal class RoastingPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; - public override void DoPatches() - { - Prefix(nameof(RoastingStickController_UpdateMarshmallowInput)); - Prefix(nameof(Marshmallow_Burn)); - Prefix(nameof(Marshmallow_Shrivel)); - Prefix(nameof(Marshmallow_RemoveMallow)); - Prefix(nameof(Marshmallow_SpawnMallow)); - } + //public override void DoPatches() + //{ + // Prefix(nameof(RoastingStickController.UpdateMarshmallowInput), nameof(RoastingStickController_UpdateMarshmallowInput)); + // Prefix(nameof(Marshmallow.Burn), nameof(Marshmallow_Burn)); + // Prefix(nameof(Marshmallow.Shrivel), nameof(Marshmallow_Shrivel)); + // Prefix(nameof(Marshmallow.RemoveMallow), nameof(Marshmallow_RemoveMallow)); + // Prefix(nameof(Marshmallow.SpawnMallow), nameof(Marshmallow_SpawnMallow)); + //} + [HarmonyPrefix] + [HarmonyPatch(typeof(Marshmallow), nameof(Marshmallow.SpawnMallow))] public static bool Marshmallow_SpawnMallow() { QSBEventManager.FireEvent(EventNames.QSBMarshmallowEvent, MarshmallowEventType.Replace); return true; } + [HarmonyPrefix] + [HarmonyPatch(typeof(Marshmallow), nameof(Marshmallow.Burn))] public static bool Marshmallow_Burn( ref Marshmallow.MallowState ____mallowState, MeshRenderer ____fireRenderer, @@ -43,6 +49,8 @@ namespace QSB.RoastingSync.Patches return false; } + [HarmonyPrefix] + [HarmonyPatch(typeof(Marshmallow), nameof(Marshmallow.Shrivel))] public static bool Marshmallow_Shrivel( ref Marshmallow.MallowState ____mallowState, ref float ____initShrivelTime) @@ -57,6 +65,8 @@ namespace QSB.RoastingSync.Patches return false; } + [HarmonyPrefix] + [HarmonyPatch(typeof(Marshmallow), nameof(Marshmallow.RemoveMallow))] public static bool Marshmallow_RemoveMallow( ParticleSystem ____smokeParticles, MeshRenderer ____fireRenderer, @@ -73,6 +83,8 @@ namespace QSB.RoastingSync.Patches return false; } + [HarmonyPrefix] + [HarmonyPatch(typeof(RoastingStickController), nameof(RoastingStickController.UpdateMarshmallowInput))] public static bool RoastingStickController_UpdateMarshmallowInput( float ____extendFraction, Marshmallow ____marshmallow, diff --git a/QSB/SectorSync/FakeSector.cs b/QSB/SectorSync/FakeSector.cs index 61c51a90..19144eb5 100644 --- a/QSB/SectorSync/FakeSector.cs +++ b/QSB/SectorSync/FakeSector.cs @@ -4,6 +4,6 @@ { public Sector AttachedSector; - protected override void Awake() { } + public override void Awake() { } } } diff --git a/QSB/ShipSync/Patches/ShipPatches.cs b/QSB/ShipSync/Patches/ShipPatches.cs index bfbe87c4..57fbb477 100644 --- a/QSB/ShipSync/Patches/ShipPatches.cs +++ b/QSB/ShipSync/Patches/ShipPatches.cs @@ -1,30 +1,22 @@ -using OWML.Utils; +using HarmonyLib; +using OWML.Utils; using QSB.Events; using QSB.Patches; using QSB.Utility; +using System; +using System.Collections.Generic; +using System.Reflection.Emit; using UnityEngine; namespace QSB.ShipSync.Patches { + [HarmonyPatch] internal class ShipPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; - public override void DoPatches() - { - Prefix(nameof(HatchController_OnPressInteract)); - Prefix(nameof(HatchController_OnEntry)); - Prefix(nameof(ShipTractorBeamSwitch_OnTriggerExit)); - Prefix(nameof(InteractZone_UpdateInteractVolume)); - Prefix(nameof(ShipElectricalComponent_OnEnterShip)); - Prefix(nameof(ShipElectricalComponent_OnExitShip)); - Prefix(nameof(ShipComponent_SetDamaged)); - Prefix(nameof(ShipHull_FixedUpdate)); - Prefix(nameof(ShipDamageController_OnImpact)); - Postfix(nameof(ShipComponent_RepairTick)); - Prefix(nameof(ShipHull_RepairTick)); - } - + [HarmonyPrefix] + [HarmonyPatch(typeof(HatchController), nameof(HatchController.OnPressInteract))] public static bool HatchController_OnPressInteract() { if (!PlayerState.IsInsideShip()) @@ -37,6 +29,8 @@ namespace QSB.ShipSync.Patches return true; } + [HarmonyPrefix] + [HarmonyPatch(typeof(HatchController), nameof(HatchController.OnEntry))] public static bool HatchController_OnEntry(GameObject hitObj) { if (hitObj.CompareTag("PlayerDetector")) @@ -47,9 +41,11 @@ namespace QSB.ShipSync.Patches return true; } - public static bool ShipTractorBeamSwitch_OnTriggerExit(Collider hitCollider, bool ____isPlayerInShip, bool ____functional) + [HarmonyPrefix] + [HarmonyPatch(typeof(ShipTractorBeamSwitch), nameof(ShipTractorBeamSwitch.OnTriggerExit))] + public static bool ShipTractorBeamSwitch_OnTriggerExit(ShipTractorBeamSwitch __instance, Collider hitCollider) { - if (!____isPlayerInShip && ____functional && hitCollider.CompareTag("PlayerDetector") && !ShipManager.Instance.HatchController.GetValue("_hatchObject").activeSelf) + if (!__instance._isPlayerInShip && __instance._functional && hitCollider.CompareTag("PlayerDetector") && !ShipManager.Instance.HatchController._hatchObject.activeSelf) { ShipManager.Instance.HatchController.Invoke("CloseHatch"); ShipManager.Instance.ShipTractorBeam.DeactivateTractorBeam(); @@ -59,7 +55,16 @@ namespace QSB.ShipSync.Patches return false; } - public static bool InteractZone_UpdateInteractVolume(InteractZone __instance, OWCamera ____playerCam, ref bool ____focused) + [HarmonyReversePatch] + [HarmonyPatch(typeof(SingleInteractionVolume), nameof(SingleInteractionVolume.UpdateInteractVolume))] + public static void SingleInteractionVolume_UpdateInteractVolume_Stub(object instance) + { + throw new NotImplementedException(); + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(InteractZone), nameof(InteractZone.UpdateInteractVolume))] + public static bool InteractZone_UpdateInteractVolume(InteractZone __instance) { /* Angle for interaction with the ship hatch * @@ -78,64 +83,86 @@ namespace QSB.ShipSync.Patches return true; } - var angle = 2f * Vector3.Angle(____playerCam.transform.forward, __instance.transform.forward); + var angle = 2f * Vector3.Angle(__instance._playerCam.transform.forward, __instance.transform.forward); - ____focused = PlayerState.IsInsideShip() + __instance._focused = PlayerState.IsInsideShip() ? angle <= 80 : angle >= 280; - __instance.CallBase("UpdateInteractVolume"); + SingleInteractionVolume_UpdateInteractVolume_Stub(__instance as SingleInteractionVolume); return false; } - public static bool ShipElectricalComponent_OnEnterShip(ShipElectricalComponent __instance, bool ____damaged, ElectricalSystem ____electricalSystem) + [HarmonyReversePatch] + [HarmonyPatch(typeof(ShipComponent), nameof(ShipComponent.OnEnterShip))] + public static void ShipComponent_OnEnterShip_Stub(object instance) { - __instance.CallBase("OnEnterShip"); + throw new NotImplementedException(); + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(ShipElectricalComponent), nameof(ShipElectricalComponent.OnEnterShip))] + public static bool ShipElectricalComponent_OnEnterShip(ShipElectricalComponent __instance) + { + ShipComponent_OnEnterShip_Stub(__instance as ShipComponent); return false; } - public static bool ShipElectricalComponent_OnExitShip(ShipElectricalComponent __instance, bool ____damaged, ElectricalSystem ____electricalSystem) + [HarmonyReversePatch] + [HarmonyPatch(typeof(ShipComponent), nameof(ShipComponent.OnExitShip))] + public static void ShipComponent_OnExitShip_Stub(object instance) { - __instance.CallBase("OnExitShip"); + throw new NotImplementedException(); + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(ShipElectricalComponent), nameof(ShipElectricalComponent.OnExitShip))] + public static bool ShipElectricalComponent_OnExitShip(ShipElectricalComponent __instance) + { + ShipComponent_OnExitShip_Stub(__instance as ShipComponent); return false; } - public static bool ShipComponent_SetDamaged(ShipComponent __instance, bool damaged, ref bool ____damaged, ref float ____repairFraction, DamageEffect ____damageEffect) + [HarmonyPrefix] + [HarmonyPatch(typeof(ShipComponent), nameof(ShipComponent.SetDamaged))] + public static bool ShipComponent_SetDamaged(ShipComponent __instance, bool damaged) { - if (____damaged == damaged) + if (__instance._damaged == damaged) { return false; } if (damaged) { - ____damaged = true; - ____repairFraction = 0f; + __instance._damaged = true; + __instance._repairFraction = 0f; __instance.GetType().GetAnyMethod("OnComponentDamaged").Invoke(__instance, null); __instance.RaiseEvent("OnDamaged", __instance); QSBEventManager.FireEvent(EventNames.QSBComponentDamaged, __instance); } else { - ____damaged = false; - ____repairFraction = 1f; + __instance._damaged = false; + __instance._repairFraction = 1f; __instance.GetType().GetAnyMethod("OnComponentRepaired").Invoke(__instance, null); __instance.RaiseEvent("OnRepaired", __instance); QSBEventManager.FireEvent(EventNames.QSBComponentRepaired, __instance); } __instance.GetType().GetAnyMethod("UpdateColliderState").Invoke(__instance, null); - if (____damageEffect) + if (__instance._damageEffect) { - ____damageEffect.SetEffectBlend(1f - ____repairFraction); + __instance._damageEffect.SetEffectBlend(1f - __instance._repairFraction); } return false; } + [HarmonyPrefix] + [HarmonyPatch(typeof(ShipHull), nameof(ShipHull.FixedUpdate))] public static bool ShipHull_FixedUpdate(ShipHull __instance, ref ImpactData ____dominantImpact, ref float ____integrity, ref bool ____damaged, DamageEffect ____damageEffect, ShipComponent[] ____components) { if (____dominantImpact != null) @@ -186,15 +213,21 @@ namespace QSB.ShipSync.Patches return false; } + [HarmonyPrefix] + [HarmonyPatch(typeof(ShipDamageController), nameof(ShipDamageController.OnImpact))] public static bool ShipDamageController_OnImpact() => ShipManager.Instance.HasAuthority; + [HarmonyPostfix] + [HarmonyPatch(typeof(ShipComponent), nameof(ShipComponent.RepairTick))] public static void ShipComponent_RepairTick(ShipComponent __instance, float ____repairFraction) { QSBEventManager.FireEvent(EventNames.QSBComponentRepairTick, __instance, ____repairFraction); return; } + [HarmonyPrefix] + [HarmonyPatch(typeof(ShipHull), nameof(ShipHull.RepairTick))] public static bool ShipHull_RepairTick(ShipHull __instance, ref float ____integrity, ref bool ____damaged, DamageEffect ____damageEffect, float ____repairTime) { if (!____damaged) diff --git a/QSB/StatueSync/Patches/StatuePatches.cs b/QSB/StatueSync/Patches/StatuePatches.cs index bce3385a..821ae821 100644 --- a/QSB/StatueSync/Patches/StatuePatches.cs +++ b/QSB/StatueSync/Patches/StatuePatches.cs @@ -1,17 +1,18 @@ -using QSB.Events; +using HarmonyLib; +using QSB.Events; using QSB.Patches; using QSB.Player; using UnityEngine; namespace QSB.StatueSync.Patches { + [HarmonyPatch] internal class StatuePatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; - public override void DoPatches() - => Prefix(nameof(MemoryUplinkTrigger_Update)); - + [HarmonyPrefix] + [HarmonyPatch(typeof(MemoryUplinkTrigger), nameof(MemoryUplinkTrigger.Update))] public static bool MemoryUplinkTrigger_Update(bool ____waitForPlayerGrounded) { if (StatueManager.Instance.HasStartedStatueLocally) diff --git a/QSB/TimeSync/Patches/TimePatches.cs b/QSB/TimeSync/Patches/TimePatches.cs index a0d5f48a..7b43dd11 100644 --- a/QSB/TimeSync/Patches/TimePatches.cs +++ b/QSB/TimeSync/Patches/TimePatches.cs @@ -1,19 +1,26 @@ -using QSB.Patches; +using HarmonyLib; +using QSB.Patches; namespace QSB.TimeSync.Patches { + [HarmonyPatch] internal class TimePatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; - public override void DoPatches() - { - Prefix(nameof(PlayerCameraEffectController_OnStartOfTimeLoop)); - Empty("OWTime_Pause"); - Empty("SubmitActionSkipToNextLoop_AdvanceToNextLoop"); // TODO : remove this, remove meditation button instead - } - + [HarmonyPrefix] + [HarmonyPatch(typeof(PlayerCameraEffectController), nameof(PlayerCameraEffectController.OnStartOfTimeLoop))] public static bool PlayerCameraEffectController_OnStartOfTimeLoop() => false; + + [HarmonyPrefix] + [HarmonyPatch(typeof(OWTime), nameof(OWTime.Pause))] + public static bool StopPausing() + => false; + + [HarmonyPrefix] + [HarmonyPatch(typeof(SubmitActionSkipToNextLoop), nameof(SubmitActionSkipToNextLoop.AdvanceToNewTimeLoop))] + public static bool StopMeditation() + => false; } } diff --git a/QSB/Tools/ProbeLauncherTool/Patches/LauncherPatches.cs b/QSB/Tools/ProbeLauncherTool/Patches/LauncherPatches.cs index 619931a0..f1427d25 100644 --- a/QSB/Tools/ProbeLauncherTool/Patches/LauncherPatches.cs +++ b/QSB/Tools/ProbeLauncherTool/Patches/LauncherPatches.cs @@ -1,4 +1,5 @@ -using QSB.Events; +using HarmonyLib; +using QSB.Events; using QSB.Patches; using QSB.Player; using QSB.Tools.ProbeLauncherTool.WorldObjects; @@ -7,17 +8,13 @@ using UnityEngine; namespace QSB.Tools.ProbeLauncherTool.Patches { + [HarmonyPatch] internal class LauncherPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; - public override void DoPatches() - { - Prefix(nameof(ProbeLauncher_RetrieveProbe)); - Postfix(nameof(ProbeLauncherEffects_PlayRetrievalClip)); - Postfix(nameof(ProbeLauncherEffects_PlayLaunchClip)); - } - + [HarmonyPrefix] + [HarmonyPatch(typeof(ProbeLauncher), nameof(ProbeLauncher.RetrieveProbe))] public static bool ProbeLauncher_RetrieveProbe( ProbeLauncher __instance, bool playEffects, @@ -74,8 +71,12 @@ namespace QSB.Tools.ProbeLauncherTool.Patches // TODO : ehhhh idk about this. maybe copy each sound source so we have a 2d version (for local) and a 3d version (for remote)? // this would probably be a whole qsb version on it's own + [HarmonyPostfix] + [HarmonyPatch(typeof(ProbeLauncherEffects), nameof(ProbeLauncherEffects.PlayRetrievalClip))] public static void ProbeLauncherEffects_PlayRetrievalClip(OWAudioSource ____owAudioSource) => ____owAudioSource.GetAudioSource().spatialBlend = 1f; + [HarmonyPostfix] + [HarmonyPatch(typeof(ProbeLauncherEffects), nameof(ProbeLauncherEffects.PlayLaunchClip))] public static void ProbeLauncherEffects_PlayLaunchClip(OWAudioSource ____owAudioSource) => ____owAudioSource.GetAudioSource().spatialBlend = 1f; } } diff --git a/QSB/TranslationSync/Patches/SpiralPatches.cs b/QSB/TranslationSync/Patches/SpiralPatches.cs index b608e0e4..788366b0 100644 --- a/QSB/TranslationSync/Patches/SpiralPatches.cs +++ b/QSB/TranslationSync/Patches/SpiralPatches.cs @@ -1,21 +1,18 @@ -using QSB.Events; +using HarmonyLib; +using QSB.Events; using QSB.Patches; using QSB.TranslationSync.WorldObjects; using QSB.WorldSync; namespace QSB.TranslationSync.Patches { + [HarmonyPatch] internal class SpiralPatches : QSBPatch { public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; - public override void DoPatches() - { - Prefix(nameof(NomaiWallText_SetAsTranslated)); - Prefix(nameof(NomaiComputer_SetAsTranslated)); - Prefix(nameof(NomaiVesselComputer_SetAsTranslated)); - } - + [HarmonyPrefix] + [HarmonyPatch(typeof(NomaiWallText), nameof(NomaiWallText.SetAsTranslated))] public static bool NomaiWallText_SetAsTranslated(NomaiWallText __instance, int id) { if (__instance.IsTranslated(id)) @@ -31,6 +28,8 @@ namespace QSB.TranslationSync.Patches return true; } + [HarmonyPrefix] + [HarmonyPatch(typeof(NomaiComputer), nameof(NomaiWallText.SetAsTranslated))] public static bool NomaiComputer_SetAsTranslated(NomaiComputer __instance, int id) { if (__instance.IsTranslated(id)) @@ -46,6 +45,8 @@ namespace QSB.TranslationSync.Patches return true; } + [HarmonyPrefix] + [HarmonyPatch(typeof(NomaiVesselComputer), nameof(NomaiWallText.SetAsTranslated))] public static bool NomaiVesselComputer_SetAsTranslated(NomaiVesselComputer __instance, int id) { if (__instance.IsTranslated(id)) diff --git a/QSB/Utility/Extensions.cs b/QSB/Utility/Extensions.cs index dbba8bd7..b787cc36 100644 --- a/QSB/Utility/Extensions.cs +++ b/QSB/Utility/Extensions.cs @@ -129,27 +129,6 @@ namespace QSB.Utility multiDelegate.GetInvocationList().ToList().ForEach(dl => dl.DynamicInvoke(args)); } - public static void CallBase(this ThisType obj, string methodName) - where ThisType : BaseType - { - var method = typeof(BaseType).GetAnyMethod(methodName); - if (method == null) - { - DebugLog.ToConsole($"Error - Couldn't find method {methodName} in {typeof(BaseType).FullName}!", MessageType.Error); - return; - } - - var functionPointer = method.MethodHandle.GetFunctionPointer(); - if (functionPointer == null) - { - DebugLog.ToConsole($"Error - Function pointer for {methodName} in {typeof(BaseType).FullName} is null!", MessageType.Error); - return; - } - - var methodAction = (Action)Activator.CreateInstance(typeof(Action), obj, functionPointer); - methodAction(); - } - // OW public static Vector3 GetRelativeAngularVelocity(this OWRigidbody baseBody, OWRigidbody relativeBody) diff --git a/QSBTests/PatchTests.cs b/QSBTests/PatchTests.cs index 29d3ebdd..bdbe1b48 100644 --- a/QSBTests/PatchTests.cs +++ b/QSBTests/PatchTests.cs @@ -10,32 +10,32 @@ namespace QSBTests [TestClass] public class PatchTests { - [TestMethod] - public void CheckUnreferencedPatches() - { - var qsbAssembly = Assembly.Load("QSB"); - var allPatchTypes = qsbAssembly - .GetTypes() - .Where(x => typeof(QSBPatch).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract); + //[TestMethod] + //public void CheckUnreferencedPatches() + //{ + // var qsbAssembly = Assembly.Load("QSB"); + // var allPatchTypes = qsbAssembly + // .GetTypes() + // .Where(x => typeof(QSBPatch).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract); - QSBPatchManager.Init(); - var patchInstances = (List)typeof(QSBPatchManager) - .GetField("_patchList", BindingFlags.NonPublic | BindingFlags.Static) - .GetValue(typeof(QSBPatchManager)); + // QSBPatchManager.Init(); + // var patchInstances = (List)typeof(QSBPatchManager) + // .GetField("_patchList", BindingFlags.NonPublic | BindingFlags.Static) + // .GetValue(typeof(QSBPatchManager)); - var failedTypes = new List(); - foreach (var type in allPatchTypes) - { - if (!patchInstances.Any(x => x.GetType() == type)) - { - failedTypes.Add(type); - } - } + // var failedTypes = new List(); + // foreach (var type in allPatchTypes) + // { + // if (!patchInstances.Any(x => x.GetType() == type)) + // { + // failedTypes.Add(type); + // } + // } - if (failedTypes.Count > 0) - { - Assert.Fail(string.Join(", ", failedTypes.Select(x => x.Name))); - } - } + // if (failedTypes.Count > 0) + // { + // Assert.Fail(string.Join(", ", failedTypes.Select(x => x.Name))); + // } + //} } } From 52f6d497afd265abf65aa48b940586ba244b5e60 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Fri, 15 Oct 2021 21:07:02 +0100 Subject: [PATCH 111/118] bump to pr4 --- QSB/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QSB/manifest.json b/QSB/manifest.json index ced3246e..3779d4d0 100644 --- a/QSB/manifest.json +++ b/QSB/manifest.json @@ -8,6 +8,6 @@ "body": "- Disable *all* other mods. (Can heavily affect performance)\n- Make sure you are not running any other network-intensive applications.\n- Make sure you have forwarded/opened the correct ports. (See the GitHub readme.)" }, "uniqueName": "Raicuparta.QuantumSpaceBuddies", - "version": "0.12.0-pr2", + "version": "0.12.0-pr4", "owmlVersion": "2.0.0" } \ No newline at end of file From 2024e5fcd9f166037089e4e330e8297e1ec8945f Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Fri, 15 Oct 2021 21:08:17 +0100 Subject: [PATCH 112/118] cleanup --- QSB/ClientServerStateSync/ClientStateManager.cs | 2 +- QSB/DeathSync/Patches/DeathPatches.cs | 2 -- QSB/ItemSync/Patches/ItemPatches.cs | 2 +- QSB/Menus/MenuManager.cs | 7 +++---- QSB/QSBCore.cs | 1 - QSB/ShipSync/Patches/ShipPatches.cs | 2 -- QSB/ShipSync/TransformSync/ShipTransformSync.cs | 2 -- QSB/Syncs/IntermediaryTransform.cs | 1 - QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs | 2 +- QSB/Utility/DebugGUI.cs | 2 +- QSB/Utility/Extensions.cs | 1 - QSBTests/PatchTests.cs | 5 ----- QuantumUNET/Components/QNetworkManagerHUD.cs | 5 +---- QuantumUNET/Messages/QSpawnDelegate.cs | 1 - QuantumUNET/QNetworkServer.cs | 1 - QuantumUNET/QNetworkServerSimple.cs | 7 +++---- 16 files changed, 11 insertions(+), 32 deletions(-) diff --git a/QSB/ClientServerStateSync/ClientStateManager.cs b/QSB/ClientServerStateSync/ClientStateManager.cs index 50b4458d..2e1d1d11 100644 --- a/QSB/ClientServerStateSync/ClientStateManager.cs +++ b/QSB/ClientServerStateSync/ClientStateManager.cs @@ -42,7 +42,7 @@ namespace QSB.ClientServerStateSync if (QSBCore.IsHost) { - + switch (newScene) { case OWScene.TitleScreen: diff --git a/QSB/DeathSync/Patches/DeathPatches.cs b/QSB/DeathSync/Patches/DeathPatches.cs index d012b634..28c9ddfe 100644 --- a/QSB/DeathSync/Patches/DeathPatches.cs +++ b/QSB/DeathSync/Patches/DeathPatches.cs @@ -4,9 +4,7 @@ using QSB.Patches; using QSB.Player; using QSB.ShipSync; using QSB.Utility; -using System.Collections.Generic; using System.Linq; -using System.Reflection.Emit; using UnityEngine; namespace QSB.DeathSync.Patches diff --git a/QSB/ItemSync/Patches/ItemPatches.cs b/QSB/ItemSync/Patches/ItemPatches.cs index 03667c05..d515a122 100644 --- a/QSB/ItemSync/Patches/ItemPatches.cs +++ b/QSB/ItemSync/Patches/ItemPatches.cs @@ -69,7 +69,7 @@ namespace QSB.ItemSync.Patches { sector = sectorGroup.GetSector(); if (sector == null && sectorGroup is SectorCullGroup) -{ + { SectorProxy controllingProxy = (sectorGroup as SectorCullGroup).GetControllingProxy(); if (controllingProxy != null) { diff --git a/QSB/Menus/MenuManager.cs b/QSB/Menus/MenuManager.cs index 1aa53ff1..cccdfb0d 100644 --- a/QSB/Menus/MenuManager.cs +++ b/QSB/Menus/MenuManager.cs @@ -1,5 +1,4 @@ using QSB.Player; -using QSB.Utility; using UnityEngine; using UnityEngine.Networking; using UnityEngine.UI; @@ -91,7 +90,7 @@ namespace QSB.Menus DisconnectButton = MenuApi.PauseMenu_MakeSimpleButton("DISCONNECT"); DisconnectButton.onClick.AddListener(Disconnect); - + if (QSBCore.IsInMultiplayer) { @@ -167,8 +166,8 @@ namespace QSB.Menus private void OnConnected() { - var text = QSBCore.IsHost - ? "STOP HOSTING" + var text = QSBCore.IsHost + ? "STOP HOSTING" : "DISCONNECT"; DisconnectButton.transform.GetChild(0).GetChild(1).GetComponent().text = text; } diff --git a/QSB/QSBCore.cs b/QSB/QSBCore.cs index b210f046..91dc9ee3 100644 --- a/QSB/QSBCore.cs +++ b/QSB/QSBCore.cs @@ -1,7 +1,6 @@ using OWML.Common; using OWML.ModHelper; using OWML.ModHelper.Input; -using OWML.Utils; using QSB.Animation.NPC; using QSB.CampfireSync; using QSB.ConversationSync; diff --git a/QSB/ShipSync/Patches/ShipPatches.cs b/QSB/ShipSync/Patches/ShipPatches.cs index 57fbb477..0d3c5c9a 100644 --- a/QSB/ShipSync/Patches/ShipPatches.cs +++ b/QSB/ShipSync/Patches/ShipPatches.cs @@ -4,8 +4,6 @@ using QSB.Events; using QSB.Patches; using QSB.Utility; using System; -using System.Collections.Generic; -using System.Reflection.Emit; using UnityEngine; namespace QSB.ShipSync.Patches diff --git a/QSB/ShipSync/TransformSync/ShipTransformSync.cs b/QSB/ShipSync/TransformSync/ShipTransformSync.cs index 235072e4..80359b87 100644 --- a/QSB/ShipSync/TransformSync/ShipTransformSync.cs +++ b/QSB/ShipSync/TransformSync/ShipTransformSync.cs @@ -1,8 +1,6 @@ using QSB.SectorSync; using QSB.Syncs.Sectored.Rigidbodies; -using QSB.Utility; using QSB.WorldSync; -using UnityEngine; namespace QSB.ShipSync.TransformSync { diff --git a/QSB/Syncs/IntermediaryTransform.cs b/QSB/Syncs/IntermediaryTransform.cs index 1db9e142..ea5f95de 100644 --- a/QSB/Syncs/IntermediaryTransform.cs +++ b/QSB/Syncs/IntermediaryTransform.cs @@ -1,7 +1,6 @@ using OWML.Common; using QSB.Utility; using System; -using System.Reflection; using UnityEngine; namespace QSB.Syncs diff --git a/QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs b/QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs index 5ed974c7..55798988 100644 --- a/QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs +++ b/QSB/Syncs/Sectored/Transforms/SectoredTransformSync.cs @@ -79,7 +79,7 @@ namespace QSB.Syncs.Sectored.Transforms _intermediaryTransform.SetPosition(Vector3.zero); _intermediaryTransform.SetRotation(Quaternion.identity); } - + return true; } diff --git a/QSB/Utility/DebugGUI.cs b/QSB/Utility/DebugGUI.cs index bf688a0b..68bada16 100644 --- a/QSB/Utility/DebugGUI.cs +++ b/QSB/Utility/DebugGUI.cs @@ -41,7 +41,7 @@ namespace QSB.Utility var reason = WakeUpSync.LocalInstance.CurrentReason; if (currentState == WakeUpSync.State.FastForwarding && reason != null) { - + GUI.Label(new Rect(220, offset, 200f, 20f), $"Reason : {(FastForwardReason)reason}", guiStyle); offset += _debugLineSpacing; } diff --git a/QSB/Utility/Extensions.cs b/QSB/Utility/Extensions.cs index b787cc36..5f5433d4 100644 --- a/QSB/Utility/Extensions.cs +++ b/QSB/Utility/Extensions.cs @@ -1,5 +1,4 @@ using OWML.Common; -using OWML.Utils; using QSB.Player; using QSB.Player.TransformSync; using QuantumUNET; diff --git a/QSBTests/PatchTests.cs b/QSBTests/PatchTests.cs index bdbe1b48..26ece311 100644 --- a/QSBTests/PatchTests.cs +++ b/QSBTests/PatchTests.cs @@ -1,9 +1,4 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using QSB.Patches; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; namespace QSBTests { diff --git a/QuantumUNET/Components/QNetworkManagerHUD.cs b/QuantumUNET/Components/QNetworkManagerHUD.cs index 7ef531bf..2da86771 100644 --- a/QuantumUNET/Components/QNetworkManagerHUD.cs +++ b/QuantumUNET/Components/QNetworkManagerHUD.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; +using System.ComponentModel; using UnityEngine; -using UnityEngine.Networking.Match; namespace QuantumUNET.Components { diff --git a/QuantumUNET/Messages/QSpawnDelegate.cs b/QuantumUNET/Messages/QSpawnDelegate.cs index f607acef..7f5ea8ee 100644 --- a/QuantumUNET/Messages/QSpawnDelegate.cs +++ b/QuantumUNET/Messages/QSpawnDelegate.cs @@ -1,5 +1,4 @@ using UnityEngine; -using UnityEngine.Networking; namespace QuantumUNET { diff --git a/QuantumUNET/QNetworkServer.cs b/QuantumUNET/QNetworkServer.cs index b40e456b..951216cc 100644 --- a/QuantumUNET/QNetworkServer.cs +++ b/QuantumUNET/QNetworkServer.cs @@ -8,7 +8,6 @@ using System.Collections.ObjectModel; using System.Linq; using UnityEngine; using UnityEngine.Networking; -using UnityEngine.Networking.Types; namespace QuantumUNET { diff --git a/QuantumUNET/QNetworkServerSimple.cs b/QuantumUNET/QNetworkServerSimple.cs index 62beef3e..e3a44be9 100644 --- a/QuantumUNET/QNetworkServerSimple.cs +++ b/QuantumUNET/QNetworkServerSimple.cs @@ -6,7 +6,6 @@ using System.Collections.ObjectModel; using System.Linq; using UnityEngine; using UnityEngine.Networking; -using UnityEngine.Networking.Types; namespace QuantumUNET { @@ -22,8 +21,8 @@ namespace QuantumUNET public NetworkReader messageReader { get; private set; } public Type networkConnectionClass { get; private set; } = typeof(QNetworkConnection); - public void SetNetworkConnectionClass() - where T : QNetworkConnection + public void SetNetworkConnectionClass() + where T : QNetworkConnection => networkConnectionClass = typeof(T); public virtual void Initialize() @@ -77,7 +76,7 @@ namespace QuantumUNET return result; } - public bool Listen(int serverListenPort) + public bool Listen(int serverListenPort) => Listen(serverListenPort, hostTopology); public bool Listen(int serverListenPort, HostTopology topology) From e929e38bec069a1c2e5d08f806554a4422dc1cb4 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 16 Oct 2021 14:12:28 +0100 Subject: [PATCH 113/118] rework unpatches (have more harnony instances) --- QSB/Patches/QSBPatch.cs | 16 +++------------- QSB/Patches/QSBPatchManager.cs | 26 ++++++++++++++++++-------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/QSB/Patches/QSBPatch.cs b/QSB/Patches/QSBPatch.cs index 4e3a4098..bf1f7af5 100644 --- a/QSB/Patches/QSBPatch.cs +++ b/QSB/Patches/QSBPatch.cs @@ -1,5 +1,4 @@ -using QSB.Utility; -using System.Linq; +using HarmonyLib; namespace QSB.Patches { @@ -7,18 +6,9 @@ namespace QSB.Patches { public abstract QSBPatchTypes Type { get; } - public virtual void DoPatches() - { - var oldMethods = QSBPatchManager.HarmonyInstance.GetPatchedMethods(); - QSBPatchManager.HarmonyInstance.PatchAll(GetType()); - foreach (var method in QSBPatchManager.HarmonyInstance.GetPatchedMethods().Except(oldMethods)) - { - DebugLog.DebugWrite($"- Patching {method.DeclaringType}.{method.Name}"); - } - } - - public void DoUnpatches() + public void DoPatches(Harmony instance) { + instance.PatchAll(GetType()); } } } \ No newline at end of file diff --git a/QSB/Patches/QSBPatchManager.cs b/QSB/Patches/QSBPatchManager.cs index 8559b627..e89e7f9f 100644 --- a/QSB/Patches/QSBPatchManager.cs +++ b/QSB/Patches/QSBPatchManager.cs @@ -36,7 +36,11 @@ namespace QSB.Patches private static List _patchList = new List(); - public static Harmony HarmonyInstance; + public static Harmony ClientInstance; + public static Harmony ServerInstance; + public static Harmony NonServerInstance; + public static Harmony DeathInstance; + public static Dictionary TypeToInstance = new Dictionary(); public static void Init() { @@ -70,7 +74,17 @@ namespace QSB.Patches new LauncherPatches() }; - HarmonyInstance = QSBCore.Helper.HarmonyHelper.GetValue("_harmony"); + ClientInstance = new Harmony("QSB.Client"); + ServerInstance = new Harmony("QSB.Server"); + NonServerInstance = new Harmony("QSB.NonServer"); + DeathInstance = new Harmony("QSB.Death"); + TypeToInstance = new Dictionary + { + { QSBPatchTypes.OnClientConnect, ClientInstance }, + { QSBPatchTypes.OnServerClientConnect, ServerInstance }, + { QSBPatchTypes.OnNonServerClientConnect, NonServerInstance }, + { QSBPatchTypes.RespawnTime, DeathInstance } + }; DebugLog.DebugWrite("Patch Manager ready.", MessageType.Success); } @@ -84,7 +98,7 @@ namespace QSB.Patches DebugLog.DebugWrite($" - Patching in {patch.GetType().Name}", MessageType.Info); try { - patch.DoPatches(); + patch.DoPatches(TypeToInstance[type]); } catch (Exception ex) { @@ -97,11 +111,7 @@ namespace QSB.Patches { OnUnpatchType?.SafeInvoke(type); DebugLog.DebugWrite($"Unpatch block {Enum.GetName(typeof(QSBPatchTypes), type)}", MessageType.Info); - foreach (var patch in _patchList.Where(x => x.Type == type)) - { - DebugLog.DebugWrite($" - Unpatching in {patch.GetType().Name}", MessageType.Info); - patch.DoUnpatches(); - } + TypeToInstance[type].UnpatchSelf(); } } } \ No newline at end of file From 56e6792ed4073922815b43998c035cc3a9648f73 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 16 Oct 2021 14:15:25 +0100 Subject: [PATCH 114/118] cleanup --- QSB/Patches/QSBPatchManager.cs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/QSB/Patches/QSBPatchManager.cs b/QSB/Patches/QSBPatchManager.cs index e89e7f9f..31cabaf4 100644 --- a/QSB/Patches/QSBPatchManager.cs +++ b/QSB/Patches/QSBPatchManager.cs @@ -36,10 +36,6 @@ namespace QSB.Patches private static List _patchList = new List(); - public static Harmony ClientInstance; - public static Harmony ServerInstance; - public static Harmony NonServerInstance; - public static Harmony DeathInstance; public static Dictionary TypeToInstance = new Dictionary(); public static void Init() @@ -74,16 +70,12 @@ namespace QSB.Patches new LauncherPatches() }; - ClientInstance = new Harmony("QSB.Client"); - ServerInstance = new Harmony("QSB.Server"); - NonServerInstance = new Harmony("QSB.NonServer"); - DeathInstance = new Harmony("QSB.Death"); TypeToInstance = new Dictionary { - { QSBPatchTypes.OnClientConnect, ClientInstance }, - { QSBPatchTypes.OnServerClientConnect, ServerInstance }, - { QSBPatchTypes.OnNonServerClientConnect, NonServerInstance }, - { QSBPatchTypes.RespawnTime, DeathInstance } + { QSBPatchTypes.OnClientConnect, new Harmony("QSB.Client") }, + { QSBPatchTypes.OnServerClientConnect, new Harmony("QSB.Server") }, + { QSBPatchTypes.OnNonServerClientConnect, new Harmony("QSB.NonServer") }, + { QSBPatchTypes.RespawnTime, new Harmony("QSB.Death") } }; DebugLog.DebugWrite("Patch Manager ready.", MessageType.Success); From 576c14b6c646f3e670cc9989c6ff763eefec1fd1 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 16 Oct 2021 16:57:01 +0100 Subject: [PATCH 115/118] add back in menu support --- QSB/Menus/IMenuAPI.cs | 7 - QSB/QSBCore.cs | 5 +- QSB/SectorSync/QSBSectorManager.cs | 3 +- QSB/manifest.json | 3 +- QuantumUNET/Components/QNetworkManagerHUD.cs | 128 ------------------- QuantumUNET/QuantumUNET.csproj | 1 - 6 files changed, 6 insertions(+), 141 deletions(-) delete mode 100644 QuantumUNET/Components/QNetworkManagerHUD.cs diff --git a/QSB/Menus/IMenuAPI.cs b/QSB/Menus/IMenuAPI.cs index e4682dd2..c30510f0 100644 --- a/QSB/Menus/IMenuAPI.cs +++ b/QSB/Menus/IMenuAPI.cs @@ -14,13 +14,6 @@ namespace QSB.Menus GameObject PauseMenu_MakeSceneLoadButton(string name, SubmitActionLoadScene.LoadableScenes sceneToLoad, PopupMenu confirmPopup = null, Menu customMenu = null); Button PauseMenu_MakeSimpleButton(string name, Menu customMenu = null); Menu PauseMenu_MakePauseListMenu(string title); - // Options - Menu OptionsMenu_MakeNonScrollingOptionsTab(string name); - GameObject OptionsMenu_MakeTwoButtonToggle(string label, string trueText, string falseText, string tooltipText, bool savedValue, Menu menuTab); - GameObject OptionsMenu_MakeNonDisplaySliderElement(string label, string tooltipText, float savedValue, Menu menuTab); - void OptionsMenu_MakeSpacer(float minHeight, Menu menuTab); - void OptionsMenu_MakeLabel(string label, Menu menuTab); - void OptionsMenu_MakeTextInput(string label, string tooltipText, string placeholderText, string savedValue, Menu menuTab); // Misc PopupMenu MakeTwoChoicePopup(string message, string confirmText, string cancelText); PopupInputMenu MakeInputFieldPopup(string message, string placeholderMessage, string confirmText, string cancelText); diff --git a/QSB/QSBCore.cs b/QSB/QSBCore.cs index 91dc9ee3..55c2fd1f 100644 --- a/QSB/QSBCore.cs +++ b/QSB/QSBCore.cs @@ -78,7 +78,7 @@ namespace QSB Helper = ModHelper; DebugLog.ToConsole($"* Start of QSB version {QSBVersion} - authored by {Helper.Manifest.Author}", MessageType.Info); - //MenuApi = ModHelper.Interaction.GetModApi("_nebula.MenuFramework"); + MenuApi = ModHelper.Interaction.GetModApi("_nebula.MenuFramework"); NetworkAssetBundle = Helper.Assets.LoadBundle("assets/network"); InstrumentAssetBundle = Helper.Assets.LoadBundle("assets/instruments"); @@ -94,9 +94,8 @@ namespace QSB gameObject.AddComponent(); gameObject.AddComponent(); gameObject.AddComponent(); - //gameObject.AddComponent(); + gameObject.AddComponent(); gameObject.AddComponent(); - gameObject.AddComponent(); // WorldObject managers gameObject.AddComponent(); diff --git a/QSB/SectorSync/QSBSectorManager.cs b/QSB/SectorSync/QSBSectorManager.cs index 3bbc053a..9dedaa14 100644 --- a/QSB/SectorSync/QSBSectorManager.cs +++ b/QSB/SectorSync/QSBSectorManager.cs @@ -36,7 +36,8 @@ namespace QSB.SectorSync if (sync.HasAuthority && sync.AttachedObject.gameObject.activeInHierarchy - && sync.IsReady) + && sync.IsReady + && sync.SectorSync.IsReady) { CheckTransformSyncSector(sync); } diff --git a/QSB/manifest.json b/QSB/manifest.json index 3779d4d0..32918373 100644 --- a/QSB/manifest.json +++ b/QSB/manifest.json @@ -9,5 +9,6 @@ }, "uniqueName": "Raicuparta.QuantumSpaceBuddies", "version": "0.12.0-pr4", - "owmlVersion": "2.0.0" + "owmlVersion": "2.0.0", + "dependencies": [ "_nebula.MenuFramework" ] } \ No newline at end of file diff --git a/QuantumUNET/Components/QNetworkManagerHUD.cs b/QuantumUNET/Components/QNetworkManagerHUD.cs deleted file mode 100644 index 2da86771..00000000 --- a/QuantumUNET/Components/QNetworkManagerHUD.cs +++ /dev/null @@ -1,128 +0,0 @@ -using System.ComponentModel; -using UnityEngine; - -namespace QuantumUNET.Components -{ - [EditorBrowsable(EditorBrowsableState.Never)] - public class QNetworkManagerHUD : MonoBehaviour - { - private void Awake() - { - this.manager = base.GetComponent(); - } - - private void OnGUI() - { - if (this.showGUI) - { - int num = 10 + this.offsetX; - int num2 = 40 + this.offsetY; - bool flag = this.manager.client == null || this.manager.client.connection == null || this.manager.client.connection.connectionId == -1; - if (!this.manager.IsClientConnected() && !QNetworkServer.active) - { - if (flag) - { - if (Application.platform != RuntimePlatform.WebGLPlayer) - { - if (GUI.Button(new Rect((float)num, (float)num2, 200f, 20f), "LAN Host(H)")) - { - this.manager.StartHost(); - } - num2 += 24; - } - if (GUI.Button(new Rect((float)num, (float)num2, 105f, 20f), "LAN Client(C)")) - { - this.manager.StartClient(); - } - this.manager.networkAddress = GUI.TextField(new Rect((float)(num + 100), (float)num2, 95f, 20f), this.manager.networkAddress); - num2 += 24; - if (Application.platform == RuntimePlatform.WebGLPlayer) - { - GUI.Box(new Rect((float)num, (float)num2, 200f, 25f), "( WebGL cannot be server )"); - num2 += 24; - } - else - { - if (GUI.Button(new Rect((float)num, (float)num2, 200f, 20f), "LAN Server Only(S)")) - { - this.manager.StartServer(); - } - num2 += 24; - } - } - else - { - GUI.Label(new Rect((float)num, (float)num2, 200f, 20f), string.Concat(new object[] - { - "Connecting to ", - this.manager.networkAddress, - ":", - this.manager.networkPort, - ".." - })); - num2 += 24; - if (GUI.Button(new Rect((float)num, (float)num2, 200f, 20f), "Cancel Connection Attempt")) - { - this.manager.StopClient(); - } - } - } - else - { - if (QNetworkServer.active) - { - string text = "Server: port=" + this.manager.networkPort; - GUI.Label(new Rect((float)num, (float)num2, 300f, 20f), text); - num2 += 24; - } - if (this.manager.IsClientConnected()) - { - GUI.Label(new Rect((float)num, (float)num2, 300f, 20f), string.Concat(new object[] - { - "Client: address=", - this.manager.networkAddress, - " port=", - this.manager.networkPort - })); - num2 += 24; - } - } - - if (this.manager.IsClientConnected() && !QClientScene.ready) - { - if (GUI.Button(new Rect((float)num, (float)num2, 200f, 20f), "Client Ready")) - { - QClientScene.Ready(this.manager.client.connection); - if (QClientScene.localPlayers.Count == 0) - { - QClientScene.AddPlayer(0); - } - } - num2 += 24; - } - - if (QNetworkServer.active || this.manager.IsClientConnected()) - { - if (GUI.Button(new Rect((float)num, (float)num2, 200f, 20f), "Stop (X)")) - { - this.manager.StopHost(); - } - num2 += 24; - } - } - } - - public QNetworkManager manager; - - [SerializeField] - public bool showGUI = true; - - [SerializeField] - public int offsetX; - - [SerializeField] - public int offsetY; - - private bool m_ShowServer; - } -} diff --git a/QuantumUNET/QuantumUNET.csproj b/QuantumUNET/QuantumUNET.csproj index 861b1034..74f0f29a 100644 --- a/QuantumUNET/QuantumUNET.csproj +++ b/QuantumUNET/QuantumUNET.csproj @@ -65,7 +65,6 @@ - From 7c4372dca7a92e30a68f1a33d230bc788b66fcf3 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 16 Oct 2021 23:19:49 +0100 Subject: [PATCH 116/118] remove patch logs --- QSB/Patches/QSBPatchManager.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/QSB/Patches/QSBPatchManager.cs b/QSB/Patches/QSBPatchManager.cs index 31cabaf4..508c31a7 100644 --- a/QSB/Patches/QSBPatchManager.cs +++ b/QSB/Patches/QSBPatchManager.cs @@ -84,10 +84,10 @@ namespace QSB.Patches public static void DoPatchType(QSBPatchTypes type) { OnPatchType?.SafeInvoke(type); - DebugLog.DebugWrite($"Patch block {Enum.GetName(typeof(QSBPatchTypes), type)}", MessageType.Info); + //DebugLog.DebugWrite($"Patch block {Enum.GetName(typeof(QSBPatchTypes), type)}", MessageType.Info); foreach (var patch in _patchList.Where(x => x.Type == type)) { - DebugLog.DebugWrite($" - Patching in {patch.GetType().Name}", MessageType.Info); + //DebugLog.DebugWrite($" - Patching in {patch.GetType().Name}", MessageType.Info); try { patch.DoPatches(TypeToInstance[type]); @@ -102,7 +102,7 @@ namespace QSB.Patches public static void DoUnpatchType(QSBPatchTypes type) { OnUnpatchType?.SafeInvoke(type); - DebugLog.DebugWrite($"Unpatch block {Enum.GetName(typeof(QSBPatchTypes), type)}", MessageType.Info); + //DebugLog.DebugWrite($"Unpatch block {Enum.GetName(typeof(QSBPatchTypes), type)}", MessageType.Info); TypeToInstance[type].UnpatchSelf(); } } From 50efe668d3ee603c5722f851f70515df0e9ea3eb Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Sat, 16 Oct 2021 23:35:45 +0100 Subject: [PATCH 117/118] added post-init event for world objects, fixes quantum state issue --- QSB/QuantumSync/Patches/QuantumPatches.cs | 12 +++++++--- .../QSBMultiStateQuantumObject.cs | 20 ++++++++++++----- .../WorldObjects/QSBSocketedQuantumObject.cs | 2 +- QSB/WorldSync/IWorldObject.cs | 1 + QSB/WorldSync/QSBWorldSync.cs | 22 +++++++++++++++---- QSB/WorldSync/WorldObject.cs | 1 + QSB/WorldSync/WorldObjectManager.cs | 12 +++++++++- 7 files changed, 56 insertions(+), 14 deletions(-) diff --git a/QSB/QuantumSync/Patches/QuantumPatches.cs b/QSB/QuantumSync/Patches/QuantumPatches.cs index ce0aaca5..aef42d5e 100644 --- a/QSB/QuantumSync/Patches/QuantumPatches.cs +++ b/QSB/QuantumSync/Patches/QuantumPatches.cs @@ -246,12 +246,12 @@ namespace QSB.QuantumSync.Patches if (____sector == null) { - __instance.GetType().GetMethod("CheckEnabled", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(__instance, null); + __instance.CheckEnabled(); } if (____collapseOnStart) { - __instance.GetType().GetMethod("Collapse", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(__instance, new object[] { true }); + __instance.Collapse(true); } return false; @@ -292,7 +292,13 @@ namespace QSB.QuantumSync.Patches var allMultiStates = QSBWorldSync.GetWorldObjects(); var stateObject = QSBWorldSync.GetWorldFromUnity(__instance); - var owner = allMultiStates.First(x => x.QuantumStates.Contains(stateObject)); + var owner = allMultiStates.FirstOrDefault(x => x.QuantumStates.Contains(stateObject)); + if (owner == default) + { + DebugLog.DebugWrite($"Error - Could not find QSBMultiStateQuantumObject for state {__instance.name}", MessageType.Error); + return; + } + if (owner.ControllingPlayer != QSBPlayerManager.LocalPlayerId) { return; diff --git a/QSB/QuantumSync/WorldObjects/QSBMultiStateQuantumObject.cs b/QSB/QuantumSync/WorldObjects/QSBMultiStateQuantumObject.cs index ce6ec74c..021fa8ea 100644 --- a/QSB/QuantumSync/WorldObjects/QSBMultiStateQuantumObject.cs +++ b/QSB/QuantumSync/WorldObjects/QSBMultiStateQuantumObject.cs @@ -11,7 +11,7 @@ namespace QSB.QuantumSync.WorldObjects { public List QuantumStates { get; private set; } public Text DebugBoxText; - public int CurrentState => AttachedObject.GetValue("_stateIndex"); + public int CurrentState => AttachedObject._stateIndex; public override void OnRemoval() { @@ -26,15 +26,25 @@ namespace QSB.QuantumSync.WorldObjects { ObjectId = id; AttachedObject = attachedObject; - QuantumStates = AttachedObject.GetValue("_states").ToList().Select(x => QSBWorldSync.GetWorldFromUnity(x)).ToList(); + if (QSBCore.DebugMode) { - DebugBoxText = DebugBoxManager.CreateBox(AttachedObject.transform, 0, CurrentState.ToString()).GetComponent(); + DebugBoxText = DebugBoxManager.CreateBox(AttachedObject.transform, 0, $"Multistate\r\nid:{id}\r\nstate:{CurrentState}").GetComponent(); } base.Init(attachedObject, id); } + public override void PostInit() + { + QuantumStates = AttachedObject._states.ToList().Select(x => QSBWorldSync.GetWorldFromUnity(x)).ToList(); + + if (QuantumStates.Any(x => x == null)) + { + DebugLog.ToConsole($"Error - {AttachedObject.name} has one or more null QSBQuantumStates assigned!", OWML.Common.MessageType.Error); + } + } + public void ChangeState(int newStateIndex) { if (CurrentState != -1) @@ -43,10 +53,10 @@ namespace QSB.QuantumSync.WorldObjects } QuantumStates[newStateIndex].SetVisible(true); - AttachedObject.SetValue("_stateIndex", newStateIndex); + AttachedObject._stateIndex = newStateIndex; if (QSBCore.DebugMode) { - DebugBoxText.text = newStateIndex.ToString(); + DebugBoxText.text = $"Multistate\r\nid:{ObjectId}\r\nstate:{CurrentState}"; } } } diff --git a/QSB/QuantumSync/WorldObjects/QSBSocketedQuantumObject.cs b/QSB/QuantumSync/WorldObjects/QSBSocketedQuantumObject.cs index 57d18d4c..fe381047 100644 --- a/QSB/QuantumSync/WorldObjects/QSBSocketedQuantumObject.cs +++ b/QSB/QuantumSync/WorldObjects/QSBSocketedQuantumObject.cs @@ -20,7 +20,7 @@ namespace QSB.QuantumSync.WorldObjects base.Init(quantumObject, id); if (QSBCore.DebugMode) { - DebugBoxText = DebugBoxManager.CreateBox(AttachedObject.transform, 0, ObjectId.ToString()).GetComponent(); + DebugBoxText = DebugBoxManager.CreateBox(AttachedObject.transform, 0, $"Socketed\r\nid:{ObjectId}").GetComponent(); } } diff --git a/QSB/WorldSync/IWorldObject.cs b/QSB/WorldSync/IWorldObject.cs index ba993075..37ce7d53 100644 --- a/QSB/WorldSync/IWorldObject.cs +++ b/QSB/WorldSync/IWorldObject.cs @@ -7,6 +7,7 @@ namespace QSB.WorldSync int ObjectId { get; } string Name { get; } + void PostInit(); void OnRemoval(); MonoBehaviour ReturnObject(); } diff --git a/QSB/WorldSync/QSBWorldSync.cs b/QSB/WorldSync/QSBWorldSync.cs index e26580be..a61f9fa5 100644 --- a/QSB/WorldSync/QSBWorldSync.cs +++ b/QSB/WorldSync/QSBWorldSync.cs @@ -45,23 +45,31 @@ namespace QSB.WorldSync if (unityObject == null) { - DebugLog.ToConsole($"Error - Trying to run GetWorldFromUnity with a null unity object! TWorldObject:{typeof(TWorldObject).Name}, TUnityObject:{typeof(TUnityObject).Name}.", MessageType.Error); + DebugLog.ToConsole($"Error - Trying to run GetWorldFromUnity with a null unity object! TWorldObject:{typeof(TWorldObject).Name}, TUnityObject:{typeof(TUnityObject).Name}", MessageType.Error); return default; } if (!QSBCore.IsInMultiplayer) { - DebugLog.ToConsole($"Warning - Trying to run GetWorldFromUnity while not in multiplayer!"); + DebugLog.ToConsole($"Warning - Trying to run GetWorldFromUnity while not in multiplayer! TWorldObject:{typeof(TWorldObject).Name}, TUnityObject:{typeof(TUnityObject).Name}", MessageType.Warning); return default; } if (!WorldObjectsToUnityObjects.ContainsKey(unityObject)) { - DebugLog.ToConsole($"Error - WorldObjectsToUnityObjects does not contain \"{unityObject.name}\"! Called from {new StackTrace().GetFrame(1).GetMethod().Name}", MessageType.Error); + DebugLog.ToConsole($"Error - WorldObjectsToUnityObjects does not contain \"{unityObject.name}\"! TWorldObject:{typeof(TWorldObject).Name}, TUnityObject:{typeof(TUnityObject).Name}", MessageType.Error); return default; } - return WorldObjectsToUnityObjects[unityObject] as TWorldObject; + var returnObject = WorldObjectsToUnityObjects[unityObject] as TWorldObject; + + if (returnObject == default || returnObject == null) + { + DebugLog.ToConsole($"Error - World object for unity object {unityObject.name} is null! TWorldObject:{typeof(TWorldObject).Name}, TUnityObject:{typeof(TUnityObject).Name}", MessageType.Error); + return default; + } + + return returnObject; } public static int GetIdFromUnity(TUnityObject unityObject) @@ -121,6 +129,12 @@ namespace QSB.WorldSync { var worldObject = (TWorldObject)Activator.CreateInstance(typeof(TWorldObject)); WorldObjects.Add(worldObject); + if (worldObject == null) + { + // if this happens, god help you + DebugLog.ToConsole($"Error - CreateWorldObject is returning a null value! This is very bad!", MessageType.Error); + } + return worldObject; } diff --git a/QSB/WorldSync/WorldObject.cs b/QSB/WorldSync/WorldObject.cs index caaf7173..8c765d12 100644 --- a/QSB/WorldSync/WorldObject.cs +++ b/QSB/WorldSync/WorldObject.cs @@ -10,6 +10,7 @@ namespace QSB.WorldSync public string Name => AttachedObject == null ? "" : AttachedObject.name; public abstract void Init(T attachedObject, int id); + public virtual void PostInit() { } public virtual void OnRemoval() { } public MonoBehaviour ReturnObject() => AttachedObject; } diff --git a/QSB/WorldSync/WorldObjectManager.cs b/QSB/WorldSync/WorldObjectManager.cs index 7d2ac4a4..f5fbfb36 100644 --- a/QSB/WorldSync/WorldObjectManager.cs +++ b/QSB/WorldSync/WorldObjectManager.cs @@ -67,9 +67,19 @@ namespace QSB.WorldSync } } - QSBCore.UnityEvents.FireInNUpdates(() => AllReady = true, 1); + QSBCore.UnityEvents.FireInNUpdates(DoPostInit, 1); } + private static void DoPostInit() + { + AllReady = true; + var allWorldObjects = QSBWorldSync.GetWorldObjects(); + foreach (var worldObject in allWorldObjects) + { + worldObject.PostInit(); + } + } + protected abstract void RebuildWorldObjects(OWScene scene); } } From 2d8a81bd4c37a9d3301042457feff070aa44ba16 Mon Sep 17 00:00:00 2001 From: Mister_Nebula <41904486+misternebula@users.noreply.github.com> Date: Mon, 18 Oct 2021 09:02:48 +0100 Subject: [PATCH 118/118] add quantum visualisation debug objects --- AssetBundles/debug | Bin 2876 -> 9046 bytes AssetBundles/debug.manifest | 18 +++- AssetBundles/network | Bin 3001 -> 2821 bytes AssetBundles/network.manifest | 8 +- QSB/QSB.csproj | 4 +- QSB/QSBCore.cs | 2 + .../WorldObjects/QSBQuantumObject.cs | 46 ++++++++++ UnityProject/Assets/Capsule.prefab | 81 ++++++++++++++++++ UnityProject/Assets/Capsule.prefab.meta | 7 ++ UnityProject/Assets/Cube.prefab | 81 ++++++++++++++++++ UnityProject/Assets/Cube.prefab.meta | 7 ++ UnityProject/Assets/DebugVis.mat | 77 +++++++++++++++++ UnityProject/Assets/DebugVis.mat.meta | 8 ++ UnityProject/Assets/Sphere.prefab | 81 ++++++++++++++++++ UnityProject/Assets/Sphere.prefab.meta | 7 ++ UnityProject/Assets/UnlitColored.shader | 22 +++++ UnityProject/Assets/UnlitColored.shader.meta | 9 ++ 17 files changed, 448 insertions(+), 10 deletions(-) create mode 100644 UnityProject/Assets/Capsule.prefab create mode 100644 UnityProject/Assets/Capsule.prefab.meta create mode 100644 UnityProject/Assets/Cube.prefab create mode 100644 UnityProject/Assets/Cube.prefab.meta create mode 100644 UnityProject/Assets/DebugVis.mat create mode 100644 UnityProject/Assets/DebugVis.mat.meta create mode 100644 UnityProject/Assets/Sphere.prefab create mode 100644 UnityProject/Assets/Sphere.prefab.meta create mode 100644 UnityProject/Assets/UnlitColored.shader create mode 100644 UnityProject/Assets/UnlitColored.shader.meta diff --git a/AssetBundles/debug b/AssetBundles/debug index 8931c62999ed36f0bbc184c8cac9a5f0fca7b23d..98a62156407c6e1b693a734d66bc8a51f4092ad4 100644 GIT binary patch delta 9013 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 diff --git a/AssetBundles/debug.manifest b/AssetBundles/debug.manifest index 454a65a2..58b6eecd 100644 --- a/AssetBundles/debug.manifest +++ b/AssetBundles/debug.manifest @@ -1,20 +1,28 @@ ManifestFileVersion: 0 -CRC: 48093825 +CRC: 3009665417 Hashes: AssetFileHash: serializedVersion: 2 - Hash: ecc8021e5da67c4a34234d989b8e6e82 + Hash: dbc913ca95e649d2e00d188ff573830e TypeTreeHash: serializedVersion: 2 - Hash: 62b30f9a48f8b9d21fede49c56800beb + Hash: b2ece8ae09df261ff59d764d08696641 HashAppended: 0 ClassTypes: - Class: 1 Script: {instanceID: 0} +- Class: 4 + Script: {instanceID: 0} - Class: 21 Script: {instanceID: 0} +- Class: 23 + Script: {instanceID: 0} - Class: 28 Script: {instanceID: 0} +- Class: 33 + Script: {instanceID: 0} +- Class: 43 + Script: {instanceID: 0} - Class: 48 Script: {instanceID: 0} - Class: 114 @@ -33,5 +41,9 @@ ClassTypes: Script: {instanceID: 0} SerializeReferenceClassIdentifiers: [] Assets: +- Assets/Cube.prefab +- Assets/Capsule.prefab +- Assets/DebugVis.mat - Assets/LogCanvas.prefab +- Assets/Sphere.prefab Dependencies: [] diff --git a/AssetBundles/network b/AssetBundles/network index e0096d8b8655bb6cf7d8fe64d144d9aa778b723d..b5f3712fdefbb6e4626fa1493a58a9fde00a8878 100644 GIT binary patch delta 2727 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 delta 2908 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 G9%v%|bc{j( diff --git a/AssetBundles/network.manifest b/AssetBundles/network.manifest index 558463e8..2780f677 100644 --- a/AssetBundles/network.manifest +++ b/AssetBundles/network.manifest @@ -1,12 +1,12 @@ ManifestFileVersion: 0 -CRC: 4070870812 +CRC: 1991351671 Hashes: AssetFileHash: serializedVersion: 2 - Hash: 29833081f68126f69e50212247d21be7 + Hash: 494875ba5d9a67b7d87421e95e386357 TypeTreeHash: serializedVersion: 2 - Hash: 51df0ad50c79617520b68dc5fc2a08cf + Hash: 6968c5d2bbef57a79632abd61ea01bb3 HashAppended: 0 ClassTypes: - Class: 1 @@ -19,8 +19,6 @@ ClassTypes: Script: {fileID: -1768714887, guid: 93b08009869340045a8e7321508b6355, type: 3} - Class: 114 Script: {fileID: -1267208747, guid: 93b08009869340045a8e7321508b6355, type: 3} -- Class: 114 - Script: {fileID: 11500000, guid: f8c40b79b6cc65d4e904a1e931638b0f, type: 3} - Class: 115 Script: {instanceID: 0} SerializeReferenceClassIdentifiers: [] diff --git a/QSB/QSB.csproj b/QSB/QSB.csproj index 756cbd7c..494c074a 100644 --- a/QSB/QSB.csproj +++ b/QSB/QSB.csproj @@ -22,7 +22,7 @@ DEBUG;TRACE prompt 4 - true + true pdbonly @@ -340,7 +340,7 @@ - + $(GameDir)\OuterWilds_Data\Managed\publicized_assemblies\Assembly-CSharp_publicized.dll diff --git a/QSB/QSBCore.cs b/QSB/QSBCore.cs index 55c2fd1f..784c7f49 100644 --- a/QSB/QSBCore.cs +++ b/QSB/QSBCore.cs @@ -59,6 +59,7 @@ namespace QSB public static AssetBundle NetworkAssetBundle { get; private set; } public static AssetBundle InstrumentAssetBundle { get; private set; } public static AssetBundle ConversationAssetBundle { get; private set; } + public static AssetBundle DebugAssetBundle { get; private set; } public static bool WorldObjectsReady => WorldObjectManager.AllReady && IsInMultiplayer && PlayerTransformSync.LocalInstance != null; public static bool IsHost => QNetworkServer.active; public static bool IsInMultiplayer => QNetworkManager.singleton.isNetworkActive; @@ -83,6 +84,7 @@ namespace QSB NetworkAssetBundle = Helper.Assets.LoadBundle("assets/network"); InstrumentAssetBundle = Helper.Assets.LoadBundle("assets/instruments"); ConversationAssetBundle = Helper.Assets.LoadBundle("assets/conversation"); + DebugAssetBundle = Helper.Assets.LoadBundle("assets/debug"); QSBPatchManager.Init(); diff --git a/QSB/QuantumSync/WorldObjects/QSBQuantumObject.cs b/QSB/QuantumSync/WorldObjects/QSBQuantumObject.cs index 766bd389..c0f700c3 100644 --- a/QSB/QuantumSync/WorldObjects/QSBQuantumObject.cs +++ b/QSB/QuantumSync/WorldObjects/QSBQuantumObject.cs @@ -6,6 +6,7 @@ using QSB.Utility; using QSB.WorldSync; using System.Collections.Generic; using System.Linq; +using UnityEngine; namespace QSB.QuantumSync.WorldObjects { @@ -29,6 +30,26 @@ namespace QSB.QuantumSync.WorldObjects public override void Init(T attachedObject, int id) { + var debugBundle = QSBCore.DebugAssetBundle; + var sphere = debugBundle.LoadAsset("Assets/Sphere.prefab"); + var cube = debugBundle.LoadAsset("Assets/Cube.prefab"); + var capsule = debugBundle.LoadAsset("Assets/Capsule.prefab"); + + if (cube == null) + { + DebugLog.DebugWrite($"CUBE IS NULL"); + } + + if (sphere == null) + { + DebugLog.DebugWrite($"SPHERE IS NULL"); + } + + if (capsule == null) + { + DebugLog.DebugWrite($"CAPSULE IS NULL"); + } + foreach (var shape in GetAttachedShapes()) { if (shape == null) @@ -42,6 +63,31 @@ namespace QSB.QuantumSync.WorldObjects shape.OnShapeDeactivated += (Shape s) => QSBCore.UnityEvents.FireOnNextUpdate(() => OnDisable(s)); + + if (shape is BoxShape boxShape) + { + var newCube = UnityEngine.Object.Instantiate(cube); + newCube.transform.parent = shape.transform; + newCube.transform.localPosition = Vector3.zero; + newCube.transform.localRotation = Quaternion.Euler(0, 0, 0); + newCube.transform.localScale = boxShape.size; + } + else if (shape is SphereShape sphereShape) + { + var newSphere = UnityEngine.Object.Instantiate(sphere); + newSphere.transform.parent = shape.transform; + newSphere.transform.localPosition = Vector3.zero; + newSphere.transform.localRotation = Quaternion.Euler(0, 0, 0); + newSphere.transform.localScale = Vector3.one * (sphereShape.radius * 2); + } + else if (shape is CapsuleShape capsuleShape) + { + var newCapsule = Object.Instantiate(capsule); + newCapsule.transform.parent = shape.transform; + newCapsule.transform.localPosition = Vector3.zero; + newCapsule.transform.localRotation = Quaternion.Euler(0, 0, 0); + newCapsule.transform.localScale = new Vector3(capsuleShape.radius * 2, capsuleShape.height, capsuleShape.radius * 2); + } } if (GetAttachedShapes().Any(x => !x.enabled || !x.active)) diff --git a/UnityProject/Assets/Capsule.prefab b/UnityProject/Assets/Capsule.prefab new file mode 100644 index 00000000..4f6d7f89 --- /dev/null +++ b/UnityProject/Assets/Capsule.prefab @@ -0,0 +1,81 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &158443016395961829 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8824963426012170043} + - component: {fileID: 2763613722105978426} + - component: {fileID: 308561794084866108} + m_Layer: 0 + m_Name: Capsule + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8824963426012170043 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 158443016395961829} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &2763613722105978426 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 158443016395961829} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &308561794084866108 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 158443016395961829} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 3f581bc765fd2dd4e8e2a6f19d1c5a54, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 diff --git a/UnityProject/Assets/Capsule.prefab.meta b/UnityProject/Assets/Capsule.prefab.meta new file mode 100644 index 00000000..5b7cc6fc --- /dev/null +++ b/UnityProject/Assets/Capsule.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3c3ea1522189f70449edb225a0432847 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: debug + assetBundleVariant: diff --git a/UnityProject/Assets/Cube.prefab b/UnityProject/Assets/Cube.prefab new file mode 100644 index 00000000..035b5436 --- /dev/null +++ b/UnityProject/Assets/Cube.prefab @@ -0,0 +1,81 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &2878217370054538106 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3260343914971232886} + - component: {fileID: 7886282064430510966} + - component: {fileID: 5552009110269121987} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3260343914971232886 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2878217370054538106} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7886282064430510966 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2878217370054538106} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &5552009110269121987 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2878217370054538106} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 3f581bc765fd2dd4e8e2a6f19d1c5a54, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 diff --git a/UnityProject/Assets/Cube.prefab.meta b/UnityProject/Assets/Cube.prefab.meta new file mode 100644 index 00000000..02418c16 --- /dev/null +++ b/UnityProject/Assets/Cube.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 80b35e10573debc4bb73dd392a3147c4 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: debug + assetBundleVariant: diff --git a/UnityProject/Assets/DebugVis.mat b/UnityProject/Assets/DebugVis.mat new file mode 100644 index 00000000..21ccb8f7 --- /dev/null +++ b/UnityProject/Assets/DebugVis.mat @@ -0,0 +1,77 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: DebugVis + m_Shader: {fileID: 4800000, guid: 9ec76f18fccbe654a850002455808200, type: 3} + m_ShaderKeywords: _ALPHAPREMULTIPLY_ON + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.172 + - _DetailNormalMapScale: 1 + - _DstBlend: 10 + - _GlossMapScale: 1 + - _Glossiness: 0 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 3 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 0 + m_Colors: + - _Color: {r: 1, g: 0.51526886, b: 0, a: 0.09803922} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/UnityProject/Assets/DebugVis.mat.meta b/UnityProject/Assets/DebugVis.mat.meta new file mode 100644 index 00000000..c606ccf6 --- /dev/null +++ b/UnityProject/Assets/DebugVis.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3f581bc765fd2dd4e8e2a6f19d1c5a54 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: debug + assetBundleVariant: diff --git a/UnityProject/Assets/Sphere.prefab b/UnityProject/Assets/Sphere.prefab new file mode 100644 index 00000000..631f758b --- /dev/null +++ b/UnityProject/Assets/Sphere.prefab @@ -0,0 +1,81 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &6988891241326536966 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1528378738054518666} + - component: {fileID: 6895832966541343269} + - component: {fileID: 1995909747615478316} + m_Layer: 0 + m_Name: Sphere + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1528378738054518666 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6988891241326536966} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &6895832966541343269 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6988891241326536966} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &1995909747615478316 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6988891241326536966} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 3f581bc765fd2dd4e8e2a6f19d1c5a54, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 diff --git a/UnityProject/Assets/Sphere.prefab.meta b/UnityProject/Assets/Sphere.prefab.meta new file mode 100644 index 00000000..17c16850 --- /dev/null +++ b/UnityProject/Assets/Sphere.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c23e96dc4c16238499a5a34dae13c90c +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: debug + assetBundleVariant: diff --git a/UnityProject/Assets/UnlitColored.shader b/UnityProject/Assets/UnlitColored.shader new file mode 100644 index 00000000..bee32967 --- /dev/null +++ b/UnityProject/Assets/UnlitColored.shader @@ -0,0 +1,22 @@ +Shader "Unlit/Transparent Colored" { + Properties { + _Color ("Main Color", Color) = (1,1,1,1) + _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {} + } + + SubShader { + Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"} + + ZWrite Off + Lighting Off + Cull Off + Fog { Mode Off } + + Blend SrcAlpha OneMinusSrcAlpha + + Pass { + Color [_Color] + SetTexture [_MainTex] { combine texture * primary } + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/UnlitColored.shader.meta b/UnityProject/Assets/UnlitColored.shader.meta new file mode 100644 index 00000000..46ed92e3 --- /dev/null +++ b/UnityProject/Assets/UnlitColored.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 9ec76f18fccbe654a850002455808200 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: