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 RunicFoodGuard v0.1.0
plugins/RunicFoodGuard/RunicFoodGuard.dll
Decompiled 2 days agousing System; using System.Collections.Generic; using System.Diagnostics; 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 HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("RunicFoodGuard")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("ExtraSlots food-slot protection for RunicStorage Quick Stack.")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0")] [assembly: AssemblyProduct("RunicFoodGuard")] [assembly: AssemblyTitle("RunicFoodGuard")] [assembly: AssemblyVersion("0.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 RunicFoodGuard { internal static class HookContract { internal const string RunicVersion = "1.3.4"; internal const string ExtraSlotsVersion = "1.2.12"; internal static void Validate(MethodInfo execute, MethodInfo protection, Type player, Type item) { if (execute == null || execute.IsStatic || execute.ReturnType != typeof(void) || execute.GetParameters().Length != 0 || execute.GetMethodBody() == null) { throw new InvalidOperationException("ExecuteQuickStack no longer has the supported signature."); } ParameterInfo[] array = protection?.GetParameters(); if (protection == null || !protection.IsStatic || protection.ReturnType != typeof(bool) || array.Length != 3 || array[0].ParameterType != player || array[1].ParameterType != item || !array[2].ParameterType.IsEnum || array[2].ParameterType.FullName != "RunicStorage.Engine.StorageProtectionState") { throw new InvalidOperationException("IsProtected no longer has the supported signature."); } if (PatchProcessor.GetOriginalInstructions((MethodBase)execute, (ILGenerator)null).Count((CodeInstruction i) => CodeInstructionExtensions.Calls(i, protection)) != 1) { throw new InvalidOperationException("Quick Stack no longer uses the supported protection check."); } } } [BepInPlugin("compat.runicstorage.extraslots.foodguard", "Runic Food Guard", "0.1.0")] [BepInDependency("chazman.RunicStorage", "1.3.4")] [BepInDependency("shudnal.ExtraSlots", "1.2.12")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "compat.runicstorage.extraslots.foodguard"; public const string Version = "0.1.0"; private Harmony harmony; private ConfigEntry<bool> enabledSetting; private ConfigEntry<bool> debugLogging; private Func<List<ItemData>> foodItems; private bool failureLogged; private void Awake() { //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Expected O, but got Unknown //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Expected O, but got Unknown //IL_029a: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_02bc: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Expected O, but got Unknown //IL_02ce: Expected O, but got Unknown enabledSetting = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Protect ExtraSlots food slots during RunicStorage Quick Stack. Does not affect Store All, crafting or manual transfers."); debugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogging", false, "Log the number of food stacks captured for each Quick Stack attempt, including deferred discovery retries."); if (Application.isBatchMode) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Client-only protection; no patches installed in batch/server mode."); return; } try { PluginInfo val = Chainloader.PluginInfos["chazman.RunicStorage"]; PluginInfo val2 = Chainloader.PluginInfos["shudnal.ExtraSlots"]; if (val.Metadata.Version.ToString() != "1.3.4" || val2.Metadata.Version.ToString() != "1.2.12") { throw new NotSupportedException("Supported versions: RunicStorage 1.3.4 and ExtraSlots 1.2.12. Found " + val.Metadata.Version?.ToString() + " and " + val2.Metadata.Version?.ToString() + "."); } Type? type = ((object)val.Instance).GetType().Assembly.GetType("RunicStorage.Runtime.StorageActions", throwOnError: true); MethodInfo methodInfo = AccessTools.DeclaredMethod(type, "ExecuteQuickStack", Type.EmptyTypes, (Type[])null); MethodInfo methodInfo2 = AccessTools.DeclaredMethod(type, "IsProtected", (Type[])null, (Type[])null); HookContract.Validate(methodInfo, methodInfo2, typeof(Player), typeof(ItemData)); MethodInfo method = ((object)val2.Instance).GetType().Assembly.GetType("ExtraSlots.API", throwOnError: true).GetMethod("GetFoodSlotsItems", BindingFlags.Static | BindingFlags.Public, null, Type.EmptyTypes, null); if (method == null || method.ReturnType != typeof(List<ItemData>)) { throw new InvalidOperationException("ExtraSlots food-slot API signature changed."); } foodItems = (Func<List<ItemData>>)Delegate.CreateDelegate(typeof(Func<List<ItemData>>), method); ProtectionHooks.IsEnabled = () => enabledSetting.Value; ProtectionHooks.GetLocalPlayer = () => Player.m_localPlayer; ProtectionHooks.CaptureFood = Capture; ProtectionHooks.OnCaptureFailure = CaptureFailed; ProtectionHooks.OnCaptured = delegate(int count) { failureLogged = false; if (debugLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Quick Stack: protecting " + count + " food-slot stack(s).")); } }; harmony = new Harmony("compat.runicstorage.extraslots.foodguard"); harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(ProtectionHooks), "ProtectionPostfix", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ProtectionHooks), "Prefix", (Type[])null) { priority = 800 }, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(ProtectionHooks), "Finalizer", (Type[])null) { priority = 0 }, (HarmonyMethod)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Food-slot Quick Stack protection installed. RunicStorage 1.3.4 / ExtraSlots 1.2.12. Enabled=" + enabledSetting.Value + ".")); } catch (Exception ex) { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } ((BaseUnityPlugin)this).Logger.LogError((object)("FOOD PROTECTION NOT ACTIVE. " + ex)); } } private IReadOnlyList<object> Capture(object owner) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown Player val = (Player)owner; if (val != Player.m_localPlayer) { throw new InvalidOperationException("The local player changed during food-slot capture."); } List<ItemData> list = foodItems(); List<ItemData> carried = ((Humanoid)val).GetInventory().GetAllItems(); if (list == null || list.Any((ItemData item) => item == null || !carried.Any((ItemData other) => item == other))) { throw new InvalidOperationException("The food-slot snapshot does not match the live carried inventory."); } return list.Cast<object>().ToArray(); } private void CaptureFailed(Exception error) { if (!failureLogged) { ((BaseUnityPlugin)this).Logger.LogError((object)("Quick Stack skipped because food-slot protection could not be read. " + error)); } failureLogged = true; Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, "Runic Food Guard: food slots could not be read; Quick Stack skipped.", 0, (Sprite)null, false); } } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } ProtectionHooks.IsEnabled = null; ProtectionHooks.GetLocalPlayer = null; ProtectionHooks.CaptureFood = null; ProtectionHooks.OnCaptureFailure = null; ProtectionHooks.OnCaptured = null; } } internal static class ProtectionHooks { internal sealed class Scope { internal Scope Previous; internal object Player; internal object[] Food; internal bool Closed; } internal static Func<bool> IsEnabled; internal static Func<object> GetLocalPlayer; internal static Func<object, IReadOnlyList<object>> CaptureFood; internal static Action<Exception> OnCaptureFailure; internal static Action<int> OnCaptured; [ThreadStatic] private static Scope current; public static bool Prefix(out Scope __state) { __state = new Scope { Previous = current }; current = __state; try { if (!IsEnabled()) { return true; } object obj = GetLocalPlayer(); if (obj == null) { return true; } IReadOnlyList<object> readOnlyList = CaptureFood(obj); if (readOnlyList == null || readOnlyList.Count > 3) { throw new InvalidOperationException("ExtraSlots returned an unexpected food-slot snapshot."); } object[] array = new object[readOnlyList.Count]; for (int i = 0; i < array.Length; i++) { if (readOnlyList[i] == null) { throw new InvalidOperationException("ExtraSlots returned a null food item."); } for (int j = 0; j < i; j++) { if (array[j] == readOnlyList[i]) { throw new InvalidOperationException("ExtraSlots returned duplicate food item references."); } } array[i] = readOnlyList[i]; } __state.Player = obj; __state.Food = array; OnCaptured?.Invoke(array.Length); return true; } catch (Exception obj2) { try { OnCaptureFailure?.Invoke(obj2); } catch { } return false; } } public static void ProtectionPostfix(object __0, object __1, ref bool __result) { if (__result || current?.Food == null || current.Player != __0) { return; } object[] food = current.Food; for (int i = 0; i < food.Length; i++) { if (food[i] == __1) { __result = true; break; } } } public static Exception Finalizer(Scope __state, Exception __exception) { if (__state != null && !__state.Closed) { __state.Closed = true; current = __state.Previous; __state.Previous = null; __state.Player = null; __state.Food = null; } return __exception; } } }