using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using BepInEx.Unity.IL2CPP.Utils.Collections; using CellMenu; using Enemies; using Expedition; using FX_EffectSystem; using GTFO.API; using GameData; using Gear; using HarmonyLib; using IRF; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using MemoryLeakFix.Handler; using MemoryLeakFix.Patches; using Microsoft.CodeAnalysis; using SNetwork; using UnityEngine; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("MemoryLeakFix")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+8c330a6f0f86b3abe29d46130ea461862a241d57")] [assembly: AssemblyProduct("MemoryLeakFix")] [assembly: AssemblyTitle("MemoryLeakFix")] [assembly: AssemblyVersion("1.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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 MemoryLeakFix { internal static class DinoLogger { private static ManualLogSource logger = Logger.CreateLogSource("MemoryLeakFix"); public static void Log(string format, params object[] args) { Log(string.Format(format, args)); } public static void Log(string str) { if (logger != null) { logger.Log((LogLevel)8, (object)str); } } public static void Warning(string format, params object[] args) { Warning(string.Format(format, args)); } public static void Warning(string str) { if (logger != null) { logger.Log((LogLevel)4, (object)str); } } public static void Error(string format, params object[] args) { Error(string.Format(format, args)); } public static void Error(string str) { if (logger != null) { logger.Log((LogLevel)2, (object)str); } } public static void Debug(string format, params object[] args) { Debug(string.Format(format, args)); } public static void Debug(string str) { if (logger != null) { logger.Log((LogLevel)32, (object)str); } } } [BepInPlugin("Dinorush.MemoryLeakFix", "MemoryLeakFix", "1.3.7")] [BepInDependency(/*Could not decode attribute arguments.*/)] internal sealed class EntryPoint : BasePlugin { public const string MODNAME = "MemoryLeakFix"; public override void Load() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) new Harmony("MemoryLeakFix").PatchAll(); ((BasePlugin)this).Log.LogMessage((object)"Loaded MemoryLeakFix"); LevelAPI.OnLevelCleanup += OnLevelCleanup; AssetAPI.OnStartupAssetsLoaded += OnAssetsLoaded; } private void OnLevelCleanup() { if (Decay.s_poolHandle != null) { Decay.s_poolHandle.Clear(); } FallingObjectHandler.Clear(); MapPatches.OnCleanup(); } private void OnAssetsLoaded() { FallingObjectHandler.Current.EnsureInit(); } } } namespace MemoryLeakFix.Patches { internal static class CellMenuPatches { [HarmonyPatch(typeof(CM_ScrollWindow), "ResetHeaders")] [HarmonyPostfix] private static void Post_ResetHeaders(CM_ScrollWindow __instance) { __instance.m_nonContentItems?.Clear(); } } [HarmonyPatch] internal static class DebrisPatches { [HarmonyPatch(typeof(DebrisSpawner), "RunSequence", new Type[] { typeof(Transform) })] [HarmonyPatch(typeof(DebrisSpawner), "RunSequence", new Type[] { })] [HarmonyPostfix] private static void Post_RunSequence(DebrisSpawner __instance) { Enumerator enumerator = __instance.m_destructionObjects.GetEnumerator(); while (enumerator.MoveNext()) { CoroutineManager.StartCoroutine(CollectionExtensions.WrapToIl2Cpp(DelayedHide(enumerator.Current)), (Action)null); } } private static IEnumerator DelayedHide(DestructionObjects objects) { yield return (object)new WaitForSeconds(Random.Range(3f, 5f)); GameObject destructionObject = objects.m_DestructionObject; MeshCollider component = destructionObject.GetComponent(); if ((Object)(object)component != (Object)null) { ((Collider)component).enabled = false; } Rigidbody component2 = destructionObject.GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.isKinematic = true; component2.detectCollisions = false; } } } [HarmonyPatch] internal static class DecayPatches { private const float DecayClearDelay = 5f; [HarmonyPatch(typeof(Decay), "Initialize", new Type[] { typeof(SkinnedMeshRenderer), typeof(List) })] [HarmonyPostfix] private static void AddEndCallback(Decay __instance) { Decay obj = __instance; obj.OnDecaySafeToDespawnRenderer += Action.op_Implicit((Action)delegate { ((MonoBehaviour)__instance).StartCoroutine(CollectionExtensions.WrapToIl2Cpp(DelayedClear(__instance))); }); } private static IEnumerator DelayedClear(Decay __instance) { yield return (object)new WaitForSeconds(5f); if ((Object)(object)__instance.m_particles != (Object)null) { __instance.m_particles.Stop(true, (ParticleSystemStopBehavior)0); } __instance.m_playing = true; } } [HarmonyPatch] internal static class FallingPatches { [HarmonyPatch(typeof(GlueClusterGrenadeInstance), "Start")] [HarmonyPostfix] private static void GlueGrenadeSpawn(GlueClusterGrenadeInstance __instance) { FallingObjectHandler.AddObject(((Component)__instance).gameObject); } [HarmonyPatch(typeof(GlowstickInstance), "Setup")] [HarmonyPostfix] private static void GlowstickSpawn(GlowstickInstance __instance) { FallingObjectHandler.AddObject(((Component)__instance).gameObject); } [HarmonyPatch(typeof(FogRepellerInstance), "Start")] [HarmonyPostfix] private static void FogRepellerSpawn(FogRepellerInstance __instance) { FallingObjectHandler.AddObject(((Component)__instance).gameObject); } [HarmonyPatch(typeof(GlueGunProjectile), "Awake")] [HarmonyPostfix] private static void GlueGunSpawn(GlueGunProjectile __instance) { FallingObjectHandler.AddObject(((Component)__instance).gameObject, delegate { if ((Object)(object)__instance != (Object)null) { ProjectileManager.WantToDestroyGlue(__instance.SyncID); } if ((Object)(object)__instance != (Object)null) { __instance.SyncDestroy(); } }); } [HarmonyPatch(typeof(BulletWeapon), "DropMagazine")] [HarmonyWrapSafe] [HarmonyPostfix] private static void DropMag(BulletWeapon __instance) { Pool magDropPool = __instance.m_magDropPool; if (magDropPool != null) { FallingObjectHandler.AddPool(magDropPool); FallingObjectHandler.AddObject((magDropPool.m_freeInstances.Count > 0) ? magDropPool.m_freeInstances.First.Value : magDropPool.m_usedInstances.First.Value, (Action?)magDropPool.Return); } } } [HarmonyPatch] internal static class IRFRenderPatches { private static Camera _drawMeshCamera; private static bool _overrideCamera; [HarmonyPatch(typeof(Graphics), "DrawMeshInstancedIndirect", new Type[] { typeof(Mesh), typeof(int), typeof(Material), typeof(Bounds), typeof(ComputeBuffer), typeof(int), typeof(MaterialPropertyBlock), typeof(ShadowCastingMode), typeof(bool), typeof(int), typeof(Camera), typeof(LightProbeUsage), typeof(LightProbeProxyVolume) })] [HarmonyPrefix] private static void FixCamera(ref Camera camera) { if (_overrideCamera) { camera = _drawMeshCamera; } } [HarmonyPatch(typeof(FPSCamera), "OnControllerEnable")] [HarmonyPostfix] private static void EnableCamera(FPSCamera __instance) { _drawMeshCamera = ((CameraController)__instance).m_camera; _overrideCamera = true; } [HarmonyPatch(typeof(FPSCamera), "OnControllerDisable")] [HarmonyPostfix] private static void DisableCamera() { _overrideCamera = false; } } [HarmonyPatch] internal static class MapPatches { private const float UpdateInterval = 0.25f; private static float _nextAllowedTime; private static bool _routineActive; private static bool _forceAllow; public static void OnCleanup() { _routineActive = false; } [HarmonyPatch(typeof(CM_PageMap), "UpdatePlayerData")] [HarmonyPrefix] private static bool Pre_UpdateData(CM_PageMap __instance) { if (_forceAllow) { _forceAllow = false; return true; } if (_routineActive) { return false; } float time = Clock.Time; if (time < _nextAllowedTime) { CoroutineManager.StartCoroutine(CollectionExtensions.WrapToIl2Cpp(DelayedApply(__instance)), (Action)null); return false; } _nextAllowedTime = time + 0.25f; return true; } private static IEnumerator DelayedApply(CM_PageMap __instance) { _routineActive = true; yield return (object)new WaitForSeconds(0.25f); _routineActive = false; __instance.UpdatePlayerData(); } [HarmonyPatch(typeof(CM_PageMap), "OnEnable")] [HarmonyPrefix] private static void Pre_Enable() { _forceAllow = true; } } [HarmonyPatch] internal static class MeleePatches { private static SNet_BroadcastAction? s_meleePacket; [HarmonyPatch(typeof(MeleeWeaponFirstPerson), "Setup")] [HarmonyPrefix] private static bool FixPacket(MeleeWeaponFirstPerson __instance, ItemDataBlock data) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown if (s_meleePacket != null) { ((Item)__instance).ItemDataBlock = data; ((Item)__instance).PublicName = data.publicName; ((Item)__instance).m_expeditionGearComponent = ((Component)__instance).GetComponent(); if ((Object)(object)((Item)__instance).m_expeditionGearComponent != (Object)null) { ((Item)__instance).m_expeditionGearComponent.Setup(); } ((ItemEquippable)__instance).Sound = new CellSoundPlayer(); ((ItemEquippable)__instance).SightLookAlign = CustomExtensions.FindChildRecursive(((Component)__instance).transform, ((Item)__instance).ItemDataBlock.SightLookAlign, true); ((ItemEquippable)__instance).MuzzleAlign = CustomExtensions.FindChildRecursive(((Component)__instance).transform, ((Item)__instance).ItemDataBlock.MuzzleAlign, true); ((ItemEquippable)__instance).BackpackAlign = CustomExtensions.FindChildRecursive(((Component)__instance).transform, ((Item)__instance).ItemDataBlock.BackpackAlign, true); ((Behaviour)__instance).enabled = false; __instance.ModelData = ((Component)__instance).GetComponentInChildren(); __instance.WeaponAnimator = __instance.ModelData.m_animator; __instance.m_hitPacket = s_meleePacket; return false; } return true; } [HarmonyPatch(typeof(MeleeWeaponFirstPerson), "Setup")] [HarmonyPostfix] private static void CachePacket(MeleeWeaponFirstPerson __instance) { s_meleePacket = __instance.m_hitPacket; } } [HarmonyPatch] internal static class ObjectCleanupPatches { [HarmonyPatch(typeof(InstancedRenderFeature), "OnDestroy")] [HarmonyPrefix] private static void Pre_Destroy(InstancedRenderFeature __instance) { if ((Object)(object)((Component)__instance).transform.parent == (Object)null) { return; } InstancedRenderFeatureRenderer renderer = __instance.m_renderer; if (renderer != null && (Object)(object)renderer.m_compute != (Object)null) { Object.Destroy((Object)(object)renderer.m_compute); renderer.m_compute = null; } if (__instance.m_controller == null) { return; } foreach (Material item in (Il2CppArrayBase)(object)__instance.m_controller.Materials) { Object.Destroy((Object)(object)item); } } [HarmonyPatch(typeof(EnemyAgent), "OnDestroy")] [HarmonyPrefix] private static void Pre_EnemyDestroy(EnemyAgent __instance) { FlyerAnimationController componentInChildren = ((Component)__instance).GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null) { Object.Destroy((Object)(object)componentInChildren.m_fleshBulbs.m_material); } } [HarmonyPatch(typeof(FX_Manager), "ResetManager")] [HarmonyPrefix] private static void Pre_Reset() { foreach (FX_PointLight item in (Il2CppArrayBase)(object)FX_Manager.s_effectPointLights) { Object.Destroy((Object)(object)((Component)item).gameObject); } FX_Manager.s_activeLights.Clear(); FX_Manager.s_freeLights.Clear(); } [HarmonyPatch(typeof(GS_AfterLevel), "CleanupAfterExpedition")] [HarmonyPriority(200)] [HarmonyPostfix] private static void Post_AllCleanup() { foreach (TextAsset item in Object.FindObjectsOfType()) { if (((Il2CppArrayBase)(object)item.bytes).Length == 0) { Object.Destroy((Object)(object)item); } } } } [HarmonyPatch] internal static class SoundPatches { [HarmonyPatch(typeof(GlueGunProjectile), "SyncDestroy")] [HarmonyPrefix] private static void GlueGunSpawn(GlueGunProjectile __instance) { __instance.m_sound.Recycle(); } [HarmonyPatch(typeof(ProjectileBase), "Collision")] [HarmonyPostfix] private static void ProjectileDestroy(ProjectileBase __instance) { __instance.m_soundPlayer.Recycle(); } [HarmonyPatch(typeof(StrikerBigTentacle), "OnDead")] [HarmonyPostfix] private static void BigTentacleDead(StrikerBigTentacle __instance) { __instance.m_tipSound.Recycle(); } } } namespace MemoryLeakFix.Handler { internal sealed class FallingObjectHandler : MonoBehaviour { private static readonly Action BasicDestroy; private const int MaxSteps = 20; private const float UpdateInterval = 1f; private readonly LinkedList<(GameObject? go, Action destroyFunc)> _objects = new LinkedList<(GameObject, Action)>(); private readonly Dictionary _pools = new Dictionary(); private float _nextUpdateTime; private LinkedListNode<(GameObject? go, Action destroyFunc)>? _currentNode; public static FallingObjectHandler Current { get; private set; } static FallingObjectHandler() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown BasicDestroy = delegate(GameObject go) { Object.Destroy((Object)(object)go); }; ClassInjector.RegisterTypeInIl2Cpp(); GameObject val = new GameObject("MemoryLeakFix_FallingObjectHandler"); Object.DontDestroyOnLoad((Object)val); Current = val.AddComponent(); } public void EnsureInit() { } public void Awake() { Current = this; } public static void AddObject(GameObject go, Action? destroyFunc = null) { Current._objects.AddLast((go, destroyFunc ?? BasicDestroy)); } public static void AddPool(Pool pool) { Current._pools.TryAdd(((Il2CppObjectBase)pool).Pointer, pool); } private void Update() { //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (Clock.Time < _nextUpdateTime) { return; } if (_currentNode == null) { _currentNode = _objects.First; } for (int i = 0; i < 20; i++) { if (_currentNode == null) { break; } GameObject item = _currentNode.Value.go; LinkedListNode<(GameObject, Action)> next = _currentNode.Next; if ((Object)(object)item != (Object)null && item.transform.position.y < -10000f) { _currentNode.Value.destroyFunc(item); _objects.Remove(_currentNode); } else if ((Object)(object)item == (Object)null || !item.active) { _objects.Remove(_currentNode); } _currentNode = next; } if (_currentNode == null) { _nextUpdateTime = Clock.Time + 1f; } } private void OnClear() { KeyValuePair[] array = _pools.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair keyValuePair = array[i]; var (key, val2) = (KeyValuePair)(ref keyValuePair); if (val2 == null) { _pools.Remove(key); } else if (val2.m_usedInstances.Count > 0) { GameObject[] array2 = (GameObject[])(object)new GameObject[val2.m_usedInstances.Count]; LinkedListNode val3 = val2.m_usedInstances.First; int num = 0; while (num < val2.m_usedInstances.Count) { array2[num] = val3.Value; num++; val3 = val3.Next; } GameObject[] array3 = array2; foreach (GameObject val4 in array3) { val2.Return(val4); } } } _objects.Clear(); _currentNode = null; } public static void Clear() { Current.OnClear(); } } }