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 spacefixland v2.1.5
BepInEx/plugins/spacefixland/spacefixland.dll
Decompiled an hour agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using DunGen; using DunGen.Graph; using HarmonyLib; using LethalLevelLoader; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace DawnLandingCompatibility; internal static class LogFloodProtection { private static readonly RepeatedLogGate gate = new RepeatedLogGate(256); private static ConfigEntry<bool> enabled; private static ConfigEntry<int> interval; private static ManualLogSource log; [ThreadStatic] private static bool reporting; internal static void Install(ConfigFile config, ManualLogSource logger) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Expected O, but got Unknown //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Expected O, but got Unknown log = logger; enabled = config.Bind<bool>("Performance", "ThrottleRepeatedUnityDiagnostics", true, "Keep the first identical Unity warning/error/exception per object, then at most one per interval. Does not fix the underlying errors. Disable for complete diagnostic logs."); interval = config.Bind<int>("Performance", "DiagnosticIntervalSeconds", 10, "Seconds between identical Unity diagnostics (clamped to 1-60). Different exception stacks and object contexts remain separate."); Harmony val = new Harmony("clair.dawnlandingcompatibility.logging"); try { Type type = typeof(Debug).Assembly.GetType("UnityEngine.DebugLogHandler", throwOnError: true); val.Patch((MethodBase)AccessTools.Method(type, "LogException", new Type[2] { typeof(Exception), typeof(Object) }, (Type[])null), new HarmonyMethod(typeof(LogFloodProtection), "ExceptionPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(type, "LogFormat", new Type[4] { typeof(LogType), typeof(Object), typeof(string), typeof(object[]) }, (Type[])null), new HarmonyMethod(typeof(LogFloodProtection), "FormatPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); log.LogInfo((object)"Optional log-flood protection installed; first diagnostics and periodic repeats are preserved."); } catch (Exception ex) { val.UnpatchSelf(); log.LogWarning((object)("Log-flood protection unavailable; Dawn compatibility remains active: " + ex.Message)); } } internal static void Reset() { gate.Clear(); } private static bool Allow(string key) { int suppressed; bool flag = gate.Allow(key, (double)Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency, Math.Max(1, Math.Min(60, interval.Value)), out suppressed); if (flag && suppressed > 0) { reporting = true; try { log.LogWarning((object)("Log-flood protection suppressed " + suppressed + " identical Unity diagnostics; allowing the next occurrence below. Underlying error still needs repair.")); } finally { reporting = false; } } return flag; } private static bool ExceptionPrefix(Exception __0, Object __1) { if (reporting || !enabled.Value || __0 == null) { return true; } int num = ((!object.ReferenceEquals(__1, null)) ? RuntimeHelpers.GetHashCode(__1) : 0); return Allow("exception|" + num + "|" + __0.GetType().FullName + "|" + __0.Message + "|" + __0.StackTrace); } private static bool FormatPrefix(LogType __0, Object __1, string __2, object[] __3) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) if (reporting || !enabled.Value || ((int)__0 != 2 && (int)__0 != 0) || __2 == null) { return true; } string text = __2; if (__3 != null && __3.Length != 0) { foreach (object obj in __3) { if (obj != null && !(obj is string) && !obj.GetType().IsPrimitive && !obj.GetType().IsEnum) { return true; } } try { text = string.Format(CultureInfo.InvariantCulture, __2, __3); } catch (FormatException) { return true; } } int num = ((!object.ReferenceEquals(__1, null)) ? RuntimeHelpers.GetHashCode(__1) : 0); return Allow(string.Concat(__0, "|", num, "|", text)); } } [BepInDependency("imabatby.lethallevelloader", "1.7.13")] [BepInDependency("com.github.teamxiaolan.dawnlib", "0.9.27")] [BepInPlugin("clair.dawnlandingcompatibility", "spacefixland", "2.1.5")] public sealed class Plugin : BaseUnityPlugin { private static ManualLogSource log; private static MethodInfo getInfo; private static MethodInfo setInfo; private static MethodInfo handoff; private static MethodInfo getArchetypeInfo; private static FieldInfo dungeonRegistry; private static PropertyInfo registryValues; private static PropertyInfo infoFlow; private static readonly HashSet<DungeonFlow> fallbackFlows = new HashSet<DungeonFlow>(); private Harmony harmony; private void Awake() { //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Expected O, but got Unknown //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Expected O, but got Unknown //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Expected O, but got Unknown //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Expected O, but got Unknown log = ((BaseUnityPlugin)this).Logger; LogFloodProtection.Install(((BaseUnityPlugin)this).Config, log); PluginInfo val = Chainloader.PluginInfos["com.github.teamxiaolan.dawnlib"]; if (val.Metadata.Version != new Version(0, 9, 27)) { log.LogError((object)string.Concat("Unsupported DawnLib version ", val.Metadata.Version, "; no patches installed. Built for 0.9.27.")); return; } try { Assembly assembly = ((object)val.Instance).GetType().Assembly; Type type = assembly.GetType("Dawn.DungeonFlowExtensions", throwOnError: true); getInfo = AccessTools.Method(type, "GetDawnInfo", (Type[])null, (Type[])null); setInfo = AccessTools.Method(type, "SetDawnInfo", (Type[])null, (Type[])null); dungeonRegistry = AccessTools.Field(assembly.GetType("Dawn.LethalContent", throwOnError: true), "Dungeons"); handoff = AccessTools.Method(assembly.GetType("Dawn.Internal.LethalLevelLoaderCompat", throwOnError: true), "LetLLLHandleGeneration", (Type[])null, (Type[])null); getArchetypeInfo = AccessTools.Method(assembly.GetType("Dawn.DungeonArchetypeExtensions", throwOnError: true), "GetDawnInfo", (Type[])null, (Type[])null); Type type2 = assembly.GetType("Dawn.DungeonRegistrationHandler", throwOnError: true); MethodInfo methodInfo = AccessTools.Method(type2, "LoadDungeonBundle", Type.EmptyTypes, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(type2, "TryInjectTileSets", (Type[])null, (Type[])null); if (getInfo == null || setInfo == null || dungeonRegistry == null || handoff == null || getArchetypeInfo == null || methodInfo == null || methodInfo.ReturnType != typeof(IEnumerator) || methodInfo2 == null) { throw new MissingMethodException("Expected DawnLib 0.9.27 dungeon API is unavailable."); } harmony = new Harmony("clair.dawnlandingcompatibility"); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "WrapLoad", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(Plugin), "CheckTileMetadata", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(RoundManager), "LoadNewLevel", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "ResetVisit", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); log.LogInfo((object)"DawnLib 0.9.27 dungeon compatibility installed. Valid Dawn loaders are preserved."); } catch (Exception ex) { if (harmony != null) { harmony.UnpatchSelf(); } log.LogError((object)("Could not install compatibility adapter: " + ex)); } } private static void ResetVisit() { fallbackFlows.Clear(); LogFloodProtection.Reset(); } private static void WrapLoad(ref IEnumerator __result) { __result = LoadOrRepair(__result); } internal static bool OwnedByLLL(DungeonFlow flow) { foreach (ExtendedDungeonFlow extendedDungeonFlow in PatchedContent.ExtendedDungeonFlows) { if ((Object)(object)extendedDungeonFlow != (Object)null && (Object)(object)extendedDungeonFlow.DungeonFlow == (Object)(object)flow) { return true; } } return false; } private static bool RestoreMetadata(DungeonFlow flow) { object value = dungeonRegistry.GetValue(null); if (registryValues == null) { registryValues = AccessTools.Property(value.GetType(), "Values"); } if (!(registryValues.GetValue(value, null) is IEnumerable enumerable)) { return false; } object obj = null; foreach (object item in enumerable) { if (item != null) { if (infoFlow == null) { infoFlow = AccessTools.Property(item.GetType(), "DungeonFlow"); } object? value2 = infoFlow.GetValue(item, null); DungeonFlow val = (DungeonFlow)((value2 is DungeonFlow) ? value2 : null); if (!((Object)(object)val == (Object)null) && (Object)(object)val == (Object)(object)flow) { obj = item; break; } } } if (obj == null) { return false; } setInfo.Invoke(null, new object[2] { flow, obj }); return getInfo.Invoke(null, new object[1] { flow }) != null; } private static IEnumerator LoadOrRepair(IEnumerator original) { RoundManager round = RoundManager.Instance; DungeonFlow flow = (((Object)(object)round == (Object)null || (Object)(object)round.dungeonGenerator == (Object)null || round.dungeonGenerator.Generator == null) ? null : round.dungeonGenerator.Generator.DungeonFlow); bool fallback = false; if ((Object)(object)flow != (Object)null) { try { if (getInfo.Invoke(null, new object[1] { flow }) == null) { if (RestoreMetadata(flow)) { log.LogInfo((object)("Restored Dawn metadata for dungeon " + ((Object)flow).name + ".")); } else if (OwnedByLLL(flow)) { fallback = true; } else { log.LogError((object)("Missing Dawn metadata for unrecognized dungeon " + ((Object)flow).name + "; preserving Dawn's loader. Check host/client mod parity.")); } } } catch (Exception ex) { log.LogError((object)("Dungeon metadata inspection failed; preserving Dawn's loader: " + ex)); } } if (fallback) { fallbackFlows.Add(flow); log.LogWarning((object)("Missing Dawn metadata on LLL-owned dungeon '" + ((Object)flow).name + "'; handing generation back to LethalLevelLoader. Install this adapter on host and every client.")); if (original is IDisposable disposable) { disposable.Dispose(); } yield return (object)new WaitForSeconds(0.1f); if (!((Object)(object)RoundManager.Instance != (Object)(object)round) && !((Object)(object)round.dungeonGenerator == (Object)null) && !((Object)(object)round.dungeonGenerator.Generator.DungeonFlow != (Object)(object)flow)) { handoff.Invoke(null, null); } } else { if (original == null) { yield break; } try { while (original.MoveNext()) { yield return original.Current; } } finally { if (original is IDisposable disposable2) { disposable2.Dispose(); } } } } private static bool CheckTileMetadata(DungeonFlow __0) { if ((Object)(object)__0 == (Object)null || !fallbackFlows.Contains(__0)) { return true; } DungeonArchetype[] usedArchetypes = __0.GetUsedArchetypes(); foreach (DungeonArchetype val in usedArchetypes) { if ((Object)(object)val != (Object)null && getArchetypeInfo.Invoke(null, new object[1] { val }) == null) { log.LogWarning((object)("Skipping Dawn tile-set additions for fallback dungeon '" + ((Object)__0).name + "' because archetype metadata is missing. Authored LLL tiles remain in use.")); return false; } } return true; } } internal sealed class RepeatedLogGate { private sealed class Entry { internal double Last; internal int Suppressed; } private readonly Dictionary<string, Entry> entries = new Dictionary<string, Entry>(); private readonly int capacity; internal RepeatedLogGate(int capacity) { this.capacity = Math.Max(1, capacity); } internal bool Allow(string key, double now, double interval, out int suppressed) { suppressed = 0; if (string.IsNullOrEmpty(key) || key.Length > 16384) { return true; } lock (entries) { if (!entries.TryGetValue(key, out var value)) { if (entries.Count < capacity) { entries.Add(key, new Entry { Last = now }); } return true; } if (now >= value.Last && now - value.Last < interval) { if (value.Suppressed < int.MaxValue) { value.Suppressed++; } return false; } suppressed = value.Suppressed; value.Suppressed = 0; value.Last = now; return true; } } internal void Clear() { lock (entries) { entries.Clear(); } } }