using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Cysharp.Threading.Tasks;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.Scripting;
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace GK2FasterDoors;
[BepInPlugin("gk2.fasterdoors", "GK2 Faster Doors", "0.1.0")]
public class Plugin : BaseUnityPlugin
{
public const string Guid = "gk2.fasterdoors";
public const string Version = "0.1.0";
internal static ManualLogSource Log;
internal static ConfigEntry<bool> Enabled;
internal static ConfigEntry<bool> Verbose;
internal static ConfigEntry<int> EveryDoors;
internal static ConfigEntry<int> GrowthMb;
private static int _doorsSkipped;
private static long _baseManaged;
private static long _baseNative;
private static bool _haveBase;
private static float _measureAt = -1f;
private static int _gcAtDoor;
private static readonly Dictionary<string, int> Stripped = new Dictionary<string, int>();
private static bool _counting;
private void Awake()
{
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_0059: Expected O, but got Unknown
//IL_0089: Unknown result type (might be due to invalid IL or missing references)
//IL_0093: Expected O, but got Unknown
//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
//IL_00c3: Expected O, but got Unknown
//IL_010c: Unknown result type (might be due to invalid IL or missing references)
//IL_0112: Expected O, but got Unknown
//IL_016c: Unknown result type (might be due to invalid IL or missing references)
//IL_0181: Unknown result type (might be due to invalid IL or missing references)
//IL_018e: Expected O, but got Unknown
//IL_018e: Expected O, but got Unknown
//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
//IL_01e4: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Skip the game's memory clean-up on most doors and teleports (it runs in full when due, see below).");
EveryDoors = ((BaseUnityPlugin)this).Config.Bind<int>("General", "FullCleanupEveryDoors", 10, new ConfigDescription("Run the full clean-up at least on every Nth door. 1 = every door, as in the unmodded game.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
GrowthMb = ((BaseUnityPlugin)this).Config.Bind<int>("General", "FullCleanupAfterGrowthMB", 300, new ConfigDescription("Also run the full clean-up on the next door once the game's memory has grown by this many MB since the last one.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(50, 4000), Array.Empty<object>()));
Verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Verbose", false, "Log every door's decision and what the full clean-up removed.");
try
{
Harmony val = new Harmony("gk2.fasterdoors");
MethodInfo methodInfo = AccessTools.Method(typeof(MainGame), "HiddenOptimization", (Type[])null, (Type[])null);
if (methodInfo == null)
{
Log.LogError((object)"MainGame.HiddenOptimization not found - the mod does nothing");
return;
}
HarmonyMethod val2 = new HarmonyMethod(typeof(Plugin), "OptPre", (Type[])null);
val2.priority = 800;
val.Patch((MethodBase)methodInfo, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
MethodInfo methodInfo2 = AccessTools.Method(typeof(MainGame).Assembly.GetType("EditorOnlyComponentStripper"), "StripAll", (Type[])null, (Type[])null);
if (methodInfo2 != null)
{
val.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(Plugin), "StripPre", (Type[])null), new HarmonyMethod(typeof(Plugin), "StripPost", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
}
MethodInfo methodInfo3 = AccessTools.Method(typeof(MainGame).Assembly.GetType("EditorOnlyComponentStripper"), "DestroyEditorOnlyComponent", (Type[])null, (Type[])null);
if (methodInfo3 != null)
{
val.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(Plugin), "DestroyPre", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
}
}
catch (Exception ex)
{
Log.LogError((object)("Patching failed, the mod does nothing: " + ex));
return;
}
Log.LogInfo((object)("GK2 Faster Doors 0.1.0 loaded (full clean-up every " + EveryDoors.Value + " doors or after +" + GrowthMb.Value + " MB; incremental GC " + ((!IncrementalGc()) ? "off" : "on") + ")"));
}
private static bool IncrementalGc()
{
try
{
return GarbageCollector.isIncremental;
}
catch
{
return false;
}
}
private void OnDestroy()
{
try
{
Harmony.UnpatchID("gk2.fasterdoors");
}
catch (Exception ex)
{
Log.LogWarning((object)("Unpatch: " + ex.Message));
}
}
private void Update()
{
if (_measureAt >= 0f && Time.unscaledTime >= _measureAt)
{
_measureAt = -1f;
_baseManaged = GC.GetTotalMemory(forceFullCollection: false);
_baseNative = Native();
_haveBase = true;
if (Verbose.Value)
{
Log.LogInfo((object)("base after full clean-up: managed " + Mb(_baseManaged) + " MB, native " + Mb(_baseNative) + " MB"));
}
}
}
private static long Native()
{
try
{
return Profiler.GetTotalAllocatedMemoryLong();
}
catch
{
return 0L;
}
}
private static long Mb(long bytes)
{
return bytes / 1048576;
}
private static bool FromTeleport()
{
try
{
StackFrame[] frames = new StackTrace(fNeedFileInfo: false).GetFrames();
if (frames == null)
{
return false;
}
StackFrame[] array = frames;
foreach (StackFrame stackFrame in array)
{
Type type = stackFrame.GetMethod()?.DeclaringType;
while (type != null)
{
if (type == typeof(PlayerController))
{
return true;
}
type = type.DeclaringType;
}
}
}
catch
{
}
return false;
}
private static bool OptPre(ref UniTask __result)
{
//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
try
{
if (!Enabled.Value)
{
FullRun("off");
return true;
}
if (!FromTeleport())
{
FullRun("scene load");
return true;
}
string text = null;
long totalMemory = GC.GetTotalMemory(forceFullCollection: false);
long num = Native();
long num2 = ((!_haveBase) ? 0 : (Math.Max(0L, totalMemory - _baseManaged) + Math.Max(0L, num - _baseNative)));
if (!_haveBase)
{
text = "first door";
}
else if (_doorsSkipped + 1 >= EveryDoors.Value)
{
text = "door " + (_doorsSkipped + 1) + " of " + EveryDoors.Value;
}
else if (num2 >= (long)GrowthMb.Value * 1024L * 1024)
{
text = "memory +" + Mb(num2) + " MB";
}
if (text != null)
{
FullRun(text);
return true;
}
_doorsSkipped++;
if (Verbose.Value)
{
Log.LogInfo((object)("door: clean-up skipped (" + _doorsSkipped + " since the last one, memory +" + Mb(num2) + " MB, managed " + Mb(totalMemory) + " MB, native " + Mb(num) + " MB, GCs since last door " + (GC.CollectionCount(0) - _gcAtDoor) + ")"));
}
_gcAtDoor = GC.CollectionCount(0);
__result = default(UniTask);
return false;
}
catch (Exception ex)
{
Log.LogWarning((object)("Deciding failed, running the game's clean-up: " + ex.Message));
return true;
}
}
private static void FullRun(string why)
{
_doorsSkipped = 0;
_measureAt = Time.unscaledTime + 3f;
_gcAtDoor = GC.CollectionCount(0);
if (Verbose.Value)
{
Log.LogInfo((object)("full clean-up (" + why + "): managed " + Mb(GC.GetTotalMemory(forceFullCollection: false)) + " MB, native " + Mb(Native()) + " MB"));
}
}
private static void StripPre()
{
_counting = Verbose.Value;
Stripped.Clear();
}
private static void StripPost()
{
if (_counting)
{
_counting = false;
Log.LogInfo((object)((Stripped.Count != 0) ? ("editor-only components removed: " + string.Join(", ", from p in Stripped
orderby p.Value descending
select p.Key + " " + p.Value)) : "editor-only components removed: none"));
}
}
private static void DestroyPre(Component __0)
{
if (_counting && !((Object)(object)__0 == (Object)null))
{
string name = ((object)__0).GetType().Name;
Stripped.TryGetValue(name, out var value);
Stripped[name] = value + 1;
}
}
}