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 1/4] 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. /// From 3a5a3978469735a881c3e88aabb595a4c9cccb59 Mon Sep 17 00:00:00 2001 From: Atony <75191600+AtonyGit@users.noreply.github.com> Date: Sat, 20 Jun 2026 14:28:08 -0500 Subject: [PATCH 2/4] Only run gc when using v17.4 --- .../ModifierDisplayComponent.cs | 4 +- MiraAPI/Patches/Freeplay/TaskAdderPatches.cs | 2 +- MiraAPI/Patches/HudManagerPatches.cs | 2 +- MiraAPI/Patches/Menu/HatsTabPatches.cs | 4 +- .../Patches/Menu/MainMenuManagerPatches.cs | 8 +- 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 | 18 +-- MiraAPI/Patches/Roles/HudManagerPatches.cs | 2 +- MiraAPI/Presets/PresetManager.cs | 2 +- MiraAPI/Presets/SavePresetPopup.cs | 2 +- MiraAPI/Utilities/Extensions.cs | 131 ++++++++++++++++-- 14 files changed, 154 insertions(+), 35 deletions(-) diff --git a/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs b/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs index e47d775..18bd476 100644 --- a/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs +++ b/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs @@ -138,7 +138,7 @@ public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr) internal void DestroyComponent(ModifierUiComponent component) { _modifiers.Remove(component.Modifier!); - component.gameObject.FakeDestroy(); + component.gameObject.DeepDestroy(); RefreshModifiers(); } @@ -208,7 +208,7 @@ public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr) continue; } - mod.Value.gameObject.FakeDestroy(); + mod.Value.gameObject.DeepDestroy(); _modifiers.Remove(mod.Key); } diff --git a/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs b/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs index ea13199..22cea2d 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.FakeDestroy()); + __instance.ActiveItems.ToArray().Do(x => x.gameObject.DeepDestroy()); __instance.ActiveItems.Clear(); float num = 0f; diff --git a/MiraAPI/Patches/HudManagerPatches.cs b/MiraAPI/Patches/HudManagerPatches.cs index 7af625e..1f19b5a 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.FakeDestroy(); + t.gameObject.DeepDestroy(); } var gridArrange = BottomLeft.GetComponent(); diff --git a/MiraAPI/Patches/Menu/HatsTabPatches.cs b/MiraAPI/Patches/Menu/HatsTabPatches.cs index 98bbc92..c6ad646 100644 --- a/MiraAPI/Patches/Menu/HatsTabPatches.cs +++ b/MiraAPI/Patches/Menu/HatsTabPatches.cs @@ -92,9 +92,9 @@ public static class HatsTabPatches if (loadRoutine != null) Coroutines.Stop(loadRoutine); hatIndex = 0; - foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.FakeDestroy(); + foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.DeepDestroy(); __instance.ColorChips.Clear(); - __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.FakeDestroy()); + __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.DeepDestroy()); var groupNameText = __instance.GetComponentInChildren(false); diff --git a/MiraAPI/Patches/Menu/MainMenuManagerPatches.cs b/MiraAPI/Patches/Menu/MainMenuManagerPatches.cs index b2377fe..edc7e9b 100644 --- a/MiraAPI/Patches/Menu/MainMenuManagerPatches.cs +++ b/MiraAPI/Patches/Menu/MainMenuManagerPatches.cs @@ -1,4 +1,5 @@ -using System.Collections; +using System; +using System.Collections; using HarmonyLib; using MiraAPI.LocalSettings; using MiraAPI.Utilities.Assets; @@ -13,6 +14,8 @@ namespace MiraAPI.Patches.Menu; [HarmonyPatch(typeof(MainMenuManager))] public static class MainMenuManagerPatches { + internal static bool NeedsDeepDestroy { get; private set; } + /// /// A postifix on Awake to load all the addressables registered. /// @@ -20,6 +23,9 @@ public static class MainMenuManagerPatches [HarmonyPostfix] public static void AwakePostfix() { + var requiredVersion = new Version(2026, 6, 5); + var version = Version.Parse(Application.version); + NeedsDeepDestroy = version >= requiredVersion; AddressablesLoader.LoadAll(); Coroutines.Start(SetFps()); } diff --git a/MiraAPI/Patches/Menu/NameplatesTabPatches.cs b/MiraAPI/Patches/Menu/NameplatesTabPatches.cs index 6fd1031..e8d15a8 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.FakeDestroy(); + foreach (ColorChip instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.DeepDestroy(); __instance.ColorChips.Clear(); - __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.FakeDestroy()); + __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.DeepDestroy()); var groupNameText = __instance.GetComponentInChildren(false); diff --git a/MiraAPI/Patches/Menu/SkinsTabPatches.cs b/MiraAPI/Patches/Menu/SkinsTabPatches.cs index bc8e64e..64b0b54 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.FakeDestroy(); + foreach (ColorChip instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.DeepDestroy(); __instance.ColorChips.Clear(); - __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.FakeDestroy()); + __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.DeepDestroy()); var groupNameText = __instance.GetComponentInChildren(false); diff --git a/MiraAPI/Patches/Menu/VisorsTabPatches.cs b/MiraAPI/Patches/Menu/VisorsTabPatches.cs index f3a8d93..385fe97 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.FakeDestroy(); + foreach (ColorChip instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.DeepDestroy(); __instance.ColorChips.Clear(); - __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.FakeDestroy()); + __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.DeepDestroy()); var groupNameText = __instance.GetComponentInChildren(false); diff --git a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs index bae9b8b..a4b6cca 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.FakeDestroy(); + refreshButton.buttonText.gameObject.DeepDestroy(); 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 6e28cd9..1d17d70 100644 --- a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs @@ -59,7 +59,7 @@ public static class RoleSettingMenuPatches { continue; } - obj.FakeDestroy(); + obj.DeepDestroy(); } Headers.Clear(); foreach (var obj in RoleOptionSettings) @@ -68,7 +68,7 @@ public static class RoleSettingMenuPatches { continue; } - obj.gameObject.FakeDestroy(); + obj.gameObject.DeepDestroy(); } RoleOptionSettings.Clear(); CurrentRole = null; @@ -218,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.FakeDestroy(); - chanceLabel.FakeDestroy(); - countLabel.FakeDestroy(); + blankLabel.DeepDestroy(); + chanceLabel.DeepDestroy(); + countLabel.DeepDestroy(); categoryHeaderMasked.Background.sprite = MiraAssets.CategoryHeader.LoadAsset(); categoryHeaderMasked.Background.sprite.texture.filterMode = FilterMode.Bilinear; @@ -309,7 +309,7 @@ public static class RoleSettingMenuPatches { continue; } - obj.FakeDestroy(); + obj.DeepDestroy(); } Headers.Clear(); foreach (var obj in RoleOptionSettings) @@ -318,7 +318,7 @@ public static class RoleSettingMenuPatches { continue; } - obj.gameObject.FakeDestroy(); + obj.gameObject.DeepDestroy(); } RoleOptionSettings.Clear(); __instance.SetQuotaTab(); @@ -444,7 +444,7 @@ public static class RoleSettingMenuPatches { foreach (var optBehaviour in __instance.AdvancedRolesSettings.GetComponentsInChildren()) { - optBehaviour.gameObject.FakeDestroy(); + optBehaviour.gameObject.DeepDestroy(); } CurrentRole = role; @@ -640,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.FakeDestroy(); + newButton.transform.FindChild("Text_TMP").gameObject.DeepDestroy(); 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 cc46c13..d5d23dc 100644 --- a/MiraAPI/Patches/Roles/HudManagerPatches.cs +++ b/MiraAPI/Patches/Roles/HudManagerPatches.cs @@ -73,7 +73,7 @@ public static class HudManagerPatches } else if (RoleTab != null) { - RoleTab.gameObject.FakeDestroy(); + RoleTab.gameObject.DeepDestroy(); RoleTab = null; } } diff --git a/MiraAPI/Presets/PresetManager.cs b/MiraAPI/Presets/PresetManager.cs index 2ef0c55..07f00cd 100644 --- a/MiraAPI/Presets/PresetManager.cs +++ b/MiraAPI/Presets/PresetManager.cs @@ -62,7 +62,7 @@ public static class PresetManager { if (btn != null) { - btn.FakeDestroy(); + btn.DeepDestroy(); } } diff --git a/MiraAPI/Presets/SavePresetPopup.cs b/MiraAPI/Presets/SavePresetPopup.cs index 721f46c..23ce48b 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.FakeDestroy(); + transform.parent.gameObject.DeepDestroy(); } public override void Close() diff --git a/MiraAPI/Utilities/Extensions.cs b/MiraAPI/Utilities/Extensions.cs index b594598..ffc8e72 100644 --- a/MiraAPI/Utilities/Extensions.cs +++ b/MiraAPI/Utilities/Extensions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -7,9 +8,11 @@ using System.Text.RegularExpressions; using HarmonyLib; using MiraAPI.GameOptions; using MiraAPI.Networking; +using MiraAPI.Patches.Menu; using MiraAPI.Roles; using MiraAPI.Voting; using Reactor.Utilities; +using Reactor.Utilities.Extensions; using Rewired; using Rewired.Data; using TMPro; @@ -53,7 +56,7 @@ public static class Extensions bool isComplete; if (self.amClosing == Minigame.CloseState.Closing) { - self.gameObject.FakeDestroy(); + self.gameObject.DeepDestroy(); return; } if (self.CloseSound && Constants.ShouldPlaySfx()) @@ -223,19 +226,129 @@ public static class Extensions /// Hides the object away rather than destroy it to prevent memory leaks. /// /// The object to hide. - public static void FakeDestroy(this GameObject obj) + public static void DeepDestroy(this GameObject obj, bool clearGc = true) { - obj.hideFlags = HideFlags.HideAndDontSave; - if (!toDestroyHolder) + if (MainMenuManagerPatches.NeedsDeepDestroy) { - toDestroyHolder = new GameObject("ToDestroyHolder"); - toDestroyHolder.gameObject.SetActive(false); + Coroutines.Start(Nuke(obj, clearGc)); + } + else + { + obj?.Destroy(); } - obj.transform.SetParent(toDestroyHolder.transform); - obj.SetActive(false); } - private static GameObject toDestroyHolder; + private static IEnumerator Nuke(GameObject? go, bool clearGc) + { + if (go == null) + yield break; + + try + { + go.transform.SetParent(null, false); + } + catch + { + // ignored + } + + try + { + go.SetActive(false); + } + catch + { + // ignored + } + + foreach (var mb in go.GetComponentsInChildren(true)) + { + if (mb == null) + continue; + + try + { + mb.StopAllCoroutines(); + } + catch + { + // ignored + } + + try + { + mb.enabled = false; + } + catch + { + // ignored + } + } + + foreach (var renderer in go.GetComponentsInChildren(true)) + { + if (renderer == null) + continue; + + try + { + foreach (var mat in renderer.materials) + { + if (mat != null) + UnityEngine.Object.Destroy(mat); + } + } + catch + { + // ignored + } + } + + foreach (var filter in go.GetComponentsInChildren(true)) + { + if (filter == null) + continue; + + try + { + var mesh = filter.mesh; + if (mesh != null) + UnityEngine.Object.Destroy(mesh); + } + catch + { + // ignored + } + } + + UnityEngine.Object.Destroy(go); + yield return null; + if (clearGc) + { + yield return CoFreeResources(); + } + } + + /// + /// Clears up the Garbage Collector manually if necessary. + /// + public static void ClearGarbageCollector() + { + if (!MainMenuManagerPatches.NeedsDeepDestroy) + { + return; + } + Coroutines.Start(CoFreeResources()); + } + + private static IEnumerator CoFreeResources() + { + yield return Resources.UnloadUnusedAssets(); + + GC.Collect(0, GCCollectionMode.Forced, blocking: true); + GC.WaitForPendingFinalizers(); + GC.Collect(0, GCCollectionMode.Forced, blocking: true); + } /// /// Gets a cache of player's vote data components to improve performance. From 750e00d06d46061bcb19c07dd1a3c4a46c7793e8 Mon Sep 17 00:00:00 2001 From: Atony <75191600+AtonyGit@users.noreply.github.com> Date: Sat, 20 Jun 2026 14:53:45 -0500 Subject: [PATCH 3/4] Run GC when destroying multiple objects --- MiraAPI/Patches/Freeplay/TaskAdderPatches.cs | 3 ++- MiraAPI/Patches/HudManagerPatches.cs | 3 ++- MiraAPI/Patches/Menu/HatsTabPatches.cs | 5 +++-- MiraAPI/Patches/Menu/NameplatesTabPatches.cs | 5 +++-- MiraAPI/Patches/Menu/SkinsTabPatches.cs | 5 +++-- MiraAPI/Patches/Menu/VisorsTabPatches.cs | 5 +++-- .../Patches/Options/RoleSettingMenuPatches.cs | 19 +++++++++++-------- MiraAPI/Utilities/Extensions.cs | 5 +++-- 8 files changed, 30 insertions(+), 20 deletions(-) diff --git a/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs b/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs index 22cea2d..f1612d9 100644 --- a/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs +++ b/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs @@ -276,8 +276,9 @@ public static class TaskAdderPatches __instance.transform.FindChild("TitleText_TMP")?.gameObject.DestroyImmediate(); } - __instance.ActiveItems.ToArray().Do(x => x.gameObject.DeepDestroy()); + __instance.ActiveItems.ToArray().Do(x => x.gameObject.DeepDestroy(false)); __instance.ActiveItems.Clear(); + Utilities.Extensions.ClearGarbageCollector(); float num = 0f; float num2 = 0f; diff --git a/MiraAPI/Patches/HudManagerPatches.cs b/MiraAPI/Patches/HudManagerPatches.cs index 1f19b5a..e3a6b91 100644 --- a/MiraAPI/Patches/HudManagerPatches.cs +++ b/MiraAPI/Patches/HudManagerPatches.cs @@ -143,8 +143,9 @@ public static class HudManagerPatches foreach (var t in BottomLeft.GetComponentsInChildren(true)) { - t.gameObject.DeepDestroy(); + t.gameObject.DeepDestroy(false); } + Utilities.Extensions.ClearGarbageCollector(); var gridArrange = BottomLeft.GetComponent(); var aspectPosition = BottomLeft.GetComponent(); diff --git a/MiraAPI/Patches/Menu/HatsTabPatches.cs b/MiraAPI/Patches/Menu/HatsTabPatches.cs index c6ad646..25eca33 100644 --- a/MiraAPI/Patches/Menu/HatsTabPatches.cs +++ b/MiraAPI/Patches/Menu/HatsTabPatches.cs @@ -92,9 +92,10 @@ public static class HatsTabPatches if (loadRoutine != null) Coroutines.Stop(loadRoutine); hatIndex = 0; - foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.DeepDestroy(); + foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.DeepDestroy(false); __instance.ColorChips.Clear(); - __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.DeepDestroy()); + __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.DeepDestroy(false)); + Utilities.Extensions.ClearGarbageCollector(); var groupNameText = __instance.GetComponentInChildren(false); diff --git a/MiraAPI/Patches/Menu/NameplatesTabPatches.cs b/MiraAPI/Patches/Menu/NameplatesTabPatches.cs index e8d15a8..0875cb2 100644 --- a/MiraAPI/Patches/Menu/NameplatesTabPatches.cs +++ b/MiraAPI/Patches/Menu/NameplatesTabPatches.cs @@ -81,9 +81,10 @@ public static class NameplatesTabPatches private static void GenerateHats(NameplatesTab __instance, int page) { - foreach (ColorChip instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.DeepDestroy(); + foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.DeepDestroy(false); __instance.ColorChips.Clear(); - __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.DeepDestroy()); + __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.DeepDestroy(false)); + Utilities.Extensions.ClearGarbageCollector(); var groupNameText = __instance.GetComponentInChildren(false); diff --git a/MiraAPI/Patches/Menu/SkinsTabPatches.cs b/MiraAPI/Patches/Menu/SkinsTabPatches.cs index 64b0b54..24bec7c 100644 --- a/MiraAPI/Patches/Menu/SkinsTabPatches.cs +++ b/MiraAPI/Patches/Menu/SkinsTabPatches.cs @@ -83,9 +83,10 @@ public static class SkinsTabPatches private static void GenerateHats(SkinsTab __instance, int page) { - foreach (ColorChip instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.DeepDestroy(); + foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.DeepDestroy(false); __instance.ColorChips.Clear(); - __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.DeepDestroy()); + __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.DeepDestroy(false)); + Utilities.Extensions.ClearGarbageCollector(); var groupNameText = __instance.GetComponentInChildren(false); diff --git a/MiraAPI/Patches/Menu/VisorsTabPatches.cs b/MiraAPI/Patches/Menu/VisorsTabPatches.cs index 385fe97..86bfa24 100644 --- a/MiraAPI/Patches/Menu/VisorsTabPatches.cs +++ b/MiraAPI/Patches/Menu/VisorsTabPatches.cs @@ -81,9 +81,10 @@ public static class VisorsTabPatches private static void GenerateHats(VisorsTab __instance, int page) { - foreach (ColorChip instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.DeepDestroy(); + foreach (var instanceColorChip in __instance.ColorChips) instanceColorChip.gameObject.DeepDestroy(false); __instance.ColorChips.Clear(); - __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.DeepDestroy()); + __instance.scroller.Inner.GetComponentsInChildren().Do(x => x.gameObject.DeepDestroy(false)); + Utilities.Extensions.ClearGarbageCollector(); var groupNameText = __instance.GetComponentInChildren(false); diff --git a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs index 1d17d70..082cc9c 100644 --- a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs @@ -59,7 +59,7 @@ public static class RoleSettingMenuPatches { continue; } - obj.DeepDestroy(); + obj.DeepDestroy(false); } Headers.Clear(); foreach (var obj in RoleOptionSettings) @@ -68,7 +68,7 @@ public static class RoleSettingMenuPatches { continue; } - obj.gameObject.DeepDestroy(); + obj.gameObject.DeepDestroy(false); } RoleOptionSettings.Clear(); CurrentRole = null; @@ -76,6 +76,7 @@ public static class RoleSettingMenuPatches __instance.roleChances = new Il2CppSystem.Collections.Generic.List(); __instance.advancedSettingChildren = new Il2CppSystem.Collections.Generic.List(); + Utilities.Extensions.ClearGarbageCollector(); var maskBg = __instance.scrollBar.transform.FindChild("MaskBg"); var hitbox = __instance.scrollBar.transform.FindChild("Hitbox"); @@ -218,9 +219,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.DeepDestroy(); - chanceLabel.DeepDestroy(); - countLabel.DeepDestroy(); + blankLabel.DeepDestroy(false); + chanceLabel.DeepDestroy(false); + countLabel.DeepDestroy(false); categoryHeaderMasked.Background.sprite = MiraAssets.CategoryHeader.LoadAsset(); categoryHeaderMasked.Background.sprite.texture.filterMode = FilterMode.Bilinear; @@ -309,7 +310,7 @@ public static class RoleSettingMenuPatches { continue; } - obj.DeepDestroy(); + obj.DeepDestroy(false); } Headers.Clear(); foreach (var obj in RoleOptionSettings) @@ -318,9 +319,10 @@ public static class RoleSettingMenuPatches { continue; } - obj.gameObject.DeepDestroy(); + obj.gameObject.DeepDestroy(false); } RoleOptionSettings.Clear(); + Utilities.Extensions.ClearGarbageCollector(); __instance.SetQuotaTab(); })); headerBtn.SetButtonEnableState(true); @@ -444,11 +446,12 @@ public static class RoleSettingMenuPatches { foreach (var optBehaviour in __instance.AdvancedRolesSettings.GetComponentsInChildren()) { - optBehaviour.gameObject.DeepDestroy(); + optBehaviour.gameObject.DeepDestroy(false); } CurrentRole = role; __instance.advancedSettingChildren.Clear(); + Utilities.Extensions.ClearGarbageCollector(); // TODO: create sub groups under the role settings. var filteredOptions = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups diff --git a/MiraAPI/Utilities/Extensions.cs b/MiraAPI/Utilities/Extensions.cs index ffc8e72..9c6faa7 100644 --- a/MiraAPI/Utilities/Extensions.cs +++ b/MiraAPI/Utilities/Extensions.cs @@ -223,9 +223,10 @@ public static class Extensions } /// - /// Hides the object away rather than destroy it to prevent memory leaks. + /// Destroys the object properly. /// - /// The object to hide. + /// The object to destroy. + /// Whether to run the garbage collector immediately. public static void DeepDestroy(this GameObject obj, bool clearGc = true) { if (MainMenuManagerPatches.NeedsDeepDestroy) From ff658db6f10c4ea713a41782ff503983714245fb Mon Sep 17 00:00:00 2001 From: Atony <75191600+AtonyGit@users.noreply.github.com> Date: Sat, 20 Jun 2026 15:08:22 -0500 Subject: [PATCH 4/4] Remove destroyable singletons --- MiraAPI/Hud/CustomPlayerMenu.cs | 2 +- MiraAPI/Patches/DetectiveNotesMinigamePatch.cs | 2 +- .../LocalSettings/ToggleBehaviourPatch.cs | 4 ++-- MiraAPI/Patches/Menu/NameplatesTabPatches.cs | 2 +- MiraAPI/Patches/Menu/VisorsTabPatches.cs | 2 +- MiraAPI/Utilities/Assets/AddressablesLoader.cs | 16 ++++++++-------- MiraAPI/Utilities/Extensions.cs | 2 +- MiraAPI/Voting/VotingUtils.cs | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/MiraAPI/Hud/CustomPlayerMenu.cs b/MiraAPI/Hud/CustomPlayerMenu.cs index 56d9f0e..bbe63a9 100644 --- a/MiraAPI/Hud/CustomPlayerMenu.cs +++ b/MiraAPI/Hud/CustomPlayerMenu.cs @@ -96,7 +96,7 @@ public class CustomPlayerMenu(IntPtr il2CppPtr) : Minigame(il2CppPtr) onClick(null); })); - DestroyableSingleton.Instance.Analytics.MinigameOpened(PlayerControl.LocalPlayer.Data, TaskType); + DebugAnalytics.Instance.Analytics.MinigameOpened(PlayerControl.LocalPlayer.Data, TaskType); var list = PlayerControl.AllPlayerControls.ToArray().Where(playerMatch).ToList(); potentialVictims = []; var list2 = new Il2CppSystem.Collections.Generic.List(); diff --git a/MiraAPI/Patches/DetectiveNotesMinigamePatch.cs b/MiraAPI/Patches/DetectiveNotesMinigamePatch.cs index b0e37e7..a11ec7e 100644 --- a/MiraAPI/Patches/DetectiveNotesMinigamePatch.cs +++ b/MiraAPI/Patches/DetectiveNotesMinigamePatch.cs @@ -26,7 +26,7 @@ public static class DetectiveNotesMinigamePatch return; } __instance.impostorButton = new List(); - var allRoles = DestroyableSingleton.Instance.AllRoles; + var allRoles = RoleManager.Instance.AllRoles; var customRoles = CustomRoleManager.CustomRoleBehaviours; foreach (var role in allRoles) { diff --git a/MiraAPI/Patches/LocalSettings/ToggleBehaviourPatch.cs b/MiraAPI/Patches/LocalSettings/ToggleBehaviourPatch.cs index ffbfdea..06d308b 100644 --- a/MiraAPI/Patches/LocalSettings/ToggleBehaviourPatch.cs +++ b/MiraAPI/Patches/LocalSettings/ToggleBehaviourPatch.cs @@ -21,8 +21,8 @@ public static class ToggleBehaviourPatch // Used to mask the text __instance.Text.richText = true; __instance.Text.text = $"" + - $"{DestroyableSingleton.Instance.GetString(__instance.BaseText)}: " + - $"{DestroyableSingleton.Instance.GetString(__instance.onState ? StringNames.SettingsOn : StringNames.SettingsOff)}"; + $"{TranslationController.Instance.GetString(__instance.BaseText)}: " + + $"{TranslationController.Instance.GetString(__instance.onState ? StringNames.SettingsOn : StringNames.SettingsOff)}"; __instance.Text.sortingOrder = 151; __instance.Text.renderer.sortingLayerName = "Default"; __instance.Text.renderer.sortingOrder = 151; diff --git a/MiraAPI/Patches/Menu/NameplatesTabPatches.cs b/MiraAPI/Patches/Menu/NameplatesTabPatches.cs index 0875cb2..ca4b79d 100644 --- a/MiraAPI/Patches/Menu/NameplatesTabPatches.cs +++ b/MiraAPI/Patches/Menu/NameplatesTabPatches.cs @@ -51,7 +51,7 @@ public static class NameplatesTabPatches } __instance.plateId = HatManager.Instance.GetNamePlateById(DataManager.Player.Customization.namePlate).ProdId; - if (!SortedNameplates.ContainsKey("Vanilla")) AddRange(DestroyableSingleton.Instance.GetUnlockedNamePlates().Select(x => ("Vanilla", x))); + if (!SortedNameplates.ContainsKey("Vanilla")) AddRange(HatManager.Instance.GetUnlockedNamePlates().Select(x => ("Vanilla", x))); InventoryUtility.CreateNextBackButtons(__instance, PreviousPage, NextPage); diff --git a/MiraAPI/Patches/Menu/VisorsTabPatches.cs b/MiraAPI/Patches/Menu/VisorsTabPatches.cs index 86bfa24..8d50ece 100644 --- a/MiraAPI/Patches/Menu/VisorsTabPatches.cs +++ b/MiraAPI/Patches/Menu/VisorsTabPatches.cs @@ -51,7 +51,7 @@ public static class VisorsTabPatches } __instance.visorId = HatManager.Instance.GetVisorById(DataManager.Player.Customization.Visor).ProdId; - if (!SortedVisors.ContainsKey("Vanilla")) AddRange(DestroyableSingleton.Instance.GetUnlockedVisors().Select(x => ("Vanilla", x))); + if (!SortedVisors.ContainsKey("Vanilla")) AddRange(HatManager.Instance.GetUnlockedVisors().Select(x => ("Vanilla", x))); InventoryUtility.CreateNextBackButtons(__instance, PreviousPage, NextPage); diff --git a/MiraAPI/Utilities/Assets/AddressablesLoader.cs b/MiraAPI/Utilities/Assets/AddressablesLoader.cs index f1ecddd..a309ed3 100644 --- a/MiraAPI/Utilities/Assets/AddressablesLoader.cs +++ b/MiraAPI/Utilities/Assets/AddressablesLoader.cs @@ -171,24 +171,24 @@ public static class AddressablesLoader var visorBehaviours = DiscoverAndReportData(RegisteredVisorKeys); var hatData = new List(); - hatData.AddRange(DestroyableSingleton.Instance.allHats); + hatData.AddRange(HatManager.Instance.allHats); hatData.ForEach(x => x.StoreName = "Vanilla"); - DestroyableSingleton.Instance.allHats = PrepareArray(hatData, hatBehaviours); + HatManager.Instance.allHats = PrepareArray(hatData, hatBehaviours); var skinData = new List(); - skinData.AddRange(DestroyableSingleton.Instance.allSkins); + skinData.AddRange(HatManager.Instance.allSkins); skinData.ForEach(x => x.StoreName = "Vanilla"); - DestroyableSingleton.Instance.allSkins = PrepareArray(skinData, skinBehaviours); + HatManager.Instance.allSkins = PrepareArray(skinData, skinBehaviours); var visorData = new List(); - visorData.AddRange(DestroyableSingleton.Instance.allVisors); + visorData.AddRange(HatManager.Instance.allVisors); VisorsTabPatches.AddRange(visorBehaviours); - DestroyableSingleton.Instance.allVisors = PrepareArray(visorData, visorBehaviours.Select(x=>x.Data).ToList()); + HatManager.Instance.allVisors = PrepareArray(visorData, visorBehaviours.Select(x=>x.Data).ToList()); var namePlateData = new List(); - namePlateData.AddRange(DestroyableSingleton.Instance.allNamePlates); + namePlateData.AddRange(HatManager.Instance.allNamePlates); NameplatesTabPatches.AddRange(namePlateBehaviours); - DestroyableSingleton.Instance.allNamePlates = PrepareArray(namePlateData, namePlateBehaviours.Select(x => x.Data).ToList()); + HatManager.Instance.allNamePlates = PrepareArray(namePlateData, namePlateBehaviours.Select(x => x.Data).ToList()); } private static T[] PrepareArray(List data, List behaviours) where T : CosmeticData diff --git a/MiraAPI/Utilities/Extensions.cs b/MiraAPI/Utilities/Extensions.cs index 9c6faa7..882a15c 100644 --- a/MiraAPI/Utilities/Extensions.cs +++ b/MiraAPI/Utilities/Extensions.cs @@ -73,7 +73,7 @@ public static class Extensions } self.amClosing = Minigame.CloseState.Closing; self.logger.Info(string.Concat("Closing minigame ", self.GetType().Name)); - IAnalyticsReporter analytics = DestroyableSingleton.Instance.Analytics; + IAnalyticsReporter analytics = DebugAnalytics.Instance.Analytics; NetworkedPlayerInfo data = PlayerControl.LocalPlayer.Data; TaskTypes taskType = self.TaskType; float realtimeSinceStartup = Time.realtimeSinceStartup - self.timeOpened; diff --git a/MiraAPI/Voting/VotingUtils.cs b/MiraAPI/Voting/VotingUtils.cs index 0edd081..716b712 100644 --- a/MiraAPI/Voting/VotingUtils.cs +++ b/MiraAPI/Voting/VotingUtils.cs @@ -208,7 +208,7 @@ public static class VotingUtils } MeetingHud.Instance.TitleText.text = - DestroyableSingleton.Instance.GetString( + TranslationController.Instance.GetString( StringNames.MeetingVotingResults, Il2CppSystem.Array.Empty());