Decompiled source of LazyLanding v1.0.1

LazyLanding.dll

Decompiled 3 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Unity.AI.Navigation;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("LazyLanding")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("LazyLanding")]
[assembly: AssemblyTitle("LazyLanding")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
[BepInPlugin("local.LazyLanding", "LazyLanding", "1.0.0")]
public class LazyLanding : BaseUnityPlugin
{
	private class NavBuild
	{
		public AsyncOperation Op;

		public bool Again;

		public Stopwatch Clock;
	}

	private class Runner : MonoBehaviour
	{
		private IEnumerator Start()
		{
			while ((Object)(object)Object.FindObjectOfType<MenuManager>() == (Object)null)
			{
				yield return null;
			}
			DeferOtherMods();
			while ((Object)(object)StartOfRound.Instance == (Object)null)
			{
				yield return null;
			}
			DeferOtherMods();
		}

		private void Update()
		{
			if (Time.frameCount >= readyFrame)
			{
				RunJobs();
			}
			CheckNavBuilds();
			if (queue.Count == 0 && navBuilds.Count == 0 && LazyLanding.heldRpc != null)
			{
				Action heldRpc = LazyLanding.heldRpc;
				LazyLanding.heldRpc = null;
				log.LogInfo((object)$"Level finished loading {holdClock.Elapsed.TotalSeconds:F1} s after generation, telling the server");
				heldRpc();
			}
		}

		private static void RunJobs()
		{
			Stopwatch stopwatch = Stopwatch.StartNew();
			bool flag = true;
			while (queue.Count > 0 && (flag || stopwatch.Elapsed.TotalMilliseconds < 8.0))
			{
				flag = false;
				var (arg, action) = queue[0];
				queue.RemoveAt(0);
				Stopwatch stopwatch2 = Stopwatch.StartNew();
				lastJobFrame = Time.frameCount;
				insertAt = 0;
				try
				{
					action();
				}
				catch (Exception ex)
				{
					log.LogError((object)$"{arg} failed: {(ex as TargetInvocationException)?.InnerException ?? ex}");
				}
				finally
				{
					insertAt = -1;
				}
				log.LogDebug((object)$"{arg}: {stopwatch2.ElapsedMilliseconds} ms (frame {Time.frameCount})");
			}
		}

		private static void CheckNavBuilds()
		{
			foreach (NavMeshSurface item in navBuilds.Keys.ToList())
			{
				NavBuild navBuild = navBuilds[item];
				if ((Object)(object)item == (Object)null)
				{
					navBuilds.Remove(item);
				}
				else if (navBuild.Op.isDone)
				{
					if (navBuild.Again)
					{
						navBuild.Again = false;
						navBuild.Op = StartNavBuild(item);
					}
					else
					{
						navBuilds.Remove(item);
						log.LogDebug((object)$"{((Object)item).name}: navmesh rebuild done after {navBuild.Clock.Elapsed.TotalSeconds:F1} s");
					}
				}
			}
		}
	}

	private const double FrameBudgetMs = 8.0;

	private static readonly (string Method, string Owner)[] DeferPatches = new(string, string)[3]
	{
		("FinishGeneratingNewLevelClientRpc", "butterystancakes.lethalcompany.chameleon"),
		("FinishGeneratingNewLevelClientRpc", "Chaos.Diversity"),
		("SetLevelObjectVariables", "com.fumiko.CullFactory")
	};

	private static readonly string[] SplitInsideJobs = new string[3] { "DiversityRemastered.DiversityManager:GenerateRoom", "DiversityRemastered.DiversityManager:CreateStairs", "DiversityRemastered.DiversityManager:GenerateMaze" };

	private static ManualLogSource log;

	private static Harmony harmony;

	private static readonly List<(string Name, Action Run)> queue = new List<(string, Action)>();

	private static readonly Dictionary<NavMeshSurface, NavBuild> navBuilds = new Dictionary<NavMeshSurface, NavBuild>();

	private static int insertAt = -1;

	private static MethodBase replaying;

	private static Action heldRpc;

	private static Stopwatch holdClock;

	private static int readyFrame;

	private static int lastJobFrame;

	private static bool inFinishGeneratingLevel;

	private static bool rpcHeld;

	private static int hostWaitState = -1;

	private static FieldInfo hostState;

	private static readonly HashSet<MethodBase> patched = new HashSet<MethodBase>();

	private static Action<RoundManager> spawnOutsideHazards;

	private static Action<RoundManager> spawnSyncedProps;

	private static Action<RoundManager> generatedFloorPostProcessing;

	private static Action<RoundManager> spawnScrapInLevel;

	private static Action<RoundManager> spawnMapObjects;

	private static Action<RoundManager, ulong> finishedGeneratingLevelServerRpc;

	private static FieldInfo chameleonDone;

	private static FieldInfo chameleonApply;

	private static Action chameleonGetReferences;

	private static Action chameleonGetSharedAssets;

	private static List<MethodInfo> splitInside;

	private static bool chameleonLooked;

	private static Type chameleonScene;

	private static Type chameleonCommon;

	private static bool Holding
	{
		get
		{
			if (rpcHeld)
			{
				if (!inFinishGeneratingLevel)
				{
					return heldRpc != null;
				}
				return true;
			}
			return false;
		}
	}

	private void Awake()
	{
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Expected O, but got Unknown
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Expected O, but got Unknown
		//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Expected O, but got Unknown
		//IL_00d7: Expected O, but got Unknown
		//IL_0103: Unknown result type (might be due to invalid IL or missing references)
		//IL_0111: Expected O, but got Unknown
		//IL_019d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bf: Expected O, but got Unknown
		//IL_01bf: Expected O, but got Unknown
		//IL_0231: Unknown result type (might be due to invalid IL or missing references)
		//IL_023d: Expected O, but got Unknown
		//IL_0243: Unknown result type (might be due to invalid IL or missing references)
		//IL_0248: Unknown result type (might be due to invalid IL or missing references)
		//IL_0250: Unknown result type (might be due to invalid IL or missing references)
		//IL_0256: Expected O, but got Unknown
		log = ((BaseUnityPlugin)this).Logger;
		harmony = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
		spawnOutsideHazards = AccessTools.MethodDelegate<Action<RoundManager>>(AccessTools.Method(typeof(RoundManager), "SpawnOutsideHazards", (Type[])null, (Type[])null), (object)null, true);
		finishedGeneratingLevelServerRpc = AccessTools.MethodDelegate<Action<RoundManager, ulong>>(AccessTools.Method(typeof(RoundManager), "FinishedGeneratingLevelServerRpc", (Type[])null, (Type[])null), (object)null, true);
		Harmony obj = harmony;
		MethodInfo methodInfo = AccessTools.Method(typeof(RoundManager), "FinishGeneratingLevel", (Type[])null, (Type[])null);
		HarmonyMethod val = new HarmonyMethod(typeof(LazyLanding), "Enter", (Type[])null)
		{
			priority = 800
		};
		HarmonyMethod val2 = new HarmonyMethod(typeof(LazyLanding), "Exit", (Type[])null);
		obj.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, new HarmonyMethod(typeof(LazyLanding), "QueueHazardsAndHoldRpc", (Type[])null), val2, (HarmonyMethod)null);
		harmony.Patch((MethodBase)AccessTools.Method(typeof(NavMeshSurface), "BuildNavMesh", (Type[])null, (Type[])null), new HarmonyMethod(typeof(LazyLanding), "BuildNavMeshAsync", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		spawnSyncedProps = AccessTools.MethodDelegate<Action<RoundManager>>(AccessTools.Method(typeof(RoundManager), "SpawnSyncedProps", (Type[])null, (Type[])null), (object)null, true);
		generatedFloorPostProcessing = AccessTools.MethodDelegate<Action<RoundManager>>(AccessTools.Method(typeof(RoundManager), "GeneratedFloorPostProcessing", (Type[])null, (Type[])null), (object)null, true);
		MethodInfo methodInfo2 = AccessTools.EnumeratorMoveNext((MethodBase)AccessTools.Method(typeof(RoundManager), "LoadNewLevelWait", (Type[])null, (Type[])null));
		hostState = AccessTools.Field(methodInfo2.DeclaringType, "<>1__state");
		harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(LazyLanding), "WaitForHostJobs", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(LazyLanding), "QueueHostSpawns", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
		spawnScrapInLevel = AccessTools.MethodDelegate<Action<RoundManager>>(AccessTools.Method(typeof(RoundManager), "SpawnScrapInLevel", (Type[])null, (Type[])null), (object)null, true);
		spawnMapObjects = AccessTools.MethodDelegate<Action<RoundManager>>(AccessTools.Method(typeof(RoundManager), "SpawnMapObjects", (Type[])null, (Type[])null), (object)null, true);
		harmony.Patch((MethodBase)AccessTools.Method(typeof(RoundManager), "GeneratedFloorPostProcessing", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(LazyLanding), "SplitFloorPostProcessing", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
		GameObject val3 = new GameObject("LazyLanding")
		{
			hideFlags = (HideFlags)61
		};
		Object.DontDestroyOnLoad((Object)val3);
		val3.AddComponent<Runner>();
	}

	private static void Enter()
	{
		inFinishGeneratingLevel = true;
	}

	private static Exception Exit(Exception __exception)
	{
		inFinishGeneratingLevel = false;
		return __exception;
	}

	private static IEnumerable<CodeInstruction> QueueHazardsAndHoldRpc(IEnumerable<CodeInstruction> instructions)
	{
		List<CodeInstruction> list = instructions.ToList();
		int num = list.FindIndex((CodeInstruction c) => CodeInstructionExtensions.Calls(c, AccessTools.Method(typeof(RoundManager), "SpawnOutsideHazards", (Type[])null, (Type[])null)));
		int num2 = list.FindIndex((CodeInstruction c) => CodeInstructionExtensions.Calls(c, AccessTools.Method(typeof(RoundManager), "FinishedGeneratingLevelServerRpc", (Type[])null, (Type[])null)));
		if (num < 0 || num2 < 0)
		{
			log.LogWarning((object)"FinishGeneratingLevel has changed, leaving level loading as is");
			return list;
		}
		list[num].opcode = OpCodes.Call;
		list[num].operand = AccessTools.Method(typeof(LazyLanding), "QueueHazards", (Type[])null, (Type[])null);
		list[num2].opcode = OpCodes.Call;
		list[num2].operand = AccessTools.Method(typeof(LazyLanding), "HoldRpc", (Type[])null, (Type[])null);
		rpcHeld = true;
		return list;
	}

	private static void QueueHazards(RoundManager roundManager)
	{
		Enqueue("RoundManager.SpawnOutsideHazards", delegate
		{
			spawnOutsideHazards(roundManager);
		});
	}

	private static void HoldRpc(RoundManager roundManager, ulong clientId)
	{
		heldRpc = delegate
		{
			finishedGeneratingLevelServerRpc(roundManager, clientId);
		};
		holdClock = Stopwatch.StartNew();
	}

	private static IEnumerable<CodeInstruction> QueueHostSpawns(IEnumerable<CodeInstruction> instructions)
	{
		List<CodeInstruction> list = instructions.ToList();
		int num = list.FindIndex((CodeInstruction c) => CodeInstructionExtensions.Calls(c, AccessTools.Method(typeof(RoundManager), "SpawnSyncedProps", (Type[])null, (Type[])null)));
		int num2 = list.FindIndex((CodeInstruction c) => CodeInstructionExtensions.Calls(c, AccessTools.Method(typeof(RoundManager), "GeneratedFloorPostProcessing", (Type[])null, (Type[])null)));
		int num3 = ((num2 < 0) ? (-1) : list.FindIndex(num2, (CodeInstruction c) => CodeInstructionExtensions.StoresField(c, hostState)));
		int num4 = ((num3 < 0) ? (-1) : ConstantInt(list[num3 - 1]));
		if (num < 0 || num2 < 0 || num4 < 0)
		{
			log.LogWarning((object)"RoundManager.LoadNewLevelWait has changed, leaving the host's spawns as they are");
			return list;
		}
		list[num].opcode = OpCodes.Call;
		list[num].operand = AccessTools.Method(typeof(LazyLanding), "QueueSyncedProps", (Type[])null, (Type[])null);
		list[num2].opcode = OpCodes.Call;
		list[num2].operand = AccessTools.Method(typeof(LazyLanding), "QueueFloorPostProcessing", (Type[])null, (Type[])null);
		hostWaitState = num4;
		return list;
	}

	private static int ConstantInt(CodeInstruction c)
	{
		if (c.opcode == OpCodes.Ldc_I4_S)
		{
			return (sbyte)c.operand;
		}
		if (c.opcode == OpCodes.Ldc_I4)
		{
			return (int)c.operand;
		}
		return Array.IndexOf(new OpCode[9]
		{
			OpCodes.Ldc_I4_0,
			OpCodes.Ldc_I4_1,
			OpCodes.Ldc_I4_2,
			OpCodes.Ldc_I4_3,
			OpCodes.Ldc_I4_4,
			OpCodes.Ldc_I4_5,
			OpCodes.Ldc_I4_6,
			OpCodes.Ldc_I4_7,
			OpCodes.Ldc_I4_8
		}, c.opcode);
	}

	private static void QueueSyncedProps(RoundManager roundManager)
	{
		Enqueue("RoundManager.SpawnSyncedProps", delegate
		{
			spawnSyncedProps(roundManager);
		});
	}

	private static void QueueFloorPostProcessing(RoundManager roundManager)
	{
		Enqueue("RoundManager.GeneratedFloorPostProcessing", delegate
		{
			generatedFloorPostProcessing(roundManager);
		});
	}

	private static bool WaitForHostJobs(object __instance, ref bool __result)
	{
		if (hostWaitState < 0 || (int)hostState.GetValue(__instance) != hostWaitState)
		{
			return true;
		}
		if (queue.Count == 0 && Time.frameCount > lastJobFrame)
		{
			return true;
		}
		__result = true;
		return false;
	}

	private static IEnumerable<CodeInstruction> SplitFloorPostProcessing(IEnumerable<CodeInstruction> instructions)
	{
		MethodInfo scrap = AccessTools.Method(typeof(RoundManager), "SpawnScrapInLevel", (Type[])null, (Type[])null);
		MethodInfo mapObjects = AccessTools.Method(typeof(RoundManager), "SpawnMapObjects", (Type[])null, (Type[])null);
		foreach (CodeInstruction instruction in instructions)
		{
			if (CodeInstructionExtensions.Calls(instruction, scrap))
			{
				instruction.operand = AccessTools.Method(typeof(LazyLanding), "SpawnScrapLater", (Type[])null, (Type[])null);
			}
			else
			{
				if (!CodeInstructionExtensions.Calls(instruction, mapObjects))
				{
					yield return instruction;
					continue;
				}
				instruction.operand = AccessTools.Method(typeof(LazyLanding), "SpawnMapObjectsLater", (Type[])null, (Type[])null);
			}
			instruction.opcode = OpCodes.Call;
			yield return instruction;
		}
	}

	private static void SpawnScrapLater(RoundManager roundManager)
	{
		CallOrQueue("RoundManager.SpawnScrapInLevel", delegate
		{
			spawnScrapInLevel(roundManager);
		});
	}

	private static void SpawnMapObjectsLater(RoundManager roundManager)
	{
		CallOrQueue("RoundManager.SpawnMapObjects", delegate
		{
			spawnMapObjects(roundManager);
		});
	}

	private static void CallOrQueue(string name, Action run)
	{
		if (insertAt >= 0)
		{
			Enqueue(name, run);
		}
		else
		{
			run();
		}
	}

	private static bool BuildNavMeshAsync(NavMeshSurface __instance)
	{
		if (!Holding || (Object)(object)__instance.navMeshData == (Object)null || !((Behaviour)__instance).isActiveAndEnabled)
		{
			return true;
		}
		if (navBuilds.TryGetValue(__instance, out var value))
		{
			value.Again = true;
		}
		else
		{
			navBuilds[__instance] = new NavBuild
			{
				Op = StartNavBuild(__instance),
				Clock = Stopwatch.StartNew()
			};
		}
		return false;
	}

	private static AsyncOperation StartNavBuild(NavMeshSurface surface)
	{
		Stopwatch stopwatch = Stopwatch.StartNew();
		AsyncOperation result = surface.UpdateNavMesh(surface.navMeshData);
		log.LogDebug((object)$"{((Object)surface).name}: navmesh rebuild started in the background ({stopwatch.ElapsedMilliseconds} ms on the main thread)");
		return result;
	}

	private static bool DeferPostfix(MethodBase __originalMethod, object[] __args)
	{
		if (__originalMethod == replaying || !inFinishGeneratingLevel)
		{
			return true;
		}
		Defer(__originalMethod, __args);
		return false;
	}

	private static bool DeferAlways(MethodBase __originalMethod, object[] __args)
	{
		if (__originalMethod == replaying)
		{
			return true;
		}
		Defer(__originalMethod, __args);
		return false;
	}

	private static bool DeferInsideJob(MethodBase __originalMethod, object[] __args)
	{
		if (__originalMethod == replaying || insertAt < 0)
		{
			return true;
		}
		Defer(__originalMethod, __args);
		return false;
	}

	private static void Defer(MethodBase method, object[] args)
	{
		args = (object[])args.Clone();
		Enqueue(method.DeclaringType.FullName + "." + method.Name, delegate
		{
			MethodBase methodBase = replaying;
			replaying = method;
			try
			{
				method.Invoke(null, args);
			}
			finally
			{
				replaying = methodBase;
			}
		});
	}

	private static void Enqueue(string name, Action run)
	{
		if (insertAt >= 0)
		{
			queue.Insert(insertAt++, (name, run));
		}
		else
		{
			queue.Add((name, run));
		}
		readyFrame = Time.frameCount + 1;
	}

	private static bool OverrideSceneInSteps()
	{
		if ((bool)chameleonDone.GetValue(null))
		{
			return false;
		}
		chameleonDone.SetValue(null, true);
		chameleonGetReferences();
		chameleonGetSharedAssets();
		if (chameleonApply.GetValue(null) is Action action)
		{
			Delegate[] invocationList = action.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Action action2 = (Action)invocationList[i];
				Enqueue("Chameleon " + action2.Method.DeclaringType.Name + "." + action2.Method.Name, action2);
			}
		}
		return false;
	}

	private static void DeferOtherMods()
	{
		//IL_0220: Unknown result type (might be due to invalid IL or missing references)
		//IL_022e: Expected O, but got Unknown
		if (!rpcHeld)
		{
			return;
		}
		Patch(from p in Harmony.GetPatchInfo((MethodBase)AccessTools.Method(typeof(RoundManager), "FinishGeneratingLevel", (Type[])null, (Type[])null)).Postfixes
			where p.owner != harmony.Id
			select p.PatchMethod, "DeferPostfix");
		Patch(DeferPatches.SelectMany(delegate((string Method, string Owner) d)
		{
			Patches patchInfo = Harmony.GetPatchInfo((MethodBase)AccessTools.Method(typeof(RoundManager), d.Method, (Type[])null, (Type[])null));
			return (patchInfo == null) ? Enumerable.Empty<MethodInfo>() : (from p in patchInfo.Prefixes.Concat(patchInfo.Postfixes)
				where p.owner == d.Owner && p.PatchMethod.ReturnType == typeof(void)
				select p.PatchMethod);
		}), "DeferAlways");
		if (splitInside == null)
		{
			splitInside = (from m in Resolve(SplitInsideJobs)
				where m.IsStatic && m.ReturnType == typeof(void)
				select m).ToList();
		}
		Patch(splitInside, "DeferInsideJob");
		if (!chameleonLooked)
		{
			chameleonLooked = true;
			chameleonScene = AccessTools.TypeByName("Chameleon.Overrides.SceneOverrides");
			chameleonCommon = AccessTools.TypeByName("Chameleon.Common");
		}
		Type type = chameleonScene;
		Type type2 = chameleonCommon;
		if (type == null || type2 == null)
		{
			return;
		}
		chameleonDone = AccessTools.Field(type, "done");
		chameleonApply = AccessTools.Field(type, "applyOverrides");
		MethodInfo methodInfo = AccessTools.Method(type2, "GetReferences", (Type[])null, (Type[])null);
		MethodInfo methodInfo2 = AccessTools.Method(type2, "GetSharedAssets", (Type[])null, (Type[])null);
		MethodInfo methodInfo3 = AccessTools.Method(type, "OverrideScene", (Type[])null, (Type[])null);
		if (!(methodInfo3 != null) || !patched.Contains(methodInfo3))
		{
			if (chameleonDone == null || chameleonApply == null || methodInfo == null || methodInfo2 == null || methodInfo3 == null)
			{
				log.LogWarning((object)"Chameleon has changed, leaving its retexture in one piece");
				return;
			}
			chameleonGetReferences = AccessTools.MethodDelegate<Action>(methodInfo, (object)null, true);
			chameleonGetSharedAssets = AccessTools.MethodDelegate<Action>(methodInfo2, (object)null, true);
			harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(LazyLanding), "OverrideSceneInSteps", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			patched.Add(methodInfo3);
			log.LogInfo((object)"Splitting Chameleon.Overrides.SceneOverrides.OverrideScene");
		}
	}

	private static IEnumerable<MethodInfo> Resolve(IEnumerable<string> names)
	{
		return from name in names
			select AccessTools.Method(name, (Type[])null, (Type[])null) into m
			where m != null
			select m;
	}

	private static void Patch(IEnumerable<MethodInfo> methods, string prefix)
	{
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b5: Expected O, but got Unknown
		foreach (MethodInfo method in methods)
		{
			if (patched.Add(method))
			{
				if (method.GetParameters().Any((ParameterInfo p) => p.ParameterType.IsByRef || p.Name == "__state"))
				{
					log.LogInfo((object)("Not deferring " + method.DeclaringType.FullName + "." + method.Name + ": it has to run in place"));
				}
				else
				{
					harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(LazyLanding), prefix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					log.LogInfo((object)("Deferring " + method.DeclaringType.FullName + "." + method.Name));
				}
			}
		}
	}
}
[BepInPlugin("local.LazyLobby", "LazyLobby", "1.0.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class LazyLobby : BaseUnityPlugin
{
	private class WeatherSync : MonoBehaviour
	{
		internal static Action enableCurrent;

		private PlayerControllerB viewed;

		private bool wasInside;

		private void LateUpdate()
		{
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Expected I4, but got Unknown
			PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
			if ((Object)(object)val == (Object)null || (Object)(object)TimeOfDay.Instance == (Object)null || (Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.inShipPhase)
			{
				viewed = null;
				return;
			}
			PlayerControllerB val2 = (val.isPlayerDead ? val.spectatedPlayerScript : val);
			if ((Object)(object)val2 == (Object)null)
			{
				return;
			}
			if ((Object)(object)val2 != (Object)(object)viewed)
			{
				viewed = val2;
				wasInside = val2.isInsideFactory;
			}
			else
			{
				if (val2.isInsideFactory == wasInside)
				{
					return;
				}
				wasInside = val2.isInsideFactory;
				if (wasInside)
				{
					TimeOfDay.Instance.DisableAllWeather(false);
					return;
				}
				if (enableCurrent != null)
				{
					enableCurrent();
					return;
				}
				int num = (int)TimeOfDay.Instance.currentLevelWeather;
				if (num >= 0 && num < TimeOfDay.Instance.effects.Length)
				{
					TimeOfDay.Instance.effects[num].effectEnabled = true;
				}
			}
		}
	}

	private class Preloader : MonoBehaviour
	{
		private IEnumerator Start()
		{
			MenuManager val;
			while ((Object)(object)(val = Object.FindObjectOfType<MenuManager>()) == (Object)null || val.isInitScene)
			{
				yield return null;
			}
			yield return (object)new WaitForSeconds(1f);
			PreloadAssets();
			if (suitsAdded != null)
			{
				yield return DecodeSuits();
			}
		}
	}

	private static readonly string[] PreloadAssetsUsedBy = new string[4] { "DiversityRemastered.Patches.StartOfRoundPatch:AwakePre", "DiversityRemastered.DiversityManager:SetupGame", "DiversityRemastered.DiversityManager:SetupRound", "DiversityRemastered.DiversityManager:GenerateMaze" };

	private static ManualLogSource log;

	private static readonly List<Object> preloaded = new List<Object>();

	private static readonly Dictionary<string, Texture2D> suitTextures = new Dictionary<string, Texture2D>(StringComparer.OrdinalIgnoreCase);

	private static readonly HashSet<Texture2D> handedOver = new HashSet<Texture2D>();

	private static FieldInfo suitsAdded;

	private static string suitPath;

	private static FieldInfo isSceneObject;

	private void Awake()
	{
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Expected O, but got Unknown
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Expected O, but got Unknown
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Expected O, but got Unknown
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Expected O, but got Unknown
		//IL_0169: Unknown result type (might be due to invalid IL or missing references)
		//IL_016e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0176: Unknown result type (might be due to invalid IL or missing references)
		//IL_017c: Expected O, but got Unknown
		//IL_017c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0155: Unknown result type (might be due to invalid IL or missing references)
		//IL_0163: Expected O, but got Unknown
		//IL_011b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0129: Expected O, but got Unknown
		log = ((BaseUnityPlugin)this).Logger;
		Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
		MethodInfo methodInfo = AccessTools.Method("Scoops.patches.MainMenuSpongePatch:Start", (Type[])null, (Type[])null);
		if (methodInfo != null)
		{
			val.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(LazyLobby), "SkipSpongeMenuUnload", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}
		MethodInfo methodInfo2 = AccessTools.Method("MoreSuits.MoreSuitsMod+StartOfRoundPatch:StartPatch", (Type[])null, (Type[])null);
		suitsAdded = AccessTools.Field(AccessTools.TypeByName("MoreSuits.MoreSuitsMod"), "SuitsAdded");
		if (methodInfo2 != null && suitsAdded != null)
		{
			HarmonyMethod val2 = new HarmonyMethod(typeof(LazyLobby), "UseDecodedSuits", (Type[])null);
			val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(LazyLobby), "ReleaseUnusedSuits", (Type[])null), val2, (HarmonyMethod)null, (HarmonyMethod)null);
		}
		isSceneObject = AccessTools.Field(AccessTools.TypeByName("Unity.Netcode.NetworkObject"), "<IsSceneObject>k__BackingField");
		MethodInfo methodInfo3 = AccessTools.Method("Unity.Netcode.NetworkObject:HostCheckForGlobalObjectIdHashOverride", (Type[])null, (Type[])null);
		if (isSceneObject != null && methodInfo3 != null)
		{
			val.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(LazyLobby), "FixUnsetSceneObject", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}
		MethodInfo methodInfo4 = AccessTools.Method("HDLethalCompany.Patch.GraphicsPatch:UpdateScanNodesPostfix", (Type[])null, (Type[])null);
		if (methodInfo4 != null)
		{
			val.Patch((MethodBase)methodInfo4, new HarmonyMethod(typeof(LazyLobby), "SkipHDScanReposition", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}
		GameObject val3 = new GameObject("LazyLobby")
		{
			hideFlags = (HideFlags)61
		};
		Object.DontDestroyOnLoad((Object)val3);
		val3.AddComponent<Preloader>();
		MethodInfo methodInfo5 = AccessTools.Method("WeatherRegistry.WeatherEffectController:EnableCurrentWeatherEffects", (Type[])null, (Type[])null);
		if (methodInfo5 != null)
		{
			WeatherSync.enableCurrent = AccessTools.MethodDelegate<Action>(methodInfo5, (object)null, true);
		}
		val3.AddComponent<WeatherSync>();
	}

	private static bool SkipSpongeMenuUnload()
	{
		return false;
	}

	private static bool SkipHDScanReposition()
	{
		return false;
	}

	private static void FixUnsetSceneObject(MonoBehaviour __instance)
	{
		if (isSceneObject.GetValue(__instance) == null)
		{
			isSceneObject.SetValue(__instance, false);
			log.LogWarning((object)("NetworkObject '" + ((Object)__instance).name + "' had no IsSceneObject; set to false so joining players can sync"));
		}
	}

	private static void PreloadAssets()
	{
		int num = 0;
		string[] preloadAssetsUsedBy = PreloadAssetsUsedBy;
		for (int i = 0; i < preloadAssetsUsedBy.Length; i++)
		{
			MethodInfo methodInfo = AccessTools.Method(preloadAssetsUsedBy[i], (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				continue;
			}
			List<KeyValuePair<OpCode, object>> list = new List<KeyValuePair<OpCode, object>>(PatchProcessor.ReadMethodBody((MethodBase)methodInfo));
			for (int j = 2; j < list.Count; j++)
			{
				if (!(list[j].Value is MethodInfo methodInfo2) || methodInfo2.Name != "LoadAsset" || methodInfo2.DeclaringType != typeof(AssetBundle) || !methodInfo2.IsGenericMethod || !(list[j - 1].Value is string text) || !(list[j - 2].Value is FieldInfo { IsStatic: not false } fieldInfo))
				{
					continue;
				}
				object? value = fieldInfo.GetValue(null);
				AssetBundle val = (AssetBundle)((value is AssetBundle) ? value : null);
				if (val == null)
				{
					continue;
				}
				AssetBundleRequest request = val.LoadAssetAsync(text, methodInfo2.GetGenericArguments()[0]);
				((AsyncOperation)request).completed += delegate
				{
					if (request.asset != (Object)null)
					{
						preloaded.Add(request.asset);
					}
				};
				num++;
			}
		}
		log.LogInfo((object)$"Loading {num} Diversity assets in the background");
	}

	private static IEnumerator DecodeSuits()
	{
		if ((bool)suitsAdded.GetValue(null))
		{
			yield break;
		}
		Stopwatch clock = Stopwatch.StartNew();
		string[] directories = Directory.GetDirectories(Paths.PluginPath, "moresuits", SearchOption.AllDirectories);
		foreach (string path in directories)
		{
			string[] files = Directory.GetFiles(path, "*.png", SearchOption.AllDirectories);
			foreach (string path2 in files)
			{
				if ((Object)(object)StartOfRound.Instance != (Object)null)
				{
					yield break;
				}
				Texture2D val = new Texture2D(2, 2);
				ImageConversion.LoadImage(val, File.ReadAllBytes(path2));
				val.Apply(true, true);
				suitTextures[Path.GetFullPath(path2)] = val;
				yield return null;
				yield return null;
			}
		}
		log.LogInfo((object)$"Decoded {suitTextures.Count} suit textures at the menu ({clock.Elapsed.TotalSeconds:F1} s)");
	}

	private static IEnumerable<CodeInstruction> UseDecodedSuits(IEnumerable<CodeInstruction> instructions)
	{
		MethodInfo readAllBytes = AccessTools.Method(typeof(File), "ReadAllBytes", (Type[])null, (Type[])null);
		ConstructorInfo newTexture = AccessTools.Constructor(typeof(Texture2D), new Type[2]
		{
			typeof(int),
			typeof(int)
		}, false);
		MethodInfo loadImage = AccessTools.Method(typeof(ImageConversion), "LoadImage", new Type[2]
		{
			typeof(Texture2D),
			typeof(byte[])
		}, (Type[])null);
		MethodInfo apply = AccessTools.Method(typeof(Texture2D), "Apply", new Type[2]
		{
			typeof(bool),
			typeof(bool)
		}, (Type[])null);
		foreach (CodeInstruction instruction in instructions)
		{
			if (CodeInstructionExtensions.Calls(instruction, readAllBytes))
			{
				instruction.operand = AccessTools.Method(typeof(LazyLobby), "ReadSuitFile", (Type[])null, (Type[])null);
			}
			else if (instruction.opcode == OpCodes.Newobj && object.Equals(instruction.operand, newTexture))
			{
				instruction.operand = AccessTools.Method(typeof(LazyLobby), "NewSuitTexture", (Type[])null, (Type[])null);
			}
			else if (CodeInstructionExtensions.Calls(instruction, loadImage))
			{
				instruction.operand = AccessTools.Method(typeof(LazyLobby), "LoadSuitImage", (Type[])null, (Type[])null);
			}
			else
			{
				if (!CodeInstructionExtensions.Calls(instruction, apply))
				{
					yield return instruction;
					continue;
				}
				instruction.operand = AccessTools.Method(typeof(LazyLobby), "ApplySuit", (Type[])null, (Type[])null);
			}
			instruction.opcode = OpCodes.Call;
			yield return instruction;
		}
	}

	private static byte[] ReadSuitFile(string path)
	{
		suitPath = Path.GetFullPath(path);
		if (!suitTextures.ContainsKey(suitPath))
		{
			return File.ReadAllBytes(path);
		}
		return Array.Empty<byte>();
	}

	private static Texture2D NewSuitTexture(int width, int height)
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Expected O, but got Unknown
		string text = suitPath;
		suitPath = null;
		if (text == null || !suitTextures.TryGetValue(text, out var value))
		{
			return new Texture2D(width, height);
		}
		suitTextures.Remove(text);
		handedOver.Add(value);
		return value;
	}

	private static bool LoadSuitImage(Texture2D texture, byte[] data)
	{
		if (!handedOver.Contains(texture))
		{
			return ImageConversion.LoadImage(texture, data);
		}
		return true;
	}

	private static void ApplySuit(Texture2D texture, bool updateMipmaps, bool makeNoLongerReadable)
	{
		if (!handedOver.Contains(texture))
		{
			texture.Apply(updateMipmaps, makeNoLongerReadable);
		}
	}

	private static void ReleaseUnusedSuits()
	{
		if (!(bool)suitsAdded.GetValue(null))
		{
			return;
		}
		if (handedOver.Count > 0 || suitTextures.Count > 0)
		{
			log.LogInfo((object)$"More_Suits used {handedOver.Count} textures decoded at the menu");
		}
		foreach (Texture2D value in suitTextures.Values)
		{
			Object.Destroy((Object)(object)value);
		}
		suitTextures.Clear();
		handedOver.Clear();
	}
}