diff --git a/.github/scripts/thunderstore_bundle.js b/.github/scripts/thunderstore_bundle.js index 3d6111cd3..839586b25 100644 --- a/.github/scripts/thunderstore_bundle.js +++ b/.github/scripts/thunderstore_bundle.js @@ -123,7 +123,7 @@ function generateManifest() { BEPINEX_DEPENDENCY, `nebula-${apiPluginInfo.name}-${apiPluginInfo.version}`, "PhantomGamers-IlLine-1.0.0", - "starfi5h-BulletTime-1.4.8", + "starfi5h-BulletTime-1.5.1", ], website_url: "https://github.com/hubastard/nebula" }; diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c826fc92..438cb25c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## Changelog +0.9.9: +- @starfi5h: Fix multiplayer tab in the option window for DSP v0.10.30.23430 +- @starfi5h: Separate error close button (x) and copy button +- @starfi5h: Sync WarningBroadcastData: LandingRelay, ApproachingSeed, BuildingDestroyed and 3 more + 0.9.8: - @AlienXAXS: Added Online Player UI (Backtick key by default) - @AlienXAXS: Updated Nebula to be compatible with Dyson Sphere Program v0.10.30.23292 diff --git a/NebulaPatcher/NebulaPlugin.cs b/NebulaPatcher/NebulaPlugin.cs index f5872d3a9..ac8debaed 100644 --- a/NebulaPatcher/NebulaPlugin.cs +++ b/NebulaPatcher/NebulaPlugin.cs @@ -13,6 +13,7 @@ using NebulaPatcher.Logger; using NebulaPatcher.MonoBehaviours; using NebulaPatcher.Patches.Dynamic; +using NebulaPatcher.Patches.Misc; using NebulaWorld; using NebulaWorld.GameStates; using NebulaWorld.SocialIntegration; @@ -156,7 +157,7 @@ private void Awake() } else if (newgameArgExists) { - Log.Error(">> New game parameters incorrect! Exiting...\nExpect: -newgame seed starCount resourceMltiplier"); + Log.Error(">> New game parameters incorrect! Exiting...\nExpect: -newgame seed starCount resourceMultiplier"); } else { @@ -264,7 +265,7 @@ public static void SetGameDescFromConfigFile(GameDesc gameDesc) } var resourceMultiplier = customFile.Bind("Basic", "resourceMultiplier", -1f, - "Resource Multiplier. Infinte = 100. Negative value: Default(1.0f) or remain the same.").Value; + "Resource Multiplier. Infinite = 100. Negative value: Default(1.0f) or remain the same.").Value; if (resourceMultiplier >= 0f) { gameDesc.resourceMultiplier = resourceMultiplier; @@ -375,10 +376,11 @@ private static void InitPatches() } #endif var harmony = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), PluginInfo.PLUGIN_ID); + harmony.PatchAll(typeof(Fix_Patches)); if (Multiplayer.IsDedicated) { Log.Info("Patching for headless mode..."); - harmony.PatchAll(typeof(Dedicated_Server_Patch)); + harmony.PatchAll(typeof(Dedicated_Server_Patches)); } #if DEBUG Environment.SetEnvironmentVariable("MONOMOD_DMD_DUMP", ""); diff --git a/NebulaPatcher/Patches/Dynamic/UIFatalErrorTip_Patch.cs b/NebulaPatcher/Patches/Dynamic/UIFatalErrorTip_Patch.cs index a1e1738b3..63f0ee53b 100644 --- a/NebulaPatcher/Patches/Dynamic/UIFatalErrorTip_Patch.cs +++ b/NebulaPatcher/Patches/Dynamic/UIFatalErrorTip_Patch.cs @@ -20,14 +20,15 @@ namespace NebulaPatcher.Patches.Dynamic; [HarmonyPatch(typeof(UIFatalErrorTip))] internal class UIFatalErrorTip_Patch { - private static GameObject button; + private static UIButton btnClose; + private static UIButton btnCopy; [HarmonyPostfix] [HarmonyPatch(nameof(UIFatalErrorTip._OnRegEvent))] [SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Original Function Name")] public static void _OnRegEvent_Postfix() { - // If there is errer message before game begin, we will show to user here + // If there is error message before game begin, we will show to user here if (Log.LastErrorMsg == null) { return; @@ -36,33 +37,19 @@ public static void _OnRegEvent_Postfix() Log.LastErrorMsg = null; } - [HarmonyPostfix] + [HarmonyPostfix, HarmonyAfter("aaa.dsp.plugin.ErrorAnalyzer")] [HarmonyPatch(nameof(UIFatalErrorTip._OnOpen))] [SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Original Function Name")] - public static void _OnOpen_Postfix() + public static void _OnOpen_Postfix(UIFatalErrorTip __instance) { try { - if (button == null) - { - button = GameObject.Find( - "UI Root/Overlay Canvas/In Game/Windows/Dyson Sphere Editor/Dyson Editor Control Panel/hierarchy/layers/blueprint-group/blueprint-2/copy-button"); - var errorPanel = GameObject.Find("UI Root/Overlay Canvas/Fatal Error/errored-panel/"); - errorPanel.transform.Find("tip-text-0").GetComponent().text = Title(); - Object.Destroy(errorPanel.transform.Find("tip-text-0").GetComponent()); - errorPanel.transform.Find("tip-text-1").GetComponent().text = Title(); - Object.Destroy(errorPanel.transform.Find("tip-text-1").GetComponent()); - button = Object.Instantiate(button, errorPanel.transform); - button.name = "Copy & Close button"; - button.transform.localPosition = - errorPanel.transform.Find("icon").localPosition + new Vector3(30, -35, 0); //-885 -30 //-855 -60 - button.GetComponent().color = new Color(0.3113f, 0f, 0.0097f, 0.6f); - button.GetComponent().BindOnClickSafe(OnClick); - ref var tips = ref button.GetComponent().tips; - tips.tipTitle = "Copy & Close Error"; - tips.tipText = "Copy the message to clipboard and close error."; - tips.corner = 1; - } + TryCreateButton(() => CreateCloseBtn(__instance), "Close Button"); + TryCreateButton(() => CreateCopyBtn(__instance), "Copy Button"); + __instance.transform.Find("tip-text-0").GetComponent().text = Title(); + __instance.transform.Find("tip-text-1").GetComponent().text = Title(); + Object.Destroy(__instance.transform.Find("tip-text-0").GetComponent()); + Object.Destroy(__instance.transform.Find("tip-text-1").GetComponent()); DedicatedServerReportError(); } @@ -72,6 +59,78 @@ public static void _OnOpen_Postfix() } } + [HarmonyPostfix] + [HarmonyPatch(nameof(UIFatalErrorTip._OnClose))] + public static void _OnClose_Postfix() + { + if (btnClose != null) + { + Object.Destroy(btnClose.gameObject); + btnClose = null; + } + if (btnCopy != null) + { + Object.Destroy(btnCopy.gameObject); + btnCopy = null; + } + } + + private static void TryCreateButton(Action createAction, string buttonName) + { + try + { + createAction(); + } + catch (Exception e) + { + Log.Warn($"{buttonName} did not patch!\n{e}"); + } + } + + private static UIButton CreateButton(string path, Transform parent, Vector3 positionOffset, Action onClickAction) + { + var go = GameObject.Find(path); + return CreateButton(go, parent, positionOffset, onClickAction); + } + + private static UIButton CreateButton(GameObject originalGo, Transform parent, Vector3 positionOffset, Action onClickAction) + { + if (originalGo != null) + { + var go = Object.Instantiate(originalGo, parent); + var rect = (RectTransform)go.transform; + rect.anchorMin = Vector2.up; + rect.anchorMax = Vector2.up; + rect.pivot = Vector2.up; + rect.anchoredPosition = positionOffset; + go.SetActive(true); + + var button = go.GetComponent(); + button.onClick += onClickAction; + button.tips.corner = 1; + return button; + } + return null; + } + + private static void CreateCloseBtn(UIFatalErrorTip __instance) + { + if (btnClose != null) return; + + const string PATH = "UI Root/Overlay Canvas/In Game/Windows/Window Template/panel-bg/btn-box/close-btn"; + btnClose = CreateButton(PATH, __instance.transform, new Vector3(-5, 0, 0), OnCloseClick); + } + + private static void CreateCopyBtn(UIFatalErrorTip __instance) + { + if (btnCopy != null) return; + + const string PATH = "UI Root/Overlay Canvas/In Game/Windows/Dyson Sphere Editor/Dyson Editor Control Panel/hierarchy/layers/blueprint-group/blueprint-2/copy-button"; + btnCopy = CreateButton(PATH, __instance.transform, new Vector3(5, -55, 0), OnCopyClick); + btnCopy.tips.tipTitle = "Copy Error".Translate(); + btnCopy.tips.tipText = "Copy the message to clipboard".Translate(); + } + private static void DedicatedServerReportError() { // OnOpen only run once for the first error report @@ -108,7 +167,12 @@ private static string Title() return stringBuilder.ToString(); } - private static void OnClick(int id) + private static void OnCloseClick(int _) + { + UIFatalErrorTip.ClearError(); + } + + private static void OnCopyClick(int id) { var stringBuilder = new StringBuilder(); stringBuilder.AppendLine("```ini"); @@ -146,8 +210,5 @@ private static void OnClick(int id) // Copy string to clipboard GUIUtility.systemCopyBuffer = stringBuilder.ToString(); - UIFatalErrorTip.ClearError(); - Object.Destroy(button); - button = null; } } diff --git a/NebulaPatcher/Patches/Dynamic/UIOptionWindow_Patch.cs b/NebulaPatcher/Patches/Dynamic/UIOptionWindow_Patch.cs index 697678062..5bb5c2573 100644 --- a/NebulaPatcher/Patches/Dynamic/UIOptionWindow_Patch.cs +++ b/NebulaPatcher/Patches/Dynamic/UIOptionWindow_Patch.cs @@ -25,7 +25,7 @@ namespace NebulaPatcher.Patches.Dynamic; [HarmonyPatch(typeof(UIOptionWindow))] internal class UIOptionWindow_Patch { - private const float subtabOffest = 160f; + private const float SubtabOffset = 160f; // Templates private static RectTransform checkboxTemplate; @@ -51,8 +51,8 @@ internal class UIOptionWindow_Patch [SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Original Function Name")] public static void _OnCreate_Postfix(UIOptionWindow __instance) { - tempToUICallbacks = new Dictionary(); - tempMultiplayerOptions = new MultiplayerOptions(); + tempToUICallbacks = new(); + tempMultiplayerOptions = new(); // Add multiplayer tab button var tabButtons = __instance.tabButtons; @@ -76,9 +76,10 @@ public static void _OnCreate_Postfix(UIOptionWindow __instance) __instance.tabTexts = newTabTexts; // Add multiplayer tab content + // Instantiate from "Miscellaneous" tab which has no scroll elements (yet) var tabTweeners = __instance.tabTweeners; - var contentTemplate = tabTweeners[0].GetComponent(); - multiplayerContent = Object.Instantiate(contentTemplate, contentTemplate.parent, true); + var contentRectTransform = tabTweeners[4].GetComponent(); + multiplayerContent = Object.Instantiate(contentRectTransform, contentRectTransform.parent, true); multiplayerContent.name = "multiplayer-content"; multiplayerContent.localPosition += new Vector3(0, -65, 0); @@ -99,7 +100,7 @@ public static void _OnCreate_Postfix(UIOptionWindow __instance) } } - // Add subtab-bar for config catagories + // Add subtab-bar for config categories var subtabsBar = (RectTransform)Object.Instantiate(multiplayerTab.parent, multiplayerContent); subtabSlider = Object.Instantiate(__instance.tabSlider, subtabsBar); subtabsBar.name = "subtab-line"; @@ -146,15 +147,15 @@ public static void _OnCreate_Postfix(UIOptionWindow __instance) } contentContainer = listContent; - // Find control templates - checkboxTemplate = contentTemplate.Find("fullscreen").GetComponent(); - comboBoxTemplate = contentTemplate.Find("resolution").GetComponent(); - sliderTemplate = contentTemplate.Find("dofblur").GetComponent(); + // Find control templates from tab "Video" + checkboxTemplate = __instance.fullscreenComp.transform.parent.GetComponent(); + comboBoxTemplate = __instance.resolutionComp.transform.parent.GetComponent(); + sliderTemplate = __instance.dofComp.transform.parent.GetComponent(); inputTemplate = Object.Instantiate(checkboxTemplate, listContent, false); Object.Destroy(inputTemplate.Find("CheckBox").gameObject); var inputField = Object.Instantiate( - UIRoot.instance.saveGameWindow.transform.Find("input-filename/InputField").GetComponent(), + UIRoot.instance.saveGameWindow.nameInput.transform.GetComponent(), inputTemplate, false); var fieldPosition = checkboxTemplate.GetChild(0).GetComponent().anchoredPosition; inputField.anchoredPosition = new Vector2(fieldPosition.x + 6, fieldPosition.y); @@ -249,7 +250,7 @@ private static void SetSubtabIndex(int index) } } subtabSlider.rectTransform.anchoredPosition = - new Vector2(subtabOffest * index, subtabSlider.rectTransform.anchoredPosition.y); + new Vector2(SubtabOffset * index, subtabSlider.rectTransform.anchoredPosition.y); } subtabIndex = index; } @@ -312,7 +313,7 @@ private static void CreateSubtab(string subtabName) { var subtab = Object.Instantiate(subtabTemplate, subtabTemplate.parent); var anchoredPosition = subtabTemplate.anchoredPosition; - subtab.anchoredPosition = new Vector2(anchoredPosition.x + subtabOffest * subtabButtons.Count, + subtab.anchoredPosition = new Vector2(anchoredPosition.x + SubtabOffset * subtabButtons.Count, anchoredPosition.y); subtabButtons.Add(subtab.GetComponent()); subtab.name = $"tab-button-{subtabButtons.Count}"; diff --git a/NebulaPatcher/Patches/Dynamic/Debugging.cs b/NebulaPatcher/Patches/Misc/Debugging.cs similarity index 97% rename from NebulaPatcher/Patches/Dynamic/Debugging.cs rename to NebulaPatcher/Patches/Misc/Debugging.cs index bc55356bd..76fcc0fc4 100644 --- a/NebulaPatcher/Patches/Dynamic/Debugging.cs +++ b/NebulaPatcher/Patches/Misc/Debugging.cs @@ -9,7 +9,7 @@ #endregion -namespace NebulaPatcher.Patches.Dynamic; +namespace NebulaPatcher.Patches.Misc; [HarmonyPatch(typeof(EnemyFormation))] internal class Debug_EnemyFormation_Patch @@ -46,7 +46,7 @@ public static void EnqueueTech_Postfix(GameHistoryData __instance, int techId) { if (Multiplayer.IsActive && Multiplayer.Session.History.IsIncomingRequest) { - //Do not run if this was triggered by incomming request + //Do not run if this was triggered by incoming request return; } __instance.UnlockTech(techId); @@ -97,13 +97,13 @@ public static void SetForNewGame_Postfix(Mecha __instance) __instance.player.package.AddItemStacked(1501, 600, 1, out _); //add sails __instance.player.package.AddItemStacked(1503, 60, 1, out _); //add rockets __instance.player.package.AddItemStacked(2312, 10, 1, out _); //add launching silo - __instance.player.package.AddItemStacked(2210, 10, 1, out _); //add artifical sun + __instance.player.package.AddItemStacked(2210, 10, 1, out _); //add artificial sun __instance.player.package.AddItemStacked(2311, 20, 1, out _); //add railgun __instance.player.package.AddItemStacked(2001, 600, 1, out _); //add MK3 belts __instance.player.package.AddItemStacked(2002, 600, 1, out _); //add MK3 belts __instance.player.package.AddItemStacked(2003, 600, 1, out _); //add MK3 belts __instance.player.package.AddItemStacked(2013, 100, 1, out _); //add MK3 inserters - __instance.player.package.AddItemStacked(2212, 20, 1, out _); //add satelite sub-station + __instance.player.package.AddItemStacked(2212, 20, 1, out _); //add satellite sub-station __instance.player.package.AddItemStacked(1128, 100, 1, out _); // add combustible unit __instance.player.package.AddItemStacked(1601, 100, 1, out _); // add magnum ammo box __instance.player.package.AddItemStacked(1604, 100, 1, out _); // add shell set @@ -111,7 +111,7 @@ public static void SetForNewGame_Postfix(Mecha __instance) __instance.player.package.AddItemStacked(1609, 100, 1, out _); // add missile set __instance.player.package.AddItemStacked(1613, 100, 1, out _); // add jammer - // temporay fix before PlayerTechBonuses update + // temporary fix before PlayerTechBonuses update __instance.energyShieldUnlocked = true; } } diff --git a/NebulaPatcher/Patches/Dynamic/Dedicated_Server_Patch.cs b/NebulaPatcher/Patches/Misc/Dedicated_Server_Patches.cs similarity index 99% rename from NebulaPatcher/Patches/Dynamic/Dedicated_Server_Patch.cs rename to NebulaPatcher/Patches/Misc/Dedicated_Server_Patches.cs index 430ef278d..d03fe2142 100644 --- a/NebulaPatcher/Patches/Dynamic/Dedicated_Server_Patch.cs +++ b/NebulaPatcher/Patches/Misc/Dedicated_Server_Patches.cs @@ -14,11 +14,11 @@ #endregion -namespace NebulaPatcher.Patches.Dynamic; +namespace NebulaPatcher.Patches.Misc; // Collections of patches that need to make game run in nographics mode // This part only get patch when Multiplayer.IsDedicated is true -internal class Dedicated_Server_Patch +internal class Dedicated_Server_Patches { [HarmonyPostfix] [HarmonyPatch(typeof(GameMain), nameof(GameMain.Begin))] diff --git a/NebulaPatcher/Patches/Misc/Fix_Patches.cs b/NebulaPatcher/Patches/Misc/Fix_Patches.cs new file mode 100644 index 000000000..4c5ccf148 --- /dev/null +++ b/NebulaPatcher/Patches/Misc/Fix_Patches.cs @@ -0,0 +1,60 @@ +#region + +using System; +using HarmonyLib; +using UnityEngine; + +#endregion + +namespace NebulaPatcher.Patches.Misc; + +// Collections of patches to deal with bugs that root cause is unknown +internal class Fix_Patches +{ + // IndexOutOfRangeException: Index was outside the bounds of the array. + // at BuildTool.GetPrefabDesc (System.Int32 objId)[0x0000e] ; IL_000E + // at BuildTool_Path.DeterminePreviews()[0x0008f] ;IL_008F + // + // This means BuildTool_Path.startObjectId has a positive id that is exceed entity pool + // May due to local buildTool affect by other player's build request + [HarmonyFinalizer] + [HarmonyPatch(typeof(BuildTool_Path), nameof(BuildTool_Path.DeterminePreviews))] + public static Exception DeterminePreviews(Exception __exception, BuildTool_Path __instance) + { + if (__exception != null) + { + // Reset state + __instance.startObjectId = 0; + __instance.startNearestAddonAreaIdx = 0; + __instance.startTarget = Vector3.zero; + __instance.pathPointCount = 0; + } + return null; + } + + // IndexOutOfRangeException: Index was outside the bounds of the array. + // at CargoTraffic.SetBeltState(System.Int32 beltId, System.Int32 state); (IL_002D) + // at CargoTraffic.SetBeltSelected(System.Int32 beltId); (IL_0000) + // at PlayerAction_Inspect.GameTick(System.Int64 timei); (IL_053E) + // + // Worst outcome when suppressed: Belt highlight is incorrect + [HarmonyFinalizer] + [HarmonyPatch(typeof(CargoTraffic), nameof(CargoTraffic.SetBeltState))] + public static Exception SetBeltState() + { + return null; + } + + // NullReferenceException: Object reference not set to an instance of an object + // at BGMController.UpdateLogic();(IL_03BC) + // at BGMController.LateUpdate(); (IL_0000) + // + // This means if (DSPGame.Game.running) is null + // Worst outcome when suppressed: BGM stops + [HarmonyFinalizer] + [HarmonyPatch(typeof(BGMController), nameof(BGMController.UpdateLogic))] + public static Exception UpdateLogic() + { + return null; + } +} diff --git a/README.md b/README.md index 2c66cb806..fb7d0480c 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ Most of the battle aspects are sync, only few features are still WIP. - [x] Server state persistence - [x] Power network syncing (charger and request power from dyson sphere) - [x] Warning alarm syncing -- [ ] Broadcast notification syncing +- [x] Broadcast notification syncing (events with guide icon) - [ ] Logistics Control Panel (I) syncing diff --git a/version.json b/version.json index 51f7e273c..eee8ac496 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "0.9.8", + "version": "0.9.9", "assemblyVersion": { "precision": "build" },