Please disclose if any significant portion of your mod was created 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 LazyLanding v1.0.1
LazyLanding.dll
Decompiled 3 hours agousing 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(); } }