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 GapCloser v1.1.0
GapCloser.dll
Decompiled 9 hours agousing System; 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.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using GapCloser.Fixes; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("GapCloser")] [assembly: AssemblyDescription("Fixes for the game and for other mods, each only in the versions it was checked against, and fast teleports")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.1.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; } } } namespace GapCloser { [BepInPlugin("onononoki.GapCloser", "GapCloser", "1.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [DefaultExecutionOrder(30000)] public class Plugin : BaseUnityPlugin { public const string ModGuid = "onononoki.GapCloser"; public const string ModName = "GapCloser"; public const string ModVersion = "1.1.0"; private static readonly string[] CheckedGameVersions = new string[1] { "1.0.20" }; internal static ManualLogSource Log; private static ConfigEntry<bool> _force; private Harmony _harmony; private void Awake() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; _force = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ForceOnUntestedVersions", false, "Turns the fixes on also for game and mod versions they were not checked against. A forced fix may do nothing or break something, the log names every forced fix."); _harmony = new Harmony("onononoki.GapCloser"); ApplyFix("AzuCraftyBoxesForgeRecipes", AzuCraftyBoxesForgeRecipes.Apply); ApplyFix("ValheimPlusProcreation", ValheimPlusProcreation.Apply); ApplyFix("TriggerWarningSpam", TriggerWarningSpam.Apply); ApplyFix("RecipeChecks", RecipeChecks.Apply); ApplyFix("RecipeCleanup", RecipeCleanup.Apply); ApplyFix("ErrorGuards", ErrorGuards.Apply); ApplyFix("FastTeleport", FastTeleport.Apply); ApplyFix("AzuCraftyBoxesCraftingList", AzuCraftyBoxesCraftingList.Apply); ApplyFix("PieceParticleCulling", PieceParticleCulling.Apply); } private void ApplyFix(string name, Action<Harmony, ConfigFile> apply) { try { apply(_harmony, ((BaseUnityPlugin)this).Config); } catch (Exception ex) { Log.LogError((object)(name + ": could not be applied (" + ex.GetType().Name + ": " + ex.Message + "), the other fixes still work")); } } private void LateUpdate() { RecipeChecks.Flush(); FastTeleport.Frame(); } internal static bool TargetMatches(string fix, string guid, string[] versions) { if (!Chainloader.PluginInfos.TryGetValue(guid, out var value)) { Log.LogInfo((object)(fix + ": " + guid + " is not installed, not needed")); return false; } string text = value.Metadata.Version.ToString(); if (Array.IndexOf(versions, text) < 0) { if (_force.Value) { Log.LogWarning((object)(fix + ": " + guid + " " + text + " is not a checked version (" + string.Join(", ", versions) + "), forced on by ForceOnUntestedVersions")); return true; } Log.LogWarning((object)(fix + ": " + guid + " " + text + " is not a checked version (" + string.Join(", ", versions) + "), the fix stays off (ForceOnUntestedVersions turns it on)")); return false; } return true; } internal static bool GameMatches(string fix) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) string text = ((object)Version.CurrentVersion/*cast due to .constrained prefix*/).ToString(); if (Array.IndexOf(CheckedGameVersions, text) >= 0) { return true; } if (_force.Value) { Log.LogWarning((object)(fix + ": Valheim " + text + " is not a checked version (" + string.Join(", ", CheckedGameVersions) + "), forced on by ForceOnUntestedVersions")); return true; } Log.LogWarning((object)(fix + ": Valheim " + text + " is not a checked version (" + string.Join(", ", CheckedGameVersions) + "), the fix stays off (ForceOnUntestedVersions turns it on)")); return false; } } } namespace GapCloser.Fixes { internal static class AzuCraftyBoxesCraftingList { private const string Name = "AzuCraftyBoxesCraftingList"; private const string ContainerType = "AzuCraftyBoxes.IContainers.VanillaContainer"; private const string CheckType = "AzuCraftyBoxes.Patches.PlayerHaveRequirementsPatchRBoolInt"; private const string BoxesType = "AzuCraftyBoxes.Util.Functions.Boxes"; private const float CountSeconds = 2f; private const float RuleSeconds = 5f; private static readonly string[] CheckedVersions = new string[1] { "1.8.19" }; private static readonly Dictionary<(Inventory, string), int> Counts = new Dictionary<(Inventory, string), int>(); private static readonly Dictionary<(string, string, string), bool> Rules = new Dictionary<(string, string, string), bool>(); private static FieldRef<object, Container> _containerOf; private static ConfigEntryBase _debugLog; private static float _countsSince; private static float _rulesSince; private static int _taking; private static int _reused; private static bool _loggedReuse; private static bool _loggedText; public static void Apply(Harmony harmony, ConfigFile config) { //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Expected O, but got Unknown if (config.Bind<bool>("Fixes", "AzuCraftyBoxesCraftingList", true, "Keeps each chest's count of a material until that chest changes and AzuCraftyBoxes' pull rule answers for a few seconds, and skips a debug text it builds for every recipe you are short on. Less work and garbage with the inventory or a crafting station open near many chests (AzuCraftyBoxes 1.8.19).").Value && Plugin.TargetMatches("AzuCraftyBoxesCraftingList", "Azumatt.AzuCraftyBoxes", CheckedVersions)) { Type type = AccessTools.TypeByName("AzuCraftyBoxes.IContainers.VanillaContainer"); MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "ItemCount", new Type[1] { typeof(string) }, (Type[])null)); FieldInfo fieldInfo = ((type == null) ? null : AccessTools.Field(type, "<_container>P")); Type type2 = AccessTools.TypeByName("AzuCraftyBoxes.Patches.PlayerHaveRequirementsPatchRBoolInt"); MethodInfo methodInfo2 = ((type2 == null) ? null : AccessTools.Method(type2, "HaveRequirementItems", (Type[])null, (Type[])null)); Type type3 = AccessTools.TypeByName("AzuCraftyBoxes.Util.Functions.Boxes"); MethodInfo methodInfo3 = ((type3 == null) ? null : AccessTools.Method(type3, "CanItemBePulled", new Type[3] { typeof(string), typeof(string), typeof(string) }, (Type[])null)); Type type4 = AccessTools.TypeByName("AzuCraftyBoxes.AzuCraftyBoxesPlugin"); _debugLog = (ConfigEntryBase)((type4 == null) ? null : /*isinst with value type is only supported in some contexts*/); if (methodInfo == null || fieldInfo == null || fieldInfo.FieldType != typeof(Container) || methodInfo2 == null || methodInfo3 == null || _debugLog == null) { Plugin.Log.LogWarning((object)"AzuCraftyBoxesCraftingList: AzuCraftyBoxes' chest count, its chest, requirement check, pull rules or debug setting not found, the fix stays off"); return; } _containerOf = AccessTools.FieldRefAccess<Container>(type, fieldInfo.Name); harmony.Patch((MethodBase)methodInfo, Method("CountPrefix"), Method("CountPostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo3, Method("RulePrefix"), Method("RulePostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(Inventory), "Changed", (Type[])null, (Type[])null), (HarmonyMethod)null, Method("Changed"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); HarmonyMethod val = new HarmonyMethod(AccessTools.Method(typeof(AzuCraftyBoxesCraftingList), "TakeStart", (Type[])null, (Type[])null)) { priority = 800 }; HarmonyMethod val2 = Method("TakeEnd"); harmony.Patch((MethodBase)AccessTools.Method(typeof(Player), "ConsumeResources", (Type[])null, (Type[])null), val, (HarmonyMethod)null, (HarmonyMethod)null, val2, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(InventoryGui), "DoCrafting", (Type[])null, (Type[])null), val, (HarmonyMethod)null, (HarmonyMethod)null, val2, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, (HarmonyMethod)null, Method("Transpiler"), (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"AzuCraftyBoxesCraftingList: on for AzuCraftyBoxes 1.8.19"); } } private static HarmonyMethod Method(string name) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown return new HarmonyMethod(AccessTools.Method(typeof(AzuCraftyBoxesCraftingList), name, (Type[])null, (Type[])null)); } private static void TakeStart() { _taking++; Counts.Clear(); } private static void TakeEnd() { if (--_taking < 0) { _taking = 0; } Counts.Clear(); } private static void Changed() { if (Counts.Count > 0) { Counts.Clear(); } } private static bool CountPrefix(object __instance, string __0, ref int __result, out (Inventory, string) __state) { __state = default((Inventory, string)); if (_taking > 0 || __0 == null) { return true; } Container obj = _containerOf.Invoke(__instance); Inventory val = ((obj != null) ? obj.GetInventory() : null); if (val == null) { return true; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup - _countsSince > 2f) { Counts.Clear(); _countsSince = realtimeSinceStartup; } if (Counts.TryGetValue((val, __0), out var value)) { __result = value; if (++_reused == 1000 && !_loggedReuse) { _loggedReuse = true; Plugin.Log.LogInfo((object)string.Format("{0}: reused kept chest counts 1000 times ({1} kept now), first time this session", "AzuCraftyBoxesCraftingList", Counts.Count)); } return false; } __state = (val, __0); return true; } private static void CountPostfix(int __result, (Inventory, string) __state) { if (__state.Item1 != null && _taking == 0) { Counts[__state] = __result; } } private static bool RulePrefix(string __0, string __1, string __2, ref bool __result, out bool __state) { __state = false; float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup - _rulesSince > 5f) { Rules.Clear(); _rulesSince = realtimeSinceStartup; } if (__0 != null && __1 != null && __2 != null && Rules.TryGetValue((__0, __1, __2), out var value)) { __result = value; return false; } __state = __0 != null && __1 != null && __2 != null; return true; } private static void RulePostfix(string __0, string __1, string __2, bool __result, bool __state) { if (__state) { Rules[(__0, __1, __2)] = __result; } } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { MethodInfo methodInfo = AccessTools.Method(typeof(string), "Format", new Type[2] { typeof(string), typeof(object[]) }, (Type[])null); MethodInfo operand = AccessTools.Method(typeof(AzuCraftyBoxesCraftingList), "FormatIfDebug", (Type[])null, (Type[])null); List<CodeInstruction> list = instructions.ToList(); int num = 0; for (int i = 0; i + 1 < list.Count; i++) { if (CodeInstructionExtensions.Calls(list[i], methodInfo) && !(list[i + 1].opcode != OpCodes.Call) && !((list[i + 1].operand as MethodInfo)?.Name != "LogIfReleaseAndDebugEnable")) { list[i].operand = operand; num++; } } Plugin.Log.LogInfo((object)string.Format("{0}: {1} debug text built only with AzuCraftyBoxes' debug log on", "AzuCraftyBoxesCraftingList", num)); return list; } private static string FormatIfDebug(string format, object[] args) { if (string.Equals(_debugLog.BoxedValue?.ToString(), "On", StringComparison.Ordinal)) { return string.Format(format, args); } if (!_loggedText) { _loggedText = true; Plugin.Log.LogInfo((object)"AzuCraftyBoxesCraftingList: skipped a debug text while AzuCraftyBoxes' debug log is off, first time this session"); } return string.Empty; } } internal static class AzuCraftyBoxesForgeRecipes { public const string TargetGuid = "Azumatt.AzuCraftyBoxes"; private const string Name = "AzuCraftyBoxesForgeRecipes"; private const string TargetType = "AzuCraftyBoxes.Patches.PlayerHaveRequirementsPatchRBoolInt"; private static readonly string[] CheckedVersions = new string[1] { "1.8.19" }; private static bool _actedOnce; public static void Apply(Harmony harmony, ConfigFile config) { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown if (!config.Bind<bool>("Fixes", "AzuCraftyBoxesForgeRecipes", true, "AzuCraftyBoxes 1.8.19: stops the Forge of Potential from teaching every recipe. Works only with the checked versions of AzuCraftyBoxes.").Value || !Plugin.TargetMatches("AzuCraftyBoxesForgeRecipes", "Azumatt.AzuCraftyBoxes", CheckedVersions)) { return; } Type type = AccessTools.TypeByName("AzuCraftyBoxes.Patches.PlayerHaveRequirementsPatchRBoolInt"); MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "Postfix", (Type[])null, (Type[])null)); if (methodInfo == null) { Plugin.Log.LogWarning((object)"AzuCraftyBoxesForgeRecipes: AzuCraftyBoxes.Patches.PlayerHaveRequirementsPatchRBoolInt.Postfix not found, the fix stays off"); return; } try { harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.Method(typeof(AzuCraftyBoxesForgeRecipes), "Prefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)("AzuCraftyBoxesForgeRecipes: on for AzuCraftyBoxes " + string.Join(", ", CheckedVersions))); } catch (Exception ex) { Plugin.Log.LogWarning((object)("AzuCraftyBoxesForgeRecipes: could not patch AzuCraftyBoxes.Patches.PlayerHaveRequirementsPatchRBoolInt.Postfix, the fix stays off: " + ex.Message)); } } private static bool Prefix(Player __0, bool discover) { if (!discover || (Object)(object)__0 == (Object)null) { return true; } CraftingStation currentCraftingStation = __0.GetCurrentCraftingStation(); if ((Object)(object)currentCraftingStation == (Object)null || !currentCraftingStation.m_upgrader) { return true; } if (!_actedOnce) { _actedOnce = true; Plugin.Log.LogInfo((object)("AzuCraftyBoxesForgeRecipes: skipped AzuCraftyBoxes' learn check at " + currentCraftingStation.m_name + ", first time this session")); } return false; } } internal static class ErrorGuards { private static readonly FieldRef<Tameable, Character> TameCharacter = AccessTools.FieldRefAccess<Tameable, Character>("m_character"); private static bool _fermenterOnce; private static bool _tameOnce; public static void Apply(Harmony harmony, ConfigFile config) { Guard(harmony, config, "AzuCraftyBoxesFermenterError", "Azumatt.AzuCraftyBoxes", new string[1] { "1.8.19" }, "AzuCraftyBoxes.Patches.SearchContainersAsWell", "Fermenter", "AzuCraftyBoxes 1.8.19: stops the NullReferenceException of its fermenter patch while there is no local player. Works only with the checked versions of AzuCraftyBoxes."); Guard(harmony, config, "ValheimPlusTameHoverError", "org.bepinex.plugins.valheim_plus", new string[1] { "0.10.3.0" }, "ValheimPlus.GameClasses.Tameable_GetHoverText_Patch", "Tame", "ValheimPlus 0.10.3.0: stops the NullReferenceException of its tame hover text on objects without a creature. Works only with the checked versions of ValheimPlus."); } private static void Guard(Harmony harmony, ConfigFile config, string name, string guid, string[] versions, string type, string prefix, string description) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown if (!config.Bind<bool>("Fixes", name, true, description).Value || !Plugin.TargetMatches(name, guid, versions)) { return; } Type type2 = AccessTools.TypeByName(type); MethodInfo methodInfo = ((type2 == null) ? null : AccessTools.Method(type2, "Postfix", (Type[])null, (Type[])null)); if (methodInfo == null) { Plugin.Log.LogWarning((object)(name + ": " + type + ".Postfix not found, the fix stays off")); return; } try { harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.Method(typeof(ErrorGuards), prefix, (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)(name + ": on for " + guid + " " + string.Join(", ", versions))); } catch (Exception ex) { Plugin.Log.LogWarning((object)(name + ": could not patch " + type + ".Postfix, the fix stays off: " + ex.Message)); } } private static bool Fermenter() { if ((Object)(object)Player.m_localPlayer != (Object)null) { return true; } if (!_fermenterOnce) { _fermenterOnce = true; Plugin.Log.LogInfo((object)"AzuCraftyBoxesFermenterError: skipped AzuCraftyBoxes' fermenter postfix, no local player, first time this session"); } return false; } private static bool Tame(Tameable __0) { if ((Object)(object)__0 != (Object)null && (Object)(object)TameCharacter.Invoke(__0) != (Object)null) { return true; } if (!_tameOnce) { _tameOnce = true; Plugin.Log.LogInfo((object)("ValheimPlusTameHoverError: skipped ValheimPlus' tame hover text on " + (((Object)(object)__0 != (Object)null) ? ((Object)__0).name : "a removed object") + ", no creature, first time this session")); } return false; } } internal static class FastTeleport { private class Trip { public float Start; public float Black = -1f; public float Moved = -1f; public float Ready = -1f; public Vector3 From; public Vector3 To; public bool Portal; public bool LoadedAtStart; } private const string Name = "FastTeleport"; private const float PastGameWaits = 8.01f; private const float MovedWithin = 1f; private static readonly FieldRef<Player, float> Timer = AccessTools.FieldRefAccess<Player, float>("m_teleportTimer"); private static readonly FieldRef<Player, Vector3> FromPos = AccessTools.FieldRefAccess<Player, Vector3>("m_teleportFromPos"); private static readonly FieldRef<Player, Vector3> TargetPos = AccessTools.FieldRefAccess<Player, Vector3>("m_teleportTargetPos"); private static readonly FieldRef<Player, bool> Distant = AccessTools.FieldRefAccess<Player, bool>("m_distantTeleport"); private static ConfigEntry<float> _fade; private static ConfigEntry<bool> _log; private static Trip _trip; private const float AfterSeconds = 5f; private const float SlowFrameMs = 40f; private static float _afterUntil = -1f; private static float _afterWorst; private static int _afterSlow; private static int _afterFrames; private static double _afterSum; public static void Apply(Harmony harmony, ConfigFile config) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Expected O, but got Unknown //IL_00d4: Expected O, but got Unknown //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Expected O, but got Unknown ConfigEntry<bool> obj = config.Bind<bool>("Fixes", "FastTeleport", true, "Teleports and portals take only as long as loading the destination really needs: a short fade to black, the move at once, and back as soon as the area, its floor and (with ValheimCommunityPatch) the server's buildings are there. Works only with the checked game versions."); _fade = config.Bind<float>("Fixes", "FastTeleportFadeSeconds", 0.25f, new ConfigDescription("Seconds the screen takes to fade to black, and back, on a teleport (the game uses 1).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), Array.Empty<object>())); _log = config.Bind<bool>("Debug", "LogTeleports", false, "Writes one line per teleport to the log: distance, whether the destination was loaded already, and when the screen was black, the player moved, the area was ready and the trip ended."); if (obj.Value && Plugin.GameMatches("FastTeleport")) { MethodInfo methodInfo = AccessTools.Method(typeof(Player), "UpdateTeleport", (Type[])null, (Type[])null); harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.Method(typeof(FastTeleport), "BeforeUpdate", (Type[])null, (Type[])null)), new HarmonyMethod(AccessTools.Method(typeof(FastTeleport), "AfterUpdate", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(Hud), "GetFadeDuration", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(FastTeleport), "Fade", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)string.Format("{0}: on, fade {1} s, log {2}", "FastTeleport", _fade.Value, _log.Value)); } } private static void BeforeUpdate(Player __instance) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && ((Character)__instance).IsTeleporting()) { if (_trip == null) { Vector3 val = TargetPos.Invoke(__instance); _trip = new Trip { Start = Time.time, From = FromPos.Invoke(__instance), To = val, Portal = Distant.Invoke(__instance), LoadedAtStart = ((Object)(object)ZNetScene.instance != (Object)null && ZNetScene.instance.IsAreaReady(val)) }; } Hud instance = Hud.instance; if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.m_loadingScreen == (Object)null) && !(instance.m_loadingScreen.alpha < 0.99f) && !(Timer.Invoke(__instance) >= 8.01f)) { Timer.Invoke(__instance) = 8.01f; _trip.Black = Time.time - _trip.Start; } } } private static void AfterUpdate(Player __instance) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || _trip == null) { return; } float num = Time.time - _trip.Start; Vector3 position = ((Component)__instance).transform.position; if (_trip.Moved < 0f) { Vector2 val = new Vector2(position.x - _trip.To.x, position.z - _trip.To.z); if (((Vector2)(ref val)).magnitude < 1f) { _trip.Moved = num; } } if (_trip.Moved >= 0f && _trip.Ready < 0f && (Object)(object)ZNetScene.instance != (Object)null && ZNetScene.instance.IsAreaReady(_trip.To)) { _trip.Ready = num; } if (!((Character)__instance).IsTeleporting()) { Trip trip = _trip; _trip = null; if (_log.Value) { Plugin.Log.LogInfo((object)(string.Format("{0}: {1} of {2:0} m, destination {3}: ", "FastTeleport", trip.Portal ? "portal" : "teleport", Vector3.Distance(trip.From, trip.To), trip.LoadedAtStart ? "loaded already" : "not loaded") + string.Format("black {0}, moved {1}, area ready {2}, done {3:0.00} s{4}", Seconds(trip.Black), Seconds(trip.Moved), Seconds(trip.Ready), num, (num >= 15f) ? " (the game's 15 s fallback)" : ""))); _afterUntil = Time.unscaledTime + 5f; _afterSlow = (_afterFrames = 0); _afterWorst = 0f; _afterSum = 0.0; } } } internal static void Frame() { if (!(_afterUntil < 0f)) { float num = Time.unscaledDeltaTime * 1000f; _afterFrames++; _afterSum += num; if (num > 40f) { _afterSlow++; } if (num > _afterWorst) { _afterWorst = num; } if (!(Time.unscaledTime < _afterUntil)) { _afterUntil = -1f; Plugin.Log.LogInfo((object)string.Format("{0}: the {1:0} s after arrival: {2} frames over {3:0} ms, worst {4:0} ms, average {5:0.0} ms", "FastTeleport", 5f, _afterSlow, 40f, _afterWorst, _afterSum / (double)_afterFrames)); } } } private static string Seconds(float value) { if (!(value < 0f)) { return $"{value:0.00} s"; } return "never"; } private static void Fade(Player player, ref float __result) { if ((Object)(object)player != (Object)null && ((Character)player).IsTeleporting() && !((Character)player).IsDead() && !player.IsSleeping()) { __result = _fade.Value; } } } internal static class PieceParticleCulling { private const string Name = "PieceParticleCulling"; public static void Apply(Harmony harmony, ConfigFile config) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown if (config.Bind<bool>("Fixes", "PieceParticleCulling", true, "Fire, smoke and portal effects of building pieces pause while no camera sees them and catch up when seen again, like most of the game's other effects. Only the effects, lights are not touched. Works only with the checked game versions.").Value && Plugin.GameMatches("PieceParticleCulling")) { harmony.Patch((MethodBase)AccessTools.Method(typeof(ZNetScene), "Awake", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(PieceParticleCulling), "SetPrefabs", (Type[])null, (Type[])null)) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"PieceParticleCulling: on"); } } private static void SetPrefabs(ZNetScene __instance) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Invalid comparison between Unknown and I4 int num = 0; int num2 = 0; foreach (GameObject prefab in __instance.m_prefabs) { if ((Object)(object)prefab == (Object)null || (Object)(object)prefab.GetComponent<Piece>() == (Object)null) { continue; } bool flag = false; ParticleSystem[] componentsInChildren = prefab.GetComponentsInChildren<ParticleSystem>(true); for (int i = 0; i < componentsInChildren.Length; i++) { MainModule main = componentsInChildren[i].main; if ((int)((MainModule)(ref main)).cullingMode == 3) { ((MainModule)(ref main)).cullingMode = (ParticleSystemCullingMode)1; num++; flag = true; } } if (flag) { num2++; } } Plugin.Log.LogInfo((object)string.Format("{0}: {1} particle systems on {2} building pieces now pause off screen", "PieceParticleCulling", num, num2)); } } internal static class RecipeChecks { private const string Name = "RecipeChecksOncePerFrame"; private static readonly FieldRef<Player, HashSet<string>> KnownRecipes = AccessTools.FieldRefAccess<Player, HashSet<string>>("m_knownRecipes"); private static Action<Player> _check; private static Action<InventoryGui, bool> _refreshPanel; private static Player _pending; private static int _frame = -1; private static bool _on; private static bool _flushing; private static bool _actedOnce; public static void Apply(Harmony harmony, ConfigFile config) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown if (config.Bind<bool>("Fixes", "RecipeChecksOncePerFrame", true, "Runs the game's recipe learning check at most twice a frame (at once, then once more at the end of the frame) instead of on every inventory change. The same recipes are learned. Works only with the checked game versions.").Value && Plugin.GameMatches("RecipeChecksOncePerFrame")) { MethodInfo methodInfo = AccessTools.Method(typeof(Player), "UpdateKnownRecipesList", (Type[])null, (Type[])null); harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.Method(typeof(RecipeChecks), "Prefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _check = AccessTools.MethodDelegate<Action<Player>>(methodInfo, (object)null, true); _refreshPanel = AccessTools.MethodDelegate<Action<InventoryGui, bool>>(AccessTools.Method(typeof(InventoryGui), "UpdateCraftingPanel", (Type[])null, (Type[])null), (object)null, true); _on = true; Plugin.Log.LogInfo((object)"RecipeChecksOncePerFrame: on"); } } private static bool Prefix(Player __instance) { if (_flushing || (Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } if (Time.frameCount != _frame) { _frame = Time.frameCount; return true; } _pending = __instance; if (!_actedOnce) { _actedOnce = true; Plugin.Log.LogInfo((object)"RecipeChecksOncePerFrame: merged a repeated recipe check into the end of the frame, first time this session"); } return false; } public static void Flush() { if (!_on || (Object)(object)_pending == (Object)null) { return; } Player pending = _pending; _pending = null; if (!((Object)(object)pending == (Object)null) && !((Object)(object)pending != (Object)(object)Player.m_localPlayer)) { int count = KnownRecipes.Invoke(pending).Count; _flushing = true; try { _check(pending); } finally { _flushing = false; } if (KnownRecipes.Invoke(pending).Count != count && (Object)(object)InventoryGui.instance != (Object)null && InventoryGui.IsVisible()) { _refreshPanel(InventoryGui.instance, arg2: false); } } } } internal static class RecipeCleanup { internal enum OnSpawn { Off, WithAzuCraftyBoxes, Always } [CompilerGenerated] private static class <>O { public static ConsoleEvent <0>__Run; public static Func<string, string> <1>__Localize; } private static ConfigEntry<OnSpawn> _onSpawn; private static bool _azuCraftyBoxes; private static Game _doneFor; private const string Name = "RecipeCleanupCommand"; private const string Command = "gapcloser_fixrecipes"; private const int ShownInConsole = 40; private static readonly FieldRef<Player, HashSet<string>> KnownRecipes = AccessTools.FieldRefAccess<Player, HashSet<string>>("m_knownRecipes"); private static readonly FieldRef<Player, HashSet<string>> KnownMaterial = AccessTools.FieldRefAccess<Player, HashSet<string>>("m_knownMaterial"); private static readonly FieldRef<Player, Dictionary<string, int>> KnownStations = AccessTools.FieldRefAccess<Player, Dictionary<string, int>>("m_knownStations"); private static readonly MethodInfo CreateBackup = AccessTools.Method(typeof(SaveSystem), "CreateBackup", (Type[])null, (Type[])null); public static void Apply(Harmony harmony, ConfigFile config) { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Expected O, but got Unknown //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Expected O, but got Unknown if (config.Bind<bool>("Fixes", "RecipeCleanupCommand", true, "Adds the console command gapcloser_fixrecipes, which lists the recipes a character could not have learned by the game's own rule (taught by the AzuCraftyBoxes forge bug). gapcloser_fixrecipes apply backs up the character file and removes them. Works only with the checked game versions.").Value && Plugin.GameMatches("RecipeCleanupCommand")) { _onSpawn = config.Bind<OnSpawn>("Fixes", "RecipeCleanupOnSpawn", OnSpawn.WithAzuCraftyBoxes, "Runs the gapcloser_fixrecipes cleanup by itself each time a character enters a world: WithAzuCraftyBoxes (only when AzuCraftyBoxes, whose forge bug it repairs, is installed), Always, or Off. It backs up the character file before it removes anything."); _azuCraftyBoxes = Chainloader.PluginInfos.ContainsKey("Azumatt.AzuCraftyBoxes"); harmony.Patch((MethodBase)AccessTools.Method(typeof(Terminal), "InitTerminal", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(RecipeCleanup), "Register", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(Player), "OnSpawned", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(RecipeCleanup), "Spawned", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)string.Format("{0}: on, console command {1}, on spawn: {2} (AzuCraftyBoxes installed: {3})", "RecipeCleanupCommand", "gapcloser_fixrecipes", _onSpawn.Value, _azuCraftyBoxes)); } } private static void Register() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown object obj = <>O.<0>__Run; if (obj == null) { ConsoleEvent val = Run; <>O.<0>__Run = val; obj = (object)val; } new ConsoleCommand("gapcloser_fixrecipes", "lists recipes this character could not have learned (AzuCraftyBoxes forge bug), 'apply' removes them", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } private static void Run(ConsoleEventArgs args) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)ObjectDB.instance == (Object)null || (Object)(object)Game.instance == (Object)null) { args.Context.AddString("gapcloser_fixrecipes: load a character into a world first"); return; } List<string> list = Find(localPlayer); Plugin.Log.LogInfo((object)string.Format("{0}: {1} known recipes fail the game's learn rule: {2}", "gapcloser_fixrecipes", list.Count, string.Join(", ", list))); if (list.Count == 0) { args.Context.AddString("gapcloser_fixrecipes: nothing to remove, every known recipe passes the game's learn rule"); return; } string text = ((args.Args.Length > 1) ? args.Args[1].ToLowerInvariant() : ""); if (text != "apply" && text != "applynobackup") { args.Context.AddString(string.Format("{0}: {1} recipes this character could not have learned (all of them in LogOutput.log):", "gapcloser_fixrecipes", list.Count)); args.Context.AddString(string.Join(", ", list.Take(40).Select(Localize)) + ((list.Count > 40) ? ", ..." : "")); args.Context.AddString("type 'gapcloser_fixrecipes apply' to back up the character file and remove them"); return; } string text2 = ((text == "apply") ? Backup() : "none (applynobackup)"); if (text2 == null) { args.Context.AddString("gapcloser_fixrecipes: the character file could not be backed up, nothing removed (see LogOutput.log)"); return; } HashSet<string> hashSet = KnownRecipes.Invoke(localPlayer); int num = 0; foreach (string item in list) { if (hashSet.Remove(item)) { num++; } } Game.instance.SavePlayerProfile(false, false); args.Context.AddString(string.Format("{0}: removed {1} recipes and saved the character, backup: {2}", "gapcloser_fixrecipes", num, text2)); Plugin.Log.LogInfo((object)string.Format("{0}: removed {1} recipes and saved the character, backup: {2}", "gapcloser_fixrecipes", num, text2)); } private static void Spawned(Player __instance) { if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || (Object)(object)Game.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { return; } switch (_onSpawn.Value) { case OnSpawn.WithAzuCraftyBoxes: if (!_azuCraftyBoxes) { break; } goto default; default: { if ((Object)(object)_doneFor == (Object)(object)Game.instance) { break; } _doneFor = Game.instance; List<string> list = Find(__instance); if (list.Count == 0) { Plugin.Log.LogInfo((object)"gapcloser_fixrecipes on spawn: every known recipe passes the game's learn rule, nothing removed"); break; } string text = Backup(); if (text == null) { Plugin.Log.LogWarning((object)string.Format("{0} on spawn: {1} recipes fail the learn rule, but the character file could not be backed up, nothing removed", "gapcloser_fixrecipes", list.Count)); break; } HashSet<string> hashSet = KnownRecipes.Invoke(__instance); int num = 0; foreach (string item in list) { if (hashSet.Remove(item)) { num++; } } Game.instance.SavePlayerProfile(false, false); Plugin.Log.LogInfo((object)string.Format("{0} on spawn: removed {1} recipes this character could not have learned ({2}), backup: {3}", "gapcloser_fixrecipes", num, string.Join(", ", list), text)); if ((Object)(object)MessageHud.instance != (Object)null) { MessageHud.instance.ShowMessage((MessageType)1, string.Format("{0}: removed {1} recipes this character could not have learned", "GapCloser", num), 0, (Sprite)null, false, true); } break; } case OnSpawn.Off: break; } } private static List<string> Find(Player player) { HashSet<string> hashSet = KnownRecipes.Invoke(player); HashSet<string> hashSet2 = new HashSet<string>(); HashSet<string> hashSet3 = new HashSet<string>(); foreach (Recipe recipe in ObjectDB.instance.m_recipes) { string text = recipe?.m_item?.m_itemData?.m_shared?.m_name; if (text != null && hashSet.Contains(text)) { hashSet2.Add(text); if (Learnable(player, recipe)) { hashSet3.Add(text); } } } hashSet2.ExceptWith(hashSet3); return hashSet2.OrderBy((string n) => n).ToList(); } private static bool Learnable(Player player, Recipe recipe) { CraftingStation craftingStation = recipe.m_craftingStation; if ((Object)(object)craftingStation != (Object)null && (!KnownStations.Invoke(player).TryGetValue(craftingStation.m_name, out var value) || value < recipe.m_minStationLevel)) { return false; } HashSet<string> hashSet = KnownMaterial.Invoke(player); Requirement[] resources = recipe.m_resources; foreach (Requirement val in resources) { if ((Object)(object)val?.m_resItem == (Object)null || val.m_upgraderResource || val.m_amount <= 0) { continue; } bool flag = hashSet.Contains(val.m_resItem.m_itemData.m_shared.m_name); if (recipe.m_requireOnlyOneIngredient) { if (flag) { return true; } } else if (!flag) { return false; } } return !recipe.m_requireOnlyOneIngredient; } private static string Localize(string name) { if (Localization.instance == null) { return name; } return Localization.instance.Localize(name); } private static string Backup() { //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) try { PlayerProfile playerProfile = Game.instance.GetPlayerProfile(); string text = playerProfile.GetPath(); if (!File.Exists(text) && File.Exists(text + ".fch")) { text += ".fch"; } if (!File.Exists(text)) { SaveWithBackups val = default(SaveWithBackups); bool flag = default(bool); int num; if (CreateBackup != null && SaveSystem.TryGetSaveByName(playerProfile.m_filename, (SaveDataType)1, ref val) && ((val != null) ? val.PrimaryFile : null) != null) { object obj = CreateBackup.Invoke(null, new object[3] { val.PrimaryFile, DateTime.Now, playerProfile.m_fileSource }); if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0) { return $"the game's own backup of {playerProfile.m_filename} ({playerProfile.m_fileSource})"; } Plugin.Log.LogWarning((object)("gapcloser_fixrecipes: character file not found at " + text + " and the game's own backup failed")); return null; } string text2 = Path.Combine(Paths.ConfigPath, "GapCloser_backups"); Directory.CreateDirectory(text2); string text3 = Path.Combine(text2, $"{Path.GetFileNameWithoutExtension(text)}_{DateTime.Now:yyyyMMdd_HHmmss}{Path.GetExtension(text)}"); File.Copy(text, text3); return text3; } catch (Exception ex) { Plugin.Log.LogWarning((object)("gapcloser_fixrecipes: backup failed: " + ex.GetType().Name + " " + ex.Message)); return null; } } } internal static class TriggerWarningSpam { private const string Name = "TriggerWarningSpam"; private static bool _actedOnce; public static void Apply(Harmony harmony, ConfigFile config) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown if (config.Bind<bool>("Fixes", "TriggerWarningSpam", true, "Stops the game's warning spam from damage areas (like stakes) whose trigger damage is off. Gameplay unchanged. Works only with the checked game versions.").Value && Plugin.GameMatches("TriggerWarningSpam")) { harmony.Patch((MethodBase)AccessTools.Method(typeof(Aoe), "CauseTriggerDamage", (Type[])null, (Type[])null), new HarmonyMethod(AccessTools.Method(typeof(TriggerWarningSpam), "Prefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"TriggerWarningSpam: on"); } } private static bool Prefix(Aoe __instance) { if (__instance.m_useTriggers) { return true; } if (!_actedOnce) { _actedOnce = true; Plugin.Log.LogInfo((object)("TriggerWarningSpam: skipped a warning-only trigger call on " + ((Object)__instance).name + ", first time this session")); } return false; } } internal static class ValheimPlusProcreation { public const string TargetGuid = "org.bepinex.plugins.valheim_plus"; private const string Name = "ValheimPlusProcreationError"; private const string TargetType = "ValheimPlus.GameClasses.Procreation_Awake_Patch"; private static readonly string[] CheckedVersions = new string[1] { "0.10.3.0" }; private static readonly FieldRef<Procreation, Character> CharacterOf = AccessTools.FieldRefAccess<Procreation, Character>("m_character"); private static bool _actedOnce; public static void Apply(Harmony harmony, ConfigFile config) { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown if (!config.Bind<bool>("Fixes", "ValheimPlusProcreationError", true, "ValheimPlus 0.10.3.0: stops the NullReferenceException of its breeding patch on objects without a creature. Works only with the checked versions of ValheimPlus.").Value || !Plugin.TargetMatches("ValheimPlusProcreationError", "org.bepinex.plugins.valheim_plus", CheckedVersions)) { return; } Type type = AccessTools.TypeByName("ValheimPlus.GameClasses.Procreation_Awake_Patch"); MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "Postfix", (Type[])null, (Type[])null)); if (methodInfo == null) { Plugin.Log.LogWarning((object)"ValheimPlusProcreationError: ValheimPlus.GameClasses.Procreation_Awake_Patch.Postfix not found, the fix stays off"); return; } try { harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.Method(typeof(ValheimPlusProcreation), "Prefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)("ValheimPlusProcreationError: on for ValheimPlus " + string.Join(", ", CheckedVersions))); } catch (Exception ex) { Plugin.Log.LogWarning((object)("ValheimPlusProcreationError: could not patch ValheimPlus.GameClasses.Procreation_Awake_Patch.Postfix, the fix stays off: " + ex.Message)); } } private static bool Prefix(Procreation __0) { if ((Object)(object)__0 != (Object)null && (Object)(object)CharacterOf.Invoke(__0) != (Object)null) { return true; } if (!_actedOnce) { _actedOnce = true; Plugin.Log.LogInfo((object)("ValheimPlusProcreationError: skipped ValheimPlus' breeding postfix on " + (((Object)(object)__0 != (Object)null) ? ((Object)__0).name : "a removed object") + ", no creature, first time this session")); } return false; } } }