using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using BepInEx; using Dungeonator; using MonoMod.RuntimeDetour; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("TheGarbageCollector")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Apache Modding Inc.")] [assembly: AssemblyProduct("TheGarbageCollector")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("42932dec-6cbb-4428-aab7-35c7bee8110b")] [assembly: AssemblyFileVersion("1.4.5")] [assembly: AssemblyVersion("1.4.5.0")] namespace TheGarbageCollector; public class AudioResourceLoader { public static uint LoadedSoundBankID = 0u; public static readonly string BankName = "GCSFX.bnk"; public static bool InitAudio() { return LoadBankFromModProject("TheGarbageCollector." + BankName, out LoadedSoundBankID); } public static bool LoadBankFromModProject(string path, out uint bankid) { path = path.Replace("/", ".").Replace("\\", "."); if (!path.EndsWith(".bnk")) { path += ".bnk"; } Assembly callingAssembly = Assembly.GetCallingAssembly(); bankid = 0u; using (Stream stream = callingAssembly.GetManifestResourceStream(path)) { if (stream != null) { string text = path.Substring(0, path.LastIndexOf('.')); string bankName = path; if (text.LastIndexOf('.') >= 0) { bankName = text.Substring(text.LastIndexOf('.') + 1); } return LoadSoundbankFromStream(stream, bankName, out bankid); } } return false; } private static bool LoadSoundbankFromStream(Stream stream, string bankName, out uint bankID) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Invalid comparison between Unknown and I4 byte[] array = StreamToByteArray(stream); IntPtr intPtr = Marshal.AllocHGlobal(array.Length); AKRESULT val = (AKRESULT)2; bankID = 0u; try { Marshal.Copy(array, 0, intPtr, array.Length); val = AkSoundEngine.LoadAndDecodeBankFromMemory(intPtr, (uint)array.Length, false, bankName, false, ref bankID); } finally { Marshal.FreeHGlobal(intPtr); } return (int)val == 1; } private static byte[] StreamToByteArray(Stream input) { byte[] array = new byte[16384]; using MemoryStream memoryStream = new MemoryStream(); int count; while ((count = input.Read(array, 0, array.Length)) > 0) { memoryStream.Write(array, 0, count); } return memoryStream.ToArray(); } } public class GCFoyer : BraveBehaviour { private enum State { PreFoyerCheck, EnableMod, Exit } private State m_State; public GCFoyer() { m_State = State.PreFoyerCheck; } public void Update() { switch (m_State) { case State.PreFoyerCheck: if (!Foyer.DoIntroSequence || !Foyer.DoMainMenu) { TheGarbageCollector.AllowModEnable = true; m_State = State.EnableMod; } break; case State.EnableMod: { int @int = PlayerPrefs.GetInt(TheGarbageCollector.GarbageCollectorMemoryCap); if (@int >= 1024) { GC_Manager.MemoryGrowthAllowence = @int; if (@int >= 8196) { TheGarbageCollector.disableMonitor = true; } } else { GC_Manager.MemoryGrowthAllowence = TheGarbageCollector.GetBestMemoryCap; if (SystemInfo.systemMemorySize > 24576) { TheGarbageCollector.disableMonitor = true; } } if (PlayerPrefs.GetInt(TheGarbageCollector.GarbageCollectorToggleName) == 1) { TheGarbageCollector.DisableGC = false; break; } if (TheGarbageCollector.DisableGC) { ETGModConsole.Log((object)(TheGarbageCollector.ModNameGreen + "TheGarbageCollector is currently enabled. Use command 'garbagecolletor toggle' disable the mod."), false); if (GC_Manager.load_mono_gc()) { TheGarbageCollector.ToggleHooksAndGC(state: true); if (SystemInfo.systemMemorySize < 8196) { ETGModConsole.Log((object)(TheGarbageCollector.ModNameGreen + "Warning: Your computer was detected as having 8GB or less ram. It is recommended only to enable this mod on machines with more then 8GB of ram!"), true); } } } else { ETGModConsole.Log((object)(TheGarbageCollector.ModNameGreen + "TheGarbageCollector is currently disabled. Use command 'garbagecolletor toggle' to enable TheGarbageCollector and disable Unity's GarbageCollector."), false); } m_State = State.Exit; break; } case State.Exit: if (Object.op_Implicit((Object)(object)((Component)this).gameObject)) { Object.Destroy((Object)(object)((Component)this).gameObject); } break; } } protected override void OnDestroy() { ((BraveBehaviour)this).OnDestroy(); } } public class GCStats : MonoBehaviour { public static GameObject GCStatsObject; public bool ShowGcData; public Vector3 UIPosition; public StringBuilder stringBuilder; private dfLabel m_label; private bool IsRedText; private bool WaitingForCollection; private Color32 WhiteColor; private Color32 RedColor; private float AllocationLimit; public static GCStats Instance { get { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if (!Object.op_Implicit((Object)(object)GCStatsObject)) { GCStatsObject = new GameObject("GCStats") { layer = 14 }; Object.DontDestroyOnLoad((Object)(object)GCStatsObject); } return GameObjectExtensions.GetOrAddComponent(GCStatsObject); } } public GCStats() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) ShowGcData = false; UIPosition = new Vector3(-710f, 130f, 0f); RedColor = new Color32((byte)190, (byte)160, (byte)0, byte.MaxValue); WhiteColor = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); IsRedText = false; WaitingForCollection = false; } private void SetupLabel() { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Expected O, but got Unknown //IL_027e: Unknown result type (might be due to invalid IL or missing references) GameUIRoot instance = GameUIRoot.Instance; if (Object.op_Implicit((Object)(object)((instance != null) ? instance.Manager : null))) { AssetBundle obj = ResourceManager.LoadAssetBundle("shared_auto_001"); dfTiledSprite component = obj.LoadAsset("Weapon Skull Ammo FG").GetComponent(); dfFont component2 = obj.LoadAsset("04b03_df40").GetComponent(); dfLabel val = GameUIRoot.Instance.Manager.AddControl(); val.Atlas = ((dfSprite)component).Atlas; val.Font = (dfFontBase)(object)component2; ((dfControl)val).Anchor = (dfAnchorStyle)6; ((dfControl)val).IsEnabled = true; ((dfControl)val).IsVisible = false; ((dfControl)val).IsInteractive = true; ((dfControl)val).Tooltip = string.Empty; ((dfControl)val).Pivot = (dfPivotPoint)6; ((dfControl)val).zindex = 29; ((dfControl)val).Opacity = 0.5f; ((dfControl)val).Color = Color32.op_Implicit(Color.white); ((dfControl)val).DisabledColor = new Color32((byte)128, (byte)128, (byte)128, byte.MaxValue); ((dfControl)val).Size = new Vector2(350f, 120f); ((dfControl)val).MinimumSize = ((dfControl)val).Size; ((dfControl)val).MaximumSize = new Vector2(400f, 240f); ((dfControl)val).ClipChildren = false; ((dfControl)val).InverseClipChildren = false; ((dfControl)val).TabIndex = -1; ((dfControl)val).CanFocus = false; ((dfControl)val).AutoFocus = false; ((dfControl)val).IsLocalized = false; ((dfControl)val).HotZoneScale = Vector2.one; ((dfControl)val).AllowSignalEvents = true; ((dfControl)val).PrecludeUpdateCycle = false; val.PerCharacterOffset = Vector2.op_Implicit(Vector2.zero); val.PreventFontChanges = true; val.BackgroundSprite = string.Empty; val.BackgroundColor = Color32.op_Implicit(Color.white); val.AutoSize = true; val.AutoHeight = false; val.WordWrap = false; val.Text = "PLACEHOLDER"; val.BottomColor = Color32.op_Implicit(Color.white); val.TextAlignment = (TextAlignment)0; val.VerticalAlignment = (dfVerticalAlignment)0; val.TextScale = 0.5f; val.TextScaleMode = (dfTextScaleMode)0; val.CharacterSpacing = 0; val.ColorizeSymbols = false; val.ProcessMarkup = false; val.Outline = false; val.OutlineSize = 0; val.ShowGradient = false; val.OutlineColor = Color32.op_Implicit(Color.black); val.Shadow = false; val.ShadowColor = Color32.op_Implicit(Color.black); val.ShadowOffset = new Vector2(1f, -1f); val.Padding = new RectOffset { left = 0, right = 0, top = 0, bottom = 0 }; val.TabSize = 48; val.MaintainJapaneseFont = false; val.MaintainKoreanFont = false; val.MaintainRussianFont = false; ((dfControl)val).Position = UIPosition; m_label = val; component2 = null; component = null; } } private void Start() { stringBuilder = new StringBuilder(500); AllocationLimit = GC_Manager.MemoryGrowthAllowence; } protected void Update() { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) if (ShowGcData && GC_Manager.d_gc_disabled) { if (!Object.op_Implicit((Object)(object)m_label)) { SetupLabel(); } if (!Object.op_Implicit((Object)(object)m_label)) { return; } if (!((dfControl)m_label).IsVisible) { ((dfControl)m_label).IsVisible = true; } if (GC_Manager.Instance.AllocatedMemorySinceLastCollection != -1) { AllocationLimit = Mathf.Max(AllocationLimit, (float)GC_Manager.Instance.AllocatedMemorySinceLastCollection); } if ((float)GC_Manager.GetTotalMemoryAllocatedInMB >= AllocationLimit) { if (!WaitingForCollection) { WaitingForCollection = true; } if (!IsRedText) { ((dfControl)m_label).Color = RedColor; IsRedText = true; } } else { if (WaitingForCollection) { WaitingForCollection = false; } if (IsRedText) { ((dfControl)m_label).Color = WhiteColor; IsRedText = false; } } stringBuilder.Length = 0; stringBuilder.AppendFormat("Current Allocation: {0:0} MB / {1:0} MB\n", GC_Manager.GetTotalMemoryAllocatedInMB, AllocationLimit); stringBuilder.AppendFormat("Current Heap Size: {0:0} MB\n", ProfileUtils.GetMonoHeapSize() / 1024 / 1024); stringBuilder.AppendFormat("Time Since Last In Combat: {0: 00} sec\n", Time.realtimeSinceStartup - GC_Manager.Instance.LastInCombatTime); if (Time.realtimeSinceStartup - GC_Manager.Instance.LastUnpausedTime > 0.1f) { stringBuilder.AppendFormat("Time Spent In Pause Screen: {0: 00} sec\n", Time.realtimeSinceStartup - GC_Manager.Instance.LastUnpausedTime); } stringBuilder.AppendFormat("Time Since Last Collection: {0: 00} sec\n", Time.realtimeSinceStartup - GC_Manager.Instance.LastCollectionTime); stringBuilder.AppendFormat("Total Collections: {0}\n", ProfileUtils.GetMonoCollectionCount()); if (WaitingForCollection) { stringBuilder.Append("Waiting to do a Collection...\n"); } else if (TheGarbageCollector.disableMonitor) { stringBuilder.Append("GC Monitor is Disabled.\n"); } m_label.Text = stringBuilder.ToString(); } else if (!ShowGcData && GC_Manager.d_gc_disabled && Object.op_Implicit((Object)(object)m_label) && ((dfControl)m_label).IsVisible) { ((dfControl)m_label).IsVisible = false; } } } public class GC_Manager : MonoBehaviour { public static FieldInfo LastGcTime = typeof(BraveMemory).GetField("LastGcTime", BindingFlags.Static | BindingFlags.NonPublic); private static FieldInfo m_stringBuilder = typeof(HUDGC).GetField("stringBuilder", BindingFlags.Instance | BindingFlags.NonPublic); public static int offset_mono_gc_disable = 111376; public static int offset_mono_gc_enable = 111384; public static int offset_mono_gc_collect = 111300; public static int MemoryGrowthAllowence = 2048; public long AllocatedMemorySinceLastCollection; public static Action mono_gc_disable; public static Action mono_gc_enable; public static Action mono_gc_collect; public static bool d_gc_disabled = false; private static bool mono_gc_loaded = false; public bool turn_off_mono_gc; public bool CanDoCollectionNow; private bool DoManualCollection; public float PauseTimeTillManualCollection; public float OutOfCombatTimeTillManualCollection; public float LastUnpausedTime; public float LastInCombatTime; public float LastCollectionTime; private float AllocationLimit; private int[] dummy_object; public static GC_Manager Instance { get { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown GameObject gCManagerObject = TheGarbageCollector.GCManagerObject; if (!Object.op_Implicit((Object)(object)((gCManagerObject != null) ? gCManagerObject.GetComponent() : null)) && !Object.op_Implicit((Object)(object)TheGarbageCollector.GCManagerObject)) { TheGarbageCollector.GCManagerObject = new GameObject("TheGarbageCollector"); Object.DontDestroyOnLoad((Object)(object)TheGarbageCollector.GCManagerObject); } return GameObjectExtensions.GetOrAddComponent(TheGarbageCollector.GCManagerObject); } } public static long GetTotalMemoryAllocatedInMB => GC.GetTotalMemory(forceFullCollection: false) / 1024 / 1024; public GC_Manager() { turn_off_mono_gc = false; LastInCombatTime = 0f; LastUnpausedTime = 0f; DoManualCollection = false; CanDoCollectionNow = false; PauseTimeTillManualCollection = 120f; OutOfCombatTimeTillManualCollection = 300f; AllocatedMemorySinceLastCollection = -1L; } [DllImport("kernel32.dll", CharSet = CharSet.Auto)] public static extern IntPtr GetModuleHandle(string lpModuleName); [DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); public static bool load_mono_gc() { if (mono_gc_loaded) { return true; } IntPtr moduleHandle = GetModuleHandle("mono.dll"); IntPtr intPtr = new IntPtr(moduleHandle.ToInt64() + offset_mono_gc_collect); IntPtr procAddress = GetProcAddress(moduleHandle, "mono_gc_collect"); if (intPtr != procAddress) { ETGModConsole.Log((object)(TheGarbageCollector.ModNameRed + "Cannot load GarbageCollector functions. Expected mono's collect at " + intPtr.ToInt64() + " Actual at " + intPtr.ToInt64() + " Module root " + moduleHandle.ToInt64()), true); return false; } mono_gc_enable = (Action)Marshal.GetDelegateForFunctionPointer(new IntPtr(moduleHandle.ToInt64() + offset_mono_gc_enable), typeof(Action)); mono_gc_disable = (Action)Marshal.GetDelegateForFunctionPointer(new IntPtr(moduleHandle.ToInt64() + offset_mono_gc_disable), typeof(Action)); mono_gc_collect = (Action)Marshal.GetDelegateForFunctionPointer(new IntPtr(moduleHandle.ToInt64() + offset_mono_gc_collect), typeof(Action)); mono_gc_loaded = true; return true; } public void Disable() { mono_gc_enable(); d_gc_disabled = false; turn_off_mono_gc = false; } public void Enable() { mono_gc_disable(); d_gc_disabled = true; turn_off_mono_gc = true; } public void DoCollect() { if (d_gc_disabled) { Collect(); return; } LastGcTime.SetValue(typeof(BraveMemory), Time.realtimeSinceStartup); GC.Collect(); } private void Collect() { if (GCStats.Instance.ShowGcData && !Dungeon.IsGenerating && Object.op_Implicit((Object)(object)GameManager.Instance) && !GameManager.Instance.IsLoadingLevel && Object.op_Implicit((Object)(object)((Component)ETGModMainBehaviour.Instance).gameObject)) { AkSoundEngine.PostEvent(TheGarbageCollector.ModSoundEventName, ((Component)ETGModMainBehaviour.Instance).gameObject); } int monoCollectionCount = ProfileUtils.GetMonoCollectionCount(); mono_gc_enable(); for (int i = 0; i < 100; i++) { dummy_object = new int[1]; dummy_object[0] = 0; } if (ProfileUtils.GetMonoCollectionCount() == monoCollectionCount) { LastGcTime.SetValue(null, Time.realtimeSinceStartup); LastCollectionTime = Time.realtimeSinceStartup; GC.Collect(); } mono_gc_disable(); AllocatedMemorySinceLastCollection = GetTotalMemoryAllocatedInMB; if (GCStats.Instance.ShowGcData && !Dungeon.IsGenerating && Object.op_Implicit((Object)(object)GameManager.Instance) && !GameManager.Instance.IsLoadingLevel && GCStats.Instance.stringBuilder != null) { Debug.Log((object)"TheGarbageCollector Collection ran. Last GC Stats:"); Debug.Log((object)GCStats.Instance.stringBuilder); } } private void Start() { } protected void Update() { if (!d_gc_disabled | Dungeon.IsGenerating | (Object.op_Implicit((Object)(object)GameManager.Instance) && GameManager.Instance.IsLoadingLevel)) { return; } if (GameManager.Instance.IsPaused) { if (LastUnpausedTime > 0.1f && Time.realtimeSinceStartup - LastUnpausedTime > PauseTimeTillManualCollection) { DoManualCollection = true; LastUnpausedTime = Time.realtimeSinceStartup; } } else { DoManualCollection = false; LastUnpausedTime = Time.realtimeSinceStartup; } if (Object.op_Implicit((Object)(object)GameManager.Instance.PrimaryPlayer) && !GameManager.Instance.IsPaused) { if (GameManager.Instance.PrimaryPlayer.IsInCombat) { LastInCombatTime = Time.realtimeSinceStartup; DoManualCollection = false; } else if (LastInCombatTime > 0f && Time.realtimeSinceStartup - LastInCombatTime > OutOfCombatTimeTillManualCollection) { DoManualCollection = true; LastInCombatTime = Time.realtimeSinceStartup; } else { DoManualCollection = false; } } GameManager instance = GameManager.Instance; if (Object.op_Implicit((Object)(object)((instance != null) ? instance.PrimaryPlayer : null)) && GameManager.Instance.PrimaryPlayer.IsInCombat) { CanDoCollectionNow = false; } else { CanDoCollectionNow = true; } AllocationLimit = MemoryGrowthAllowence; if (AllocatedMemorySinceLastCollection != -1) { AllocationLimit = Mathf.Max(AllocationLimit, (float)AllocatedMemorySinceLastCollection); } if (DoManualCollection | (!TheGarbageCollector.disableMonitor && CanDoCollectionNow && (float)GetTotalMemoryAllocatedInMB >= AllocationLimit)) { Collect(); DoManualCollection = false; CanDoCollectionNow = false; } } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("ApacheThunder.etg.TheGarbageCollector", "TheGarbageCollector", "1.4.5")] public class TheGarbageCollector : BaseUnityPlugin { public static bool DisableGC = true; public static bool disableMonitor = false; public static bool AllowModEnable = false; public const string GUID = "ApacheThunder.etg.TheGarbageCollector"; public const string ModName = "TheGarbageCollector"; public const string VERSION = "1.4.5"; public static readonly string ConsoleCommandName = "garbagecollector"; public static readonly string GarbageCollectorToggleName = "TheGarbageCollectorDisabled"; public static readonly string GarbageCollectorMemoryCap = "TheGarbageCollectorMemCap"; public static readonly string ModNameGreen = "[TheGarbageCollector] "; public static readonly string ModNameRed = "[TheGarbageCollector] "; public static readonly string ModSoundEventName = "Play_TrashMan_01"; public static string FolderPath; public static Hook BraveMemoryCollectHook; public static Hook clearLevelDataHook; public static Hook gameManagerHook; public static GameObject GCManagerObject; public static GameObject GCFoyerCheckerOBJ; public static int GetBestMemoryCap { get { if (SystemInfo.systemMemorySize > 24576) { return 8196; } if (SystemInfo.systemMemorySize > 12288) { return 5120; } if (SystemInfo.systemMemorySize > 8196) { return 3072; } return 2048; } } public void Start() { ETGModMainBehaviour.WaitForGameManagerStart((Action)GMStart); } public void GMStart(GameManager gameManager) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 FolderPath = ETGMod.FolderPath((BaseUnityPlugin)(object)this); if ((int)Application.platform != 2) { ETGModConsole.Log((object)(ModNameRed + "ERROR: Linux/MacOS/Other version of the game detected!. This mod only supports the Windows version of ETG!"), false); return; } if (!AudioResourceLoader.InitAudio()) { ETGModConsole.Log((object)(ModNameRed + "WARNING: Failed to load custom sound bank!"), false); } ETGModConsole.Commands.AddGroup(ConsoleCommandName, (Action)ConsoleInfo); ETGModConsole.Commands.GetGroup(ConsoleCommandName).AddUnit("toggle", (Action)ToggleGCSetting); ETGModConsole.Commands.GetGroup(ConsoleCommandName).AddUnit("collect", (Action)DoACollect); ETGModConsole.Commands.GetGroup(ConsoleCommandName).AddUnit("stats", (Action)ToggleGCStats); ETGModConsole.Commands.GetGroup(ConsoleCommandName).AddUnit("setmemcap", (Action)SetMemoryCap); CreateFoyerController(); } public static void CreateFoyerController() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown if (!Object.op_Implicit((Object)(object)GCFoyerCheckerOBJ)) { GCFoyerCheckerOBJ = new GameObject("TheGarbageCollector Foyer Checker", new Type[1] { typeof(GCFoyer) }); } } private void GameManager_Awake(Action orig, GameManager self) { orig(self); self.OnNewLevelFullyLoaded += OnLevelFullyLoaded; } public static void OnLevelFullyLoaded() { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown if (DisableGC && Object.op_Implicit((Object)(object)GC_Manager.Instance) && GC_Manager.d_gc_disabled) { if (gameManagerHook == null) { gameManagerHook = new Hook((MethodBase)typeof(GameManager).GetMethod("Awake", BindingFlags.Instance | BindingFlags.NonPublic), typeof(TheGarbageCollector).GetMethod("GameManager_Awake", BindingFlags.Instance | BindingFlags.NonPublic), (object)typeof(GameManager)); } GC_Manager.Instance.DoCollect(); } } private void ConsoleInfo(string[] consoleText) { if (ETGModConsole.Commands.GetGroup(ConsoleCommandName) == null || ETGModConsole.Commands.GetGroup(ConsoleCommandName).GetAllUnitNames() == null) { return; } List list = new List(); string[] allUnitNames = ETGModConsole.Commands.GetGroup(ConsoleCommandName).GetAllUnitNames(); foreach (string item in allUnitNames) { list.Add(item); } if (list.Count <= 0) { return; } if (!m_IsCommandValid(consoleText, string.Empty, string.Empty)) { ETGModConsole.Log((object)(ModNameRed + "No sub command specified! The following console commands are available for TheGarbageCollector:\n"), false); { foreach (string item2 in list) { ETGModConsole.Log((object)(" " + item2 + "\n"), false); } return; } } if (list.Contains(consoleText[0].ToLower())) { return; } ETGModConsole.Log((object)(ModNameRed + "Invalid sub-command! The following console commands are available for TheGarbageCollector:\n"), false); foreach (string item3 in list) { ETGModConsole.Log((object)(" " + item3 + "\n"), false); } } private bool m_IsCommandValid(string[] CommandText, string validCommands, string sourceSubCommand) { if (CommandText == null) { if (!string.IsNullOrEmpty(validCommands) && !string.IsNullOrEmpty(sourceSubCommand)) { ETGModConsole.Log((object)(ModNameRed + "[" + sourceSubCommand + "] ERROR: Invalid console command specified! Valid Sub-Commands: \n" + validCommands), false); } return false; } if (CommandText.Length == 0) { if (!string.IsNullOrEmpty(validCommands) && !string.IsNullOrEmpty(sourceSubCommand)) { ETGModConsole.Log((object)(ModNameRed + "[" + sourceSubCommand + "] No sub-command specified. Valid Sub-Commands: \n" + validCommands), false); } return false; } if (string.IsNullOrEmpty(CommandText[0])) { if (!string.IsNullOrEmpty(validCommands) && !string.IsNullOrEmpty(sourceSubCommand)) { ETGModConsole.Log((object)(ModNameRed + "[" + sourceSubCommand + "] No sub-command specified. Valid Sub-Commands: \n" + validCommands), false); } return false; } if (CommandText.Length > 1) { if (!string.IsNullOrEmpty(validCommands) && !string.IsNullOrEmpty(sourceSubCommand)) { ETGModConsole.Log((object)(ModNameRed + "[" + sourceSubCommand + "] ERROR: Only one sub-command is accepted!. Valid Commands: \n" + validCommands), false); } return false; } return true; } private void ToggleGCSetting(string[] consoleText) { if (!AllowModEnable) { ETGModConsole.Log((object)(ModNameRed + "Warning: Please wait until the foyer is loaded before trying to enable/disable the mod!"), false); return; } if (!DisableGC) { DisableGC = true; if (GC_Manager.load_mono_gc()) { AkSoundEngine.PostEvent(ModSoundEventName, ((Component)ETGModMainBehaviour.Instance).gameObject); ToggleHooksAndGC(DisableGC); if (SystemInfo.systemMemorySize < 8196) { ETGModConsole.Log((object)(ModNameRed + "Warning: Your computer was detected as having 8GB or less ram. It is recommended only to use this feature on machines with more then 8GB of ram!"), false); } ETGModConsole.Log((object)(ModNameGreen + "TheGarbageCollector mod is now enabled.\nNow will only do collections during floor loads and if player is AFK or been in pause menu for more then 30 seconds!"), false); } PlayerPrefs.SetInt(GarbageCollectorToggleName, 0); } else { DisableGC = false; ToggleHooksAndGC(DisableGC); ETGModConsole.Log((object)(ModNameGreen + "TheGarbageCollector mod is now disabled.\nUnity's GC will now run normally!"), false); PlayerPrefs.SetInt(GarbageCollectorToggleName, 1); } PlayerPrefs.Save(); } private void ToggleGCStats(string[] consoleText) { if (GCStats.Instance.ShowGcData) { GCStats.Instance.ShowGcData = false; ETGModConsole.Log((object)(ModNameGreen + "GC Stats disabled!"), false); } else { GCStats.Instance.ShowGcData = true; ETGModConsole.Log((object)(ModNameGreen + "GC Stats enabled!"), false); } } private void DoACollect(string[] consoleText) { ETGModConsole.Log((object)(ModNameGreen + "Doing a Collection"), false); if (Object.op_Implicit((Object)(object)GameManager.Instance) && Object.op_Implicit((Object)(object)GameManager.Instance.PrimaryPlayer)) { AkSoundEngine.PostEvent(ModSoundEventName, ((Component)GameManager.Instance.PrimaryPlayer).gameObject); } else { AkSoundEngine.PostEvent(ModSoundEventName, ((Component)ETGModMainBehaviour.Instance).gameObject); } if (DisableGC) { GC_Manager.Instance.DoCollect(); } else { BraveMemory.DoCollect(); } } private void SetMemoryCap(string[] consoleText) { if (consoleText != null && consoleText.Length == 1) { int num = int.Parse(consoleText[0]); bool flag = false; if (num < 1024) { num = GetBestMemoryCap; flag = true; } if (num >= 8196) { disableMonitor = true; } else { disableMonitor = false; } GC_Manager.MemoryGrowthAllowence = num; PlayerPrefs.SetInt(GarbageCollectorMemoryCap, num); PlayerPrefs.Save(); if (flag) { ETGModConsole.Log((object)(ModNameGreen + "Memory allocation limit manually set. To allow this to be auto set again, set it to a value below 1024!"), false); } else { ETGModConsole.Log((object)(ModNameRed + "Requested memory cap is below minimum allowed value.\nMemory cap is now set to Auto based on your current system memory spec."), false); } } else { ETGModConsole.Log((object)(ModNameRed + "No memory value specified. Please specify a memory limit you want to use!"), false); } } public static void ToggleHooksAndGC(bool state) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Expected O, but got Unknown if (state) { GC_Manager.Instance.Enable(); if (BraveMemoryCollectHook == null) { BraveMemoryCollectHook = new Hook((MethodBase)typeof(BraveMemory).GetMethod("DoCollect", BindingFlags.Static | BindingFlags.Public), typeof(TheGarbageCollector).GetMethod("DoCollect", BindingFlags.Static | BindingFlags.Public)); } if (clearLevelDataHook == null) { clearLevelDataHook = new Hook((MethodBase)typeof(GameManager).GetMethod("ClearPerLevelData", BindingFlags.Instance | BindingFlags.Public), typeof(TheGarbageCollector).GetMethod("ClearPerLevelDataHook", BindingFlags.Instance | BindingFlags.Public), (object)typeof(GameManager)); } GameManager.Instance.OnNewLevelFullyLoaded += OnLevelFullyLoaded; return; } GC_Manager.Instance.Disable(); if (BraveMemoryCollectHook != null) { BraveMemoryCollectHook.Dispose(); BraveMemoryCollectHook = null; } if (clearLevelDataHook != null) { clearLevelDataHook.Dispose(); clearLevelDataHook = null; } if (gameManagerHook != null) { gameManagerHook.Dispose(); gameManagerHook = null; } GameManager.Instance.OnNewLevelFullyLoaded -= OnLevelFullyLoaded; } public void ClearPerLevelDataHook(Action orig, GameManager self) { orig(self); if (DisableGC && GC_Manager.d_gc_disabled && GC_Manager.load_mono_gc()) { GC_Manager.Instance.DoCollect(); } } public static void DoCollect() { if (!GC_Manager.d_gc_disabled || GameManager.Instance.IsLoadingLevel) { if (GC_Manager.d_gc_disabled) { GC_Manager.Instance.DoCollect(); return; } GC_Manager.LastGcTime.SetValue(typeof(BraveMemory), Time.realtimeSinceStartup); GC.Collect(); } } }