From 95ed3da300ad458b1fac47c7958e453dc4ba355e Mon Sep 17 00:00:00 2001 From: Jannify <23176718+Jannify@users.noreply.github.com> Date: Thu, 28 Sep 2023 15:57:22 +0200 Subject: [PATCH 01/14] Handle uncatched MainMenu errors from Sn Update --- ...LoadButton_RestoreParentsSettings_Patch.cs | 19 ++++++++++++++++ .../SystemsSpawner_SetupSingleton_Patch.cs | 22 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 NitroxPatcher/Patches/Persistent/MainMenuLoadButton_RestoreParentsSettings_Patch.cs create mode 100644 NitroxPatcher/Patches/Persistent/SystemsSpawner_SetupSingleton_Patch.cs diff --git a/NitroxPatcher/Patches/Persistent/MainMenuLoadButton_RestoreParentsSettings_Patch.cs b/NitroxPatcher/Patches/Persistent/MainMenuLoadButton_RestoreParentsSettings_Patch.cs new file mode 100644 index 0000000000..97afc16037 --- /dev/null +++ b/NitroxPatcher/Patches/Persistent/MainMenuLoadButton_RestoreParentsSettings_Patch.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using NitroxModel.Helper; +using UnityEngine.UI; + +namespace NitroxPatcher.Patches.Persistent; + +/// +/// MainMenuLoadButton.RestoreParentsSettings() is throwing null refs because we copy it for our MainMenu and then destroy it. +/// Unfortunately OnDestroy can't be prevented when destroying MBs so we fix the NRE here. +/// +public sealed partial class MainMenuLoadButton_RestoreParentsSettings_Patch : NitroxPatch, IPersistentPatch +{ + private static readonly MethodInfo TARGET_METHOD = Reflect.Method((MainMenuLoadButton lb) => lb.RestoreParentsSettings()); + + public static bool Prefix(GridLayoutGroup ___gridLayoutGroup, ScrollRect ___scrollRect) + { + return ___gridLayoutGroup && ___scrollRect; + } +} diff --git a/NitroxPatcher/Patches/Persistent/SystemsSpawner_SetupSingleton_Patch.cs b/NitroxPatcher/Patches/Persistent/SystemsSpawner_SetupSingleton_Patch.cs new file mode 100644 index 0000000000..dc48a687b0 --- /dev/null +++ b/NitroxPatcher/Patches/Persistent/SystemsSpawner_SetupSingleton_Patch.cs @@ -0,0 +1,22 @@ +using System; +using System.Reflection; +using HarmonyLib; +using NitroxModel.Helper; + +namespace NitroxPatcher.Patches.Persistent; + +/// +/// Patch to suppress SentrySdk NRE as it's destroyed by us +/// +public sealed partial class SystemsSpawner_SetupSingleton_Patch : NitroxPatch, IPersistentPatch +{ + private static readonly MethodInfo TARGET_METHOD = AccessTools.EnumeratorMoveNext(Reflect.Method((SystemsSpawner s) => s.SetupSingleton(default))); + + public static void Finalizer(ref Exception __exception) + { + if (__exception is NullReferenceException) + { + __exception = null; + } + } +} From 5c30d7145fb727d0e2915c567c93935d94281f7a Mon Sep 17 00:00:00 2001 From: Jannify <23176718+Jannify@users.noreply.github.com> Date: Sat, 24 Jun 2023 17:42:00 +0200 Subject: [PATCH 02/14] Add controller support for server list --- .../MonoBehaviours/Discord/DiscordClient.cs | 3 +- .../Gui/MainMenu/MainMenuAddServerWindow.cs | 158 ++++++ .../Gui/MainMenu/MainMenuMods.cs | 1 + .../Gui/MainMenu/MainMenuMultiplayerPanel.cs | 494 ++++++++---------- .../Gui/MainMenu/MainMenuServerButton.cs | 136 +++++ 5 files changed, 505 insertions(+), 287 deletions(-) create mode 100644 NitroxClient/MonoBehaviours/Gui/MainMenu/MainMenuAddServerWindow.cs create mode 100644 NitroxClient/MonoBehaviours/Gui/MainMenu/MainMenuServerButton.cs diff --git a/NitroxClient/MonoBehaviours/Discord/DiscordClient.cs b/NitroxClient/MonoBehaviours/Discord/DiscordClient.cs index 8fb6a0dd1b..ea935fff2b 100644 --- a/NitroxClient/MonoBehaviours/Discord/DiscordClient.cs +++ b/NitroxClient/MonoBehaviours/Discord/DiscordClient.cs @@ -109,7 +109,8 @@ private void ActivityJoin(string secret) string[] splitSecret = secret.Split(':'); string ip = string.Join(":", splitSecret.Take(splitSecret.Length - 1)); string port = splitSecret.Last(); - _ = MainMenuMultiplayerPanel.OpenJoinServerMenuAsync(ip, port); + int portInt = int.Parse(port); + _ = MainMenuServerButton.OpenJoinServerMenuAsync(ip, portInt); } private void ActivityJoinRequest(ref User user) diff --git a/NitroxClient/MonoBehaviours/Gui/MainMenu/MainMenuAddServerWindow.cs b/NitroxClient/MonoBehaviours/Gui/MainMenu/MainMenuAddServerWindow.cs new file mode 100644 index 0000000000..f156428aa3 --- /dev/null +++ b/NitroxClient/MonoBehaviours/Gui/MainMenu/MainMenuAddServerWindow.cs @@ -0,0 +1,158 @@ +using System.Collections; +using NitroxClient.Unity.Helper; +using NitroxModel.Serialization; +using UnityEngine; +using UWE; + +namespace NitroxClient.MonoBehaviours.Gui.MainMenu; + +public static class MainMenuAddServerWindow +{ + private static Rect addServerWindowRect = new(Screen.width / 2 - 250, 200, 500, 200); + + private static string serverHostInput; + private static string serverNameInput; + private static string serverPortInput; + + private static bool shouldFocus; + private static bool showingAddServer; + + public static void OnExternalGUI() + { + if (showingAddServer) + { + addServerWindowRect = GUILayout.Window(GUIUtility.GetControlID(FocusType.Keyboard), addServerWindowRect, DoAddServerWindow, Language.main.Get("Nitrox_AddServer")); + } + } + + public static void ShowAddServerWindow() + { + serverNameInput = "local"; + serverHostInput = "127.0.0.1"; + serverPortInput = ServerList.DEFAULT_PORT.ToString(); + showingAddServer = true; + shouldFocus = true; + uGUI_MainMenu.main.canvasGroup.interactable = false; + } + + public static void HideAddServerWindow() + { + IEnumerator SetWindowComponents() + { + showingAddServer = false; + shouldFocus = true; + yield return null; + uGUI_MainMenu.main.canvasGroup.interactable = true; + } + + CoroutineHost.StartCoroutine(SetWindowComponents()); + } + + private static void OnAddServerButtonClicked() + { + serverNameInput = serverNameInput.Trim(); + serverHostInput = serverHostInput.Trim(); + if (int.TryParse(serverPortInput.Trim(), out int serverPort)) + { + MainMenuMultiplayerPanel.Main.CreateServerButton(serverNameInput, serverHostInput, serverPort); + ServerList.Instance.Add(new ServerList.Entry(serverNameInput, serverHostInput, serverPort)); + ServerList.Instance.Save(); + + HideAddServerWindow(); + } + else + { + Log.InGame("Server port was not a valid number!"); + } + } + + private static void OnCancelButtonClicked() + { + HideAddServerWindow(); + } + + private static void DoAddServerWindow(int windowId) + { + Event e = Event.current; + if (e.isKey) + { + switch (e.keyCode) + { + case KeyCode.Return: + OnAddServerButtonClicked(); + break; + case KeyCode.Escape: + OnCancelButtonClicked(); + break; + } + } + + GUISkinUtils.RenderWithSkin(GetGUISkin(), + () => + { + using (new GUILayout.VerticalScope("Box")) + { + using (new GUILayout.HorizontalScope()) + { + GUILayout.Label(Language.main.Get("Nitrox_AddServerName")); + GUI.SetNextControlName("serverNameField"); + // 120 so users can't go too crazy. + serverNameInput = GUILayout.TextField(serverNameInput, 120); + } + + using (new GUILayout.HorizontalScope()) + { + GUILayout.Label(Language.main.Get("Nitrox_AddServerHost")); + GUI.SetNextControlName("serverHostField"); + // 120 so users can't go too crazy. + serverHostInput = GUILayout.TextField(serverHostInput, 120); + } + + using (new GUILayout.HorizontalScope()) + { + GUILayout.Label(Language.main.Get("Nitrox_AddServerPort")); + GUI.SetNextControlName("serverPortField"); + serverPortInput = GUILayout.TextField(serverPortInput); + } + + if (GUILayout.Button(Language.main.Get("Nitrox_AddServerAdd"))) + { + OnAddServerButtonClicked(); + } + + if (GUILayout.Button(Language.main.Get("Nitrox_Cancel"))) + { + OnCancelButtonClicked(); + } + } + }); + + if (shouldFocus) + { + GUI.FocusControl("serverNameField"); + shouldFocus = false; + } + } + + private static GUISkin GetGUISkin() + { + return GUISkinUtils.RegisterDerivedOnce("menus.server", + s => + { + s.textField.fontSize = 14; + s.textField.richText = false; + s.textField.alignment = TextAnchor.MiddleLeft; + s.textField.wordWrap = true; + s.textField.stretchHeight = true; + s.textField.padding = new RectOffset(10, 10, 5, 5); + + s.label.fontSize = 14; + s.label.alignment = TextAnchor.MiddleRight; + s.label.stretchHeight = true; + s.label.fixedWidth = 80; //change this when adding new labels that need more space. + + s.button.fontSize = 14; + s.button.stretchHeight = true; + }); + } +} diff --git a/NitroxClient/MonoBehaviours/Gui/MainMenu/MainMenuMods.cs b/NitroxClient/MonoBehaviours/Gui/MainMenu/MainMenuMods.cs index 9743a976b2..c3532d3925 100644 --- a/NitroxClient/MonoBehaviours/Gui/MainMenu/MainMenuMods.cs +++ b/NitroxClient/MonoBehaviours/Gui/MainMenu/MainMenuMods.cs @@ -55,6 +55,7 @@ private void MultiplayerMenuMods() header.GetComponent().translationKey = "Nitrox_Multiplayer"; Destroy(loadedMultiplayer.RequireGameObject("Scroll View/Viewport/SavedGameAreaContent/NewGame")); Destroy(loadedMultiplayer.GetComponent()); + Destroy(loadedMultiplayer.GetComponentInChildren()); loadedMultiplayer.AddComponent().Setup(loadedMultiplayer, savedGamesRef); diff --git a/NitroxClient/MonoBehaviours/Gui/MainMenu/MainMenuMultiplayerPanel.cs b/NitroxClient/MonoBehaviours/Gui/MainMenu/MainMenuMultiplayerPanel.cs index 596457fa77..eaccaf0ff4 100644 --- a/NitroxClient/MonoBehaviours/Gui/MainMenu/MainMenuMultiplayerPanel.cs +++ b/NitroxClient/MonoBehaviours/Gui/MainMenu/MainMenuMultiplayerPanel.cs @@ -1,350 +1,272 @@ -using System; -using System.Collections; using System.Net; -using System.Net.Sockets; +using FMODUnity; using NitroxClient.Communication; using NitroxClient.GameLogic.Settings; using NitroxClient.Unity.Helper; using NitroxModel.Serialization; using TMPro; using UnityEngine; -using UnityEngine.Events; using UnityEngine.UI; -using UWE; -namespace NitroxClient.MonoBehaviours.Gui.MainMenu +namespace NitroxClient.MonoBehaviours.Gui.MainMenu; + +public class MainMenuMultiplayerPanel : MonoBehaviour, uGUI_INavigableIconGrid, uGUI_IButtonReceiver { - public class MainMenuMultiplayerPanel : MonoBehaviour + public static MainMenuMultiplayerPanel Main; + + private GameObject multiplayerButtonRef; + + private Sprite normalSprite; + private Sprite selectedSprite; + private FMODAsset hoverSound; + + private Transform serverAreaContent; + private GameObject selectedServerItem; + + public JoinServer JoinServer { get; private set; } + public bool IsJoining{ get; set; } + + + public void Setup(GameObject loadedMultiplayer, GameObject savedGamesRef) { - public static MainMenuMultiplayerPanel Main; - private Rect addServerWindowRect = new(Screen.width / 2 - 250, 200, 500, 200); - private GameObject loadedMultiplayerRef; - private GameObject savedGamesRef; - private GameObject deleteButtonRef; - private GameObject multiplayerButton; - private Transform savedGameAreaContent; - public JoinServer JoinServer { get; private set; } - - private string serverHostInput; - private string serverNameInput; - private string serverPortInput; - - private bool shouldFocus; - private bool showingAddServer; - private bool isJoining; - - public void Setup(GameObject loadedMultiplayer, GameObject savedGames) - { - Main = this; - loadedMultiplayerRef = loadedMultiplayer; - savedGamesRef = savedGames; - - //This sucks, but the only way around it is to establish a Subnautica resources cache and reference it everywhere we need it. - //Given recent push-back on elaborate designs, I've just crammed it here until we can all get on the same page as far as code-quality standards are concerned. - JoinServer = new GameObject("NitroxJoinServer").AddComponent(); - JoinServer.Setup(savedGamesRef); - - multiplayerButton = savedGamesRef.RequireGameObject("Scroll View/Viewport/SavedGameAreaContent/NewGame"); - savedGameAreaContent = loadedMultiplayerRef.RequireTransform("Scroll View/Viewport/SavedGameAreaContent"); - deleteButtonRef = savedGamesRef.GetComponent().saveInstance.GetComponent().deleteButton; - - CreateButton(translationKey: "Nitrox_AddServer", clickEvent: ShowAddServerWindow, disableTranslation: false); - LoadSavedServers(); - _ = FindLANServersAsync(); - } + Main = this; - private void CreateButton(string translationKey, UnityAction clickEvent, bool disableTranslation) - { - GameObject multiplayerButtonInst = Instantiate(multiplayerButton, savedGameAreaContent, false); - Transform txt = multiplayerButtonInst.RequireTransform("NewGameButton/Text"); - txt.GetComponent().text = translationKey; + //This sucks, but the only way around it is to establish a Subnautica resources cache and reference it everywhere we need it. + //Given recent push-back on elaborate designs, I've just crammed it here until we can all get on the same page as far as code-quality standards are concerned. + JoinServer = new GameObject("NitroxJoinServer").AddComponent(); + JoinServer.Setup(savedGamesRef); - if (disableTranslation) - { - Destroy(txt.GetComponent()); - } + MainMenuLoadMenu loadMenu = loadedMultiplayer.GetComponentInChildren(); + normalSprite = loadMenu.normalSprite; + selectedSprite = loadMenu.selectedSprite; + hoverSound = loadMenu.hoverSound; - Button multiplayerButtonButton = multiplayerButtonInst.RequireTransform("NewGameButton").GetComponent