Please disclose if your mod was created primarily using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ResourceUnloadOptimizer v1.0.4
ResourceUnloadOptimizer.dll
Decompiled 2 years agousing System; using System.Collections; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using JetBrains.Annotations; using Microsoft.CodeAnalysis; using MonoMod.RuntimeDetour; using ResourceUnloadOptimizer; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ResourceUnloadOptimizer")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Azumatt")] [assembly: AssemblyProduct("ResourceUnloadOptimizer")] [assembly: AssemblyCopyright("Copyright © 2022")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("E0E2F92E-557C-4A05-9D89-AA92A0BD75C4")] [assembly: AssemblyFileVersion("1.0.4")] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.4.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace ResourceUnloadOptimizer { [BepInPlugin("Azumatt.ResourceUnloadOptimizer", "ResourceUnloadOptimizer", "1.0.4")] public class ResourceUnloadOptimizerPlugin : BaseUnityPlugin { public enum Toggle { On = 1, Off = 0 } private static class Hooks { [HarmonyPrefix] [HarmonyPatch(typeof(GC), "Collect", new Type[] { })] public static bool GCCollectHook() { _garbageCollect = 3; return false; } public static AsyncOperation UnloadUnusedAssetsHook() { if (DisableUnload.Value != Toggle.On) { return RunUnloadAssets(); } return null; } } private class ConfigurationManagerAttributes { [UsedImplicitly] public int? Order; [UsedImplicitly] public bool? Browsable; [UsedImplicitly] public string? Category; [UsedImplicitly] public Action<ConfigEntryBase>? CustomDrawer; } internal const string ModName = "ResourceUnloadOptimizer"; internal const string ModVersion = "1.0.4"; internal const string Author = "Azumatt"; private const string ModGUID = "Azumatt.ResourceUnloadOptimizer"; private static string ConfigFileName = "Azumatt.ResourceUnloadOptimizer.cfg"; private static string ConfigFileFullPath; private readonly Harmony _harmony = new Harmony("Azumatt.ResourceUnloadOptimizer"); public static readonly ManualLogSource ResourceUnloadOptimizerLogger; private static AsyncOperation _currentOperation; private static Func<AsyncOperation> _originalUnload; private static int _garbageCollect; private float _waitTime; private static ConfigEntry<Toggle> DisableUnload; private static ConfigEntry<Toggle> OptimizeMemoryUsage; private static ConfigEntry<int> PercentMemoryThreshold; public void Awake() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 if ((int)SystemInfo.graphicsDeviceType == 4) { ResourceUnloadOptimizerLogger.LogWarning((object)"This plugin is not fully designed or beneficial on a server. Please install only on the client. Not patching any code."); return; } DisableUnload = config("1 - General", "DisableUnload", Toggle.Off, "Disable the unloading of all resources. Requires large amounts of RAM or will likely crash your game. NOT RECOMMENDED FOR NORMAL USE."); OptimizeMemoryUsage = config("1 - General", "OptimizeMemoryUsage", Toggle.On, "Use more memory (if available) in order to load the game faster and reduce random stuttering."); PercentMemoryThreshold = config("1 - General", "PercentMemoryThreshold", 75, "Minimum amount of memory to be used before resource unloading will run."); InstallHooks(); ((MonoBehaviour)this).StartCoroutine(CleanupCo()); Assembly executingAssembly = Assembly.GetExecutingAssembly(); _harmony.PatchAll(executingAssembly); SetupWatcher(); } private static void InstallHooks() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) MethodInfo methodInfo = AccessTools.Method(typeof(Resources), "UnloadUnusedAssets", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(Hooks), "UnloadUnusedAssetsHook", (Type[])null, (Type[])null); NativeDetour val = new NativeDetour((MethodBase)methodInfo, (MethodBase)methodInfo2); val.Apply(); _originalUnload = val.GenerateTrampoline<Func<AsyncOperation>>(); } private IEnumerator CleanupCo() { while (true) { if (Time.realtimeSinceStartup < _waitTime) { yield return null; continue; } _waitTime = Time.realtimeSinceStartup + 1f; if (_garbageCollect > 0 && --_garbageCollect == 0) { RunGarbageCollect(); } } } private static AsyncOperation RunUnloadAssets() { if (_currentOperation == null || (_currentOperation.isDone && !PlentyOfMemory())) { ResourceUnloadOptimizerLogger.LogDebug((object)"Starting unused asset cleanup"); _currentOperation = _originalUnload(); } return _currentOperation; } private static void RunGarbageCollect() { if (!PlentyOfMemory()) { ResourceUnloadOptimizerLogger.LogDebug((object)"Starting full garbage collection"); GC.Collect(GC.MaxGeneration); } } private static bool PlentyOfMemory() { if (OptimizeMemoryUsage.Value == Toggle.Off) { return false; } MemoryInfo.MEMORYSTATUSEX currentStatus = MemoryInfo.GetCurrentStatus(); if (currentStatus == null) { return false; } float num = (float)currentStatus.ullAvailPageFile / (float)currentStatus.ullTotalPageFile; if (currentStatus.dwMemoryLoad >= PercentMemoryThreshold.Value || !(num > 0.3f) || currentStatus.ullAvailPageFile <= 2147483648u) { return false; } ResourceUnloadOptimizerLogger.LogDebug((object)$"Skipping cleanup because of low memory load ({currentStatus.dwMemoryLoad}% RAM, {100 - (int)(num * 100f)}% Page file, {currentStatus.ullAvailPageFile / 1024 / 1024}MB available in PF)"); return true; } private void OnDestroy() { ((BaseUnityPlugin)this).Config.Save(); } private void SetupWatcher() { FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName); fileSystemWatcher.Changed += ReadConfigValues; fileSystemWatcher.Created += ReadConfigValues; fileSystemWatcher.Renamed += ReadConfigValues; fileSystemWatcher.IncludeSubdirectories = true; fileSystemWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; fileSystemWatcher.EnableRaisingEvents = true; } private void ReadConfigValues(object sender, FileSystemEventArgs e) { if (!File.Exists(ConfigFileFullPath)) { return; } try { ResourceUnloadOptimizerLogger.LogDebug((object)"ReadConfigValues called"); ((BaseUnityPlugin)this).Config.Reload(); } catch { ResourceUnloadOptimizerLogger.LogError((object)("There was an issue loading your " + ConfigFileName)); ResourceUnloadOptimizerLogger.LogError((object)"Please check your config entries for spelling and format!"); } } private ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description) { return ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, description); } private ConfigEntry<T> config<T>(string group, string name, T value, string description) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>())); } static ResourceUnloadOptimizerPlugin() { string configPath = Paths.ConfigPath; char directorySeparatorChar = Path.DirectorySeparatorChar; ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName; ResourceUnloadOptimizerLogger = Logger.CreateLogSource("ResourceUnloadOptimizer"); DisableUnload = null; OptimizeMemoryUsage = null; PercentMemoryThreshold = null; } } } namespace BepInEx { internal static class MemoryInfo { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public class MEMORYSTATUSEX { public uint dwLength; public uint dwMemoryLoad; public ulong ullTotalPhys; public ulong ullAvailPhys; public ulong ullTotalPageFile; public ulong ullAvailPageFile; public ulong ullTotalVirtual; public ulong ullAvailVirtual; public ulong ullAvailExtendedVirtual; public MEMORYSTATUSEX() { dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX)); } } public static MEMORYSTATUSEX GetCurrentStatus() { try { MEMORYSTATUSEX mEMORYSTATUSEX = new MEMORYSTATUSEX(); if (GlobalMemoryStatusEx(mEMORYSTATUSEX)) { return mEMORYSTATUSEX; } return null; } catch (Exception ex) { ResourceUnloadOptimizerPlugin.ResourceUnloadOptimizerLogger.LogError((object)ex); return null; } } [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GlobalMemoryStatusEx([In][Out] MEMORYSTATUSEX lpBuffer); } }