From e3b0eb68c0adcfb5933994e70118b0151d815f31 Mon Sep 17 00:00:00 2001 From: Atony <75191600+AtonyGit@users.noreply.github.com> Date: Fri, 12 Jun 2026 18:03:13 -0500 Subject: [PATCH] Temporary measures for clearing up memory leaks --- .../ModifierDisplayComponent.cs | 5 ++- MiraAPI/Patches/Freeplay/TaskAdderPatches.cs | 2 +- MiraAPI/Patches/HudManagerPatches.cs | 2 +- MiraAPI/Patches/Menu/HatsTabPatches.cs | 6 +-- MiraAPI/Patches/Menu/NameplatesTabPatches.cs | 4 +- MiraAPI/Patches/Menu/SkinsTabPatches.cs | 4 +- MiraAPI/Patches/Menu/VisorsTabPatches.cs | 4 +- .../Patches/Options/GamePresetsTabPatches.cs | 2 +- .../Patches/Options/RoleSettingMenuPatches.cs | 44 ++++++++++++++----- MiraAPI/Patches/Roles/HudManagerPatches.cs | 7 ++- MiraAPI/Presets/PresetManager.cs | 3 +- MiraAPI/Presets/SavePresetPopup.cs | 2 +- MiraAPI/Utilities/Extensions.cs | 20 ++++++++- 13 files changed, 74 insertions(+), 31 deletions(-) diff --git a/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs b/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs index 2982f67..e47d775 100644 --- a/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs +++ b/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs @@ -4,6 +4,7 @@ using System.Linq; using HarmonyLib; using Il2CppInterop.Runtime.Attributes; using MiraAPI.LocalSettings; +using MiraAPI.Utilities; using MiraAPI.Utilities.Assets; using Reactor.Utilities.Attributes; using Reactor.Utilities.Extensions; @@ -137,7 +138,7 @@ public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr) internal void DestroyComponent(ModifierUiComponent component) { _modifiers.Remove(component.Modifier!); - component.gameObject.DestroyImmediate(); + component.gameObject.FakeDestroy(); RefreshModifiers(); } @@ -207,7 +208,7 @@ public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr) continue; } - mod.Value.gameObject.Destroy(); + mod.Value.gameObject.FakeDestroy(); _modifiers.Remove(mod.Key); } diff --git a/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs b/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs index 5bee1ae..ea13199 100644 --- a/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs +++ b/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs @@ -276,7 +276,7 @@ public static class TaskAdderPatches __instance.transform.FindChild("TitleText_TMP")?.gameObject.DestroyImmediate(); } - __instance.ActiveItems.ToArray().Do(x => x.gameObject.Destroy()); + __instance.ActiveItems.ToArray().Do(x => x.gameObject.FakeDestroy()); __instance.ActiveItems.Clear(); float num = 0f; diff --git a/MiraAPI/Patches/HudManagerPatches.cs b/MiraAPI/Patches/HudManagerPatches.cs index de8593b..7af625e 100644 --- a/MiraAPI/Patches/HudManagerPatches.cs +++ b/MiraAPI/Patches/HudManagerPatches.cs @@ -143,7 +143,7 @@ public static class HudManagerPatches foreach (var t in BottomLeft.GetComponentsInChildren(true)) { - t.gameObject.Destroy(); + t.gameObject.FakeDestroy(); } var gridArrange = BottomLeft.GetComponent(); diff --git a/MiraAPI/Patches/Menu/HatsTabPatches.cs b/MiraAPI/Patches/Menu/HatsTabPatches.cs index 5f07cfd..98bbc92 100644 --- a/MiraAPI/Patches/Menu/HatsTabPatches.cs +++ b/MiraAPI/Patches/Menu/HatsTabPatches.cs @@ -11,8 +11,6 @@ using Reactor.Utilities; using Reactor.Utilities.Extensions; using TMPro; using UnityEngine; -using UnityEngine.Events; -using UnityEngine.UI; using Object = UnityEngine.Object; namespace MiraAPI.Patches.Menu; @@ -94,9 +92,9 @@ public static class HatsTabPatches if (loadRoutine != null) Coroutines.Stop(loadRoutine); hatIndex = 0; - foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.Destroy(); + foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.FakeDestroy(); __instance.ColorChips.Clear(); - __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.Destroy()); + __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.FakeDestroy()); var groupNameText = __instance.GetComponentInChildren(false); diff --git a/MiraAPI/Patches/Menu/NameplatesTabPatches.cs b/MiraAPI/Patches/Menu/NameplatesTabPatches.cs index 1526cce..6fd1031 100644 --- a/MiraAPI/Patches/Menu/NameplatesTabPatches.cs +++ b/MiraAPI/Patches/Menu/NameplatesTabPatches.cs @@ -81,9 +81,9 @@ public static class NameplatesTabPatches private static void GenerateHats(NameplatesTab __instance, int page) { - foreach (ColorChip instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.Destroy(); + foreach (ColorChip instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.FakeDestroy(); __instance.ColorChips.Clear(); - __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.Destroy()); + __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.FakeDestroy()); var groupNameText = __instance.GetComponentInChildren(false); diff --git a/MiraAPI/Patches/Menu/SkinsTabPatches.cs b/MiraAPI/Patches/Menu/SkinsTabPatches.cs index 8e99bf2..bc8e64e 100644 --- a/MiraAPI/Patches/Menu/SkinsTabPatches.cs +++ b/MiraAPI/Patches/Menu/SkinsTabPatches.cs @@ -83,9 +83,9 @@ public static class SkinsTabPatches private static void GenerateHats(SkinsTab __instance, int page) { - foreach (ColorChip instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.Destroy(); + foreach (ColorChip instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.FakeDestroy(); __instance.ColorChips.Clear(); - __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.Destroy()); + __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.FakeDestroy()); var groupNameText = __instance.GetComponentInChildren(false); diff --git a/MiraAPI/Patches/Menu/VisorsTabPatches.cs b/MiraAPI/Patches/Menu/VisorsTabPatches.cs index c543a94..f3a8d93 100644 --- a/MiraAPI/Patches/Menu/VisorsTabPatches.cs +++ b/MiraAPI/Patches/Menu/VisorsTabPatches.cs @@ -81,9 +81,9 @@ public static class VisorsTabPatches private static void GenerateHats(VisorsTab __instance, int page) { - foreach (ColorChip instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.Destroy(); + foreach (ColorChip instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.FakeDestroy(); __instance.ColorChips.Clear(); - __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.Destroy()); + __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.FakeDestroy()); var groupNameText = __instance.GetComponentInChildren(false); diff --git a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs index 2c6cc68..bae9b8b 100644 --- a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs +++ b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs @@ -262,7 +262,7 @@ internal static class GamePresetsTabPatches iconSpriteRend.sprite = MiraAssets.RefreshIcon.LoadAsset(); Helpers.DivideSize(refreshButton.gameObject, 2f); - refreshButton.buttonText.gameObject.Destroy(); + refreshButton.buttonText.gameObject.FakeDestroy(); refreshButton.OnClick = new Button.ButtonClickedEvent(); refreshButton.OnClick.AddListener((UnityAction)Refresh); diff --git a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs index 62560a8..6e28cd9 100644 --- a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs @@ -53,9 +53,23 @@ public static class RoleSettingMenuPatches [HarmonyPatch(nameof(RolesSettingsMenu.SetQuotaTab))] public static bool SetQuotaTabPatch(RolesSettingsMenu __instance) { - Headers.ForEach(Object.Destroy); - RoleOptionSettings.ForEach(Object.Destroy); + foreach (var obj in Headers) + { + if (!obj) + { + continue; + } + obj.FakeDestroy(); + } Headers.Clear(); + foreach (var obj in RoleOptionSettings) + { + if (!obj) + { + continue; + } + obj.gameObject.FakeDestroy(); + } RoleOptionSettings.Clear(); CurrentRole = null; CurrentRoleOptions = null; @@ -204,9 +218,9 @@ public static class RoleSettingMenuPatches var blankLabel = quotaInst.transform.FindChild("BlankLabel").gameObject; var chanceLabel = quotaInst.transform.FindChild("Chance Label").gameObject; var countLabel = quotaInst.transform.FindChild("# Label").gameObject; - blankLabel.Destroy(); - chanceLabel.Destroy(); - countLabel.Destroy(); + blankLabel.FakeDestroy(); + chanceLabel.FakeDestroy(); + countLabel.FakeDestroy(); categoryHeaderMasked.Background.sprite = MiraAssets.CategoryHeader.LoadAsset(); categoryHeaderMasked.Background.sprite.texture.filterMode = FilterMode.Bilinear; @@ -289,14 +303,22 @@ public static class RoleSettingMenuPatches { RoleGroupHidden[group] = !groupHidden; } - foreach (var header in Headers) + foreach (var obj in Headers) { - header.Destroy(); + if (!obj) + { + continue; + } + obj.FakeDestroy(); } Headers.Clear(); - foreach (var option in RoleOptionSettings) + foreach (var obj in RoleOptionSettings) { - option.gameObject.Destroy(); + if (!obj) + { + continue; + } + obj.gameObject.FakeDestroy(); } RoleOptionSettings.Clear(); __instance.SetQuotaTab(); @@ -422,7 +444,7 @@ public static class RoleSettingMenuPatches { foreach (var optBehaviour in __instance.AdvancedRolesSettings.GetComponentsInChildren()) { - optBehaviour.gameObject.DestroyImmediate(); + optBehaviour.gameObject.FakeDestroy(); } CurrentRole = role; @@ -618,7 +640,7 @@ public static class RoleSettingMenuPatches var newButton = Object.Instantiate(roleOptionSetting.buttons[0], roleOptionSetting.transform); newButton.name = "ConfigButton"; newButton.transform.localPosition = new Vector3(0.4473f, -0.3f, -2f); - newButton.transform.FindChild("Text_TMP").gameObject.DestroyImmediate(); + newButton.transform.FindChild("Text_TMP").gameObject.FakeDestroy(); newButton.activeSprites.Destroy(); var btnRend = newButton.transform.FindChild("ButtonSprite").GetComponent(); diff --git a/MiraAPI/Patches/Roles/HudManagerPatches.cs b/MiraAPI/Patches/Roles/HudManagerPatches.cs index d8ac9e1..cc46c13 100644 --- a/MiraAPI/Patches/Roles/HudManagerPatches.cs +++ b/MiraAPI/Patches/Roles/HudManagerPatches.cs @@ -1,7 +1,9 @@ using HarmonyLib; using InnerNet; using MiraAPI.Roles; +using MiraAPI.Utilities; using Reactor.Utilities.Extensions; +using UnityEngine; namespace MiraAPI.Patches.Roles; @@ -69,9 +71,10 @@ public static class HudManagerPatches CustomRoleManager.UpdateRoleTab(RoleTab, customRole); } - else if (RoleTab) + else if (RoleTab != null) { - RoleTab?.gameObject.Destroy(); + RoleTab.gameObject.FakeDestroy(); + RoleTab = null; } } } diff --git a/MiraAPI/Presets/PresetManager.cs b/MiraAPI/Presets/PresetManager.cs index ba4289f..2ef0c55 100644 --- a/MiraAPI/Presets/PresetManager.cs +++ b/MiraAPI/Presets/PresetManager.cs @@ -4,6 +4,7 @@ using System.Linq; using BepInEx.Configuration; using MiraAPI.PluginLoading; using MiraAPI.Roles; +using MiraAPI.Utilities; using Reactor.Utilities; using UnityEngine; @@ -61,7 +62,7 @@ public static class PresetManager { if (btn != null) { - Object.DestroyImmediate(btn); + btn.FakeDestroy(); } } diff --git a/MiraAPI/Presets/SavePresetPopup.cs b/MiraAPI/Presets/SavePresetPopup.cs index 87959e7..721f46c 100644 --- a/MiraAPI/Presets/SavePresetPopup.cs +++ b/MiraAPI/Presets/SavePresetPopup.cs @@ -26,7 +26,7 @@ public class SavePresetPopup(nint cppPtr) : Minigame(cppPtr) // Cleanup holder object public void OnDestroy() { - transform.parent.gameObject.Destroy(); + transform.parent.gameObject.FakeDestroy(); } public override void Close() diff --git a/MiraAPI/Utilities/Extensions.cs b/MiraAPI/Utilities/Extensions.cs index 8c4acc7..b594598 100644 --- a/MiraAPI/Utilities/Extensions.cs +++ b/MiraAPI/Utilities/Extensions.cs @@ -53,7 +53,7 @@ public static class Extensions bool isComplete; if (self.amClosing == Minigame.CloseState.Closing) { - UnityEngine.Object.Destroy(self.gameObject); + self.gameObject.FakeDestroy(); return; } if (self.CloseSound && Constants.ShouldPlaySfx()) @@ -219,6 +219,24 @@ public static class Extensions return Mathf.Approximately(number, Mathf.Round(number)); } + /// + /// Hides the object away rather than destroy it to prevent memory leaks. + /// + /// The object to hide. + public static void FakeDestroy(this GameObject obj) + { + obj.hideFlags = HideFlags.HideAndDontSave; + if (!toDestroyHolder) + { + toDestroyHolder = new GameObject("ToDestroyHolder"); + toDestroyHolder.gameObject.SetActive(false); + } + obj.transform.SetParent(toDestroyHolder.transform); + obj.SetActive(false); + } + + private static GameObject toDestroyHolder; + /// /// Gets a cache of player's vote data components to improve performance. ///