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 SurtlingFuelItemHopperFix v1.2.2
BepInEx/plugins/L4zerShark_Team-SurtlingFuelItemHopperFix/SurtlingFuelItemHopperFix.dll
Decompiled 20 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.UI; using ValheimHopper.Logic; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("SurtlingFuelItemHopperFix")] [assembly: AssemblyDescription("Compatibility bridge for SurtlingFuel and ItemHopper")] [assembly: AssemblyProduct("SurtlingFuelItemHopperFix")] [assembly: AssemblyFileVersion("1.2.2.0")] [assembly: AssemblyVersion("1.2.2.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 SurtlingFuelItemHopperFix { [BepInPlugin("local.valheim.surtlingfuelitemhopperfix", "SurtlingFuel ItemHopper Fix", "1.2.2")] [BepInDependency("com.dejan.surtlingfuel", "1.4.0")] [BepInDependency("com.maxsch.valheim.ItemHopper", "2.0.0")] [BepInDependency("com.jotunn.jotunn", "2.30.0")] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "local.valheim.surtlingfuelitemhopperfix"; public const string PluginName = "SurtlingFuel ItemHopper Fix"; public const string PluginVersion = "1.2.2"; public const string SurtlingFuelGuid = "com.dejan.surtlingfuel"; public const string ItemHopperGuid = "com.maxsch.valheim.ItemHopper"; internal const string SurtlingCorePrefab = "SurtlingCore"; internal const string CoalPrefab = "Coal"; internal const string CoreFuelRpc = "RPC_SurtlingFuel_AddCoreFuel"; internal const string CoreFuelZdoKeyName = "SurtlingFuel_CoreFuel"; internal const string SmelterPrefab = "smelter"; internal const string BlastFurnacePrefab = "blastfurnace"; internal static readonly int CoreFuelZdoKey = StringExtensionMethods.GetStableHashCode("SurtlingFuel_CoreFuel"); internal static readonly int SmelterPrefabHash = StringExtensionMethods.GetStableHashCode("smelter"); internal static readonly int BlastFurnacePrefabHash = StringExtensionMethods.GetStableHashCode("blastfurnace"); internal static ConfigEntry<string> AdditionalFuelPrefabs; internal static ManualLogSource Log; private static HashSet<string> _alternativeFuelPrefabs = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "SurtlingCore" }; private static MethodInfo _getFuelPerCore; private static FieldInfo _nviewField; private static MethodInfo _isOreAllowed; private static MethodInfo _getOreQueueSize; private static MethodInfo _surtlingAwakePostfix; private static MethodInfo _surtlingOnAddFuelPrefix; private static MethodInfo _surtlingOnHoverAddFuelPostfix; private static MethodInfo _surtlingUpdateSmelterPrefix; private static MethodInfo _surtlingUpdateSmelterPostfix; private static ConfigEntry<bool> _preferCoresOverCoal; private static bool _nviewReadFailureLogged; internal static bool PatchesReady; private Harmony _harmony; private void Awake() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ConfigurationManagerAttributes val = new ConfigurationManagerAttributes { IsAdminOnly = true }; AdditionalFuelPrefabs = ((BaseUnityPlugin)this).Config.Bind<string>("Fuel", "Additional Fuel Prefabs", "BlackCore", new ConfigDescription("Comma-, semicolon-, or line-separated item prefab names that ItemHopper may feed as SurtlingFuel core fuel into the vanilla smelter and blast furnace only. SurtlingCore is always enabled. Coal is always ignored. Every listed item receives SurtlingFuel's configured fuel value and speed boost. [Synced with Server]", (AcceptableValueBase)null, new object[1] { val })); RebuildAlternativeFuelSet(); AdditionalFuelPrefabs.SettingChanged += OnFuelListChanged; Type type = AccessTools.TypeByName("SurtlingFuel.SurtlingFuelPlugin"); Type patchesType = AccessTools.TypeByName("SurtlingFuel.SmelterPatches"); _getFuelPerCore = ((type == null) ? null : AccessTools.Method(type, "GetFuelPerCore", new Type[1] { typeof(Smelter) }, (Type[])null)); _nviewField = AccessTools.Field(typeof(Smelter), "m_nview"); _isOreAllowed = AccessTools.Method(typeof(Smelter), "IsItemAllowed", new Type[1] { typeof(ItemData) }, (Type[])null); _getOreQueueSize = AccessTools.Method(typeof(Smelter), "GetQueueSize", Type.EmptyTypes, (Type[])null); _preferCoresOverCoal = ((type == null) ? null : (AccessTools.Field(type, "PreferCoresOverCoal")?.GetValue(null) as ConfigEntry<bool>)); if (!ResolveSurtlingFuelPatchMethods(patchesType) || _getFuelPerCore == null || _nviewField == null || _isOreAllowed == null || _getOreQueueSize == null || _preferCoresOverCoal == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"A required SurtlingFuel 1.4.0/Smelter member was not found. No compatibility or restriction patches were installed."); return; } _harmony = new Harmony("local.valheim.surtlingfuelitemhopperfix"); try { ReplaceSurtlingFuelPatches(); InstallDirectSurtlingFuelGuards(); ValheimApiCompatibility.InstallHopperAdapters(_harmony); _harmony.PatchAll(typeof(Plugin).Assembly); PatchesReady = true; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Core-fuel compatibility could not start; core fueling is disabled by the replacement hooks. " + ex)); return; } ((BaseUnityPlugin)this).Logger.LogInfo((object)("Loaded SurtlingFuel ItemHopper Fix 1.2.2. Core fuel is restricted to prefab names 'smelter' and 'blastfurnace'. Alternative hopper fuels: " + string.Join(", ", _alternativeFuelPrefabs) + ".")); } private void OnDestroy() { PatchesReady = false; if (AdditionalFuelPrefabs != null) { AdditionalFuelPrefabs.SettingChanged -= OnFuelListChanged; } if (_harmony != null) { _harmony.UnpatchSelf(); } } private static void OnFuelListChanged(object sender, EventArgs eventArgs) { RebuildAlternativeFuelSet(); Log.LogInfo((object)("Updated alternative hopper fuels: " + string.Join(", ", _alternativeFuelPrefabs) + ".")); } private static void RebuildAlternativeFuelSet() { HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "SurtlingCore" }; string[] array = ((AdditionalFuelPrefabs == null) ? string.Empty : AdditionalFuelPrefabs.Value).Split(new char[4] { ',', ';', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length != 0) { if (string.Equals(text, "Coal", StringComparison.OrdinalIgnoreCase)) { Log.LogWarning((object)"Ignored Coal in Additional Fuel Prefabs; vanilla coal must use ItemHopper's normal fuel RPC."); } else { hashSet.Add(text); } } } _alternativeFuelPrefabs = hashSet; } private static bool ResolveSurtlingFuelPatchMethods(Type patchesType) { if (patchesType == null) { return false; } _surtlingAwakePostfix = AccessTools.Method(patchesType, "Awake_Postfix", new Type[1] { typeof(Smelter) }, (Type[])null); _surtlingOnAddFuelPrefix = AccessTools.Method(patchesType, "OnAddFuel_Prefix", new Type[5] { typeof(Smelter), typeof(bool).MakeByRefType(), typeof(Switch), typeof(Humanoid), typeof(ItemData) }, (Type[])null); _surtlingOnHoverAddFuelPostfix = AccessTools.Method(patchesType, "OnHoverAddFuel_Postfix", new Type[2] { typeof(Smelter), typeof(string).MakeByRefType() }, (Type[])null); _surtlingUpdateSmelterPrefix = AccessTools.Method(patchesType, "UpdateSmelter_Prefix", new Type[2] { typeof(Smelter), typeof(float).MakeByRefType() }, (Type[])null); _surtlingUpdateSmelterPostfix = AccessTools.Method(patchesType, "UpdateSmelter_Postfix", new Type[2] { typeof(Smelter), typeof(float) }, (Type[])null); if (_surtlingAwakePostfix != null && _surtlingOnAddFuelPrefix != null && _surtlingOnHoverAddFuelPostfix != null && _surtlingUpdateSmelterPrefix != null) { return _surtlingUpdateSmelterPostfix != null; } return false; } private void ReplaceSurtlingFuelPatches() { MethodInfo methodInfo = AccessTools.Method(typeof(Smelter), "Awake", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(Smelter), "OnAddFuel", new Type[3] { typeof(Switch), typeof(Humanoid), typeof(ItemData) }, (Type[])null); MethodInfo methodInfo3 = AccessTools.Method(typeof(Smelter), "OnHoverAddFuel", (Type[])null, (Type[])null); MethodInfo methodInfo4 = AccessTools.Method(typeof(Smelter), "UpdateSmelter", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null || methodInfo3 == null || methodInfo4 == null) { throw new MissingMethodException("One or more Valheim Smelter methods required by SurtlingFuel 1.4.0 were not found."); } _harmony.Unpatch((MethodBase)methodInfo, _surtlingAwakePostfix); _harmony.Unpatch((MethodBase)methodInfo2, _surtlingOnAddFuelPrefix); _harmony.Unpatch((MethodBase)methodInfo3, _surtlingOnHoverAddFuelPostfix); _harmony.Unpatch((MethodBase)methodInfo4, _surtlingUpdateSmelterPrefix); _harmony.Unpatch((MethodBase)methodInfo4, _surtlingUpdateSmelterPostfix); _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, GuardMethod("AwakePostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)methodInfo2, GuardMethod("OnAddFuelPrefix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, GuardMethod("OnHoverAddFuelPostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)methodInfo4, GuardMethod("UpdateSmelterPrefix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, GuardMethod("UpdateSmelterPostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private void InstallDirectSurtlingFuelGuards() { _harmony.Patch((MethodBase)_surtlingAwakePostfix, GuardMethod("RegisterDependencyRpcPrefix", 800), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)_surtlingOnHoverAddFuelPostfix, GuardMethod("VoidDependencyPatchPrefix", 800), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)_surtlingUpdateSmelterPrefix, GuardMethod("VoidDependencyPatchPrefix", 800), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)_surtlingUpdateSmelterPostfix, GuardMethod("VoidDependencyPatchPrefix", 800), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)_getFuelPerCore, GuardMethod("GetFuelPerCorePrefix", 800), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static HarmonyMethod GuardMethod(string methodName, int priority = 400) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown return new HarmonyMethod(typeof(SurtlingFuelPatchBridge), methodName, (Type[])null) { priority = priority }; } internal static bool IsAlternativeFuel(ItemData item) { if (item == null) { return false; } if ((Object)(object)item.m_dropPrefab != (Object)null && _alternativeFuelPrefabs.Contains(((Object)item.m_dropPrefab).name)) { return true; } string a = ((item.m_shared == null) ? null : item.m_shared.m_name); if (string.Equals(a, "$item_surtlingcore", StringComparison.Ordinal)) { return true; } if (_alternativeFuelPrefabs.Contains("BlackCore")) { return string.Equals(a, "$item_blackcore", StringComparison.Ordinal); } return false; } internal static bool IsAllowedMachine(Smelter smelter) { if (TryGetNView(smelter, out var nview)) { ZDO zDO = nview.GetZDO(); if (zDO != null) { int prefab = zDO.GetPrefab(); if (prefab != 0) { if (prefab != SmelterPrefabHash) { return prefab == BlastFurnacePrefabHash; } return true; } } } string machinePrefabName = GetMachinePrefabName(smelter); if (!string.Equals(machinePrefabName, "smelter", StringComparison.OrdinalIgnoreCase)) { return string.Equals(machinePrefabName, "blastfurnace", StringComparison.OrdinalIgnoreCase); } return true; } internal static string GetMachinePrefabName(Smelter smelter) { if ((Object)(object)smelter == (Object)null || (Object)(object)((Component)smelter).gameObject == (Object)null) { return string.Empty; } string text = ((Object)((Component)smelter).gameObject).name ?? string.Empty; if (text.EndsWith("(Clone)", StringComparison.OrdinalIgnoreCase)) { text = text.Substring(0, text.Length - "(Clone)".Length); } return text.Trim(); } internal static void ClearDisallowedCoreFuelMarker(Smelter smelter) { if (!IsAllowedMachine(smelter) && TryGetNView(smelter, out var nview) && nview.IsOwner()) { ZDO zDO = nview.GetZDO(); if (zDO != null && zDO.GetInt(CoreFuelZdoKey, 0) > 0) { zDO.Set(CoreFuelZdoKey, 0, false); Log.LogWarning((object)("Cleared stale SurtlingFuel core state from disallowed machine prefab '" + GetMachinePrefabName(smelter) + "'. Existing generic fuel was preserved.")); } } } internal static bool IsCoreFuelActive(Smelter smelter) { if (TryReadFuelState(smelter, out var _, out var _, out var coreFuel)) { return coreFuel > 0; } return false; } internal static bool CanAcceptAlternativeFuel(Smelter smelter) { if (!PatchesReady || !IsAllowedMachine(smelter)) { return false; } if (!TryReadFuelState(smelter, out var _, out var fuel, out var coreFuel)) { return false; } bool num = fuel > 0.01f && coreFuel <= 0; bool flag = fuel <= (float)smelter.m_maxFuel - 1f; return !num && flag; } internal static bool CanAcceptOrdinaryFuel(Smelter smelter, ItemData item) { if ((Object)(object)smelter == (Object)null || item == null || item.m_shared == null || (Object)(object)smelter.m_fuelItem == (Object)null || smelter.m_fuelItem.m_itemData.m_shared == null || smelter.m_fuelItem.m_itemData.m_shared.m_name != item.m_shared.m_name || (IsAllowedMachine(smelter) && IsCoreFuelActive(smelter))) { return false; } if (TryReadFuelState(smelter, out var _, out var fuel, out var _)) { return fuel <= (float)smelter.m_maxFuel - 1f; } return false; } internal static bool TryAddOrdinaryFuel(Smelter smelter, ItemData item, Inventory source) { if (source == null || !CanAcceptOrdinaryFuel(smelter, item) || !TryGetNView(smelter, out var nview) || !source.RemoveItem(item, 1)) { return false; } nview.InvokeRPC("RPC_AddFuel", new object[0]); return true; } internal static bool CanAcceptOre(Smelter smelter, ItemData item) { if (!PatchesReady || item == null || (Object)(object)item.m_dropPrefab == (Object)null || !TryGetNView(smelter, out var _) || (IsAlternativeFuel(item) && !IsAllowedMachine(smelter))) { return false; } if ((bool)_isOreAllowed.Invoke(smelter, new object[1] { item })) { return (int)_getOreQueueSize.Invoke(smelter, null) < smelter.m_maxOre; } return false; } internal static bool TryAddOre(Smelter smelter, ItemData item, Inventory source) { if (source == null || !CanAcceptOre(smelter, item) || !TryGetNView(smelter, out var nview)) { return false; } string name = ((Object)item.m_dropPrefab).name; bool cheated = item.m_cheated; if (!source.RemoveItem(item, 1)) { return false; } nview.InvokeRPC("RPC_AddOre", new object[2] { name, cheated }); return true; } internal static bool TryAddAlternativeFuel(Smelter smelter, ItemData item, Inventory source) { if (source == null || !CanAcceptAlternativeFuel(smelter)) { return false; } if (!TryReadFuelState(smelter, out var _, out var fuel, out var _)) { return false; } int val; try { val = (int)_getFuelPerCore.Invoke(null, new object[1] { smelter }); } catch (Exception ex) { Log.LogError((object)("Could not read SurtlingFuel's configured fuel value: " + ex)); return false; } int val2 = smelter.m_maxFuel - (int)Math.Ceiling(fuel); int num = Math.Min(val, val2); if (num <= 0) { return false; } if (!TryGetNView(smelter, out var nview)) { return false; } if (!source.RemoveItem(item, 1)) { return false; } nview.InvokeRPC("RPC_SurtlingFuel_AddCoreFuel", new object[1] { num }); return true; } private static bool TryReadFuelState(Smelter smelter, out ZDO zdo, out float fuel, out int coreFuel) { zdo = null; fuel = 0f; coreFuel = 0; if (!TryGetNView(smelter, out var nview)) { return false; } zdo = nview.GetZDO(); if (zdo == null) { return false; } fuel = zdo.GetFloat(ZDOVars.s_fuel, 0f); coreFuel = zdo.GetInt(CoreFuelZdoKey, 0); return true; } private static bool TryGetNView(Smelter smelter, out ZNetView nview) { nview = null; if ((Object)(object)smelter == (Object)null || _nviewField == null) { return false; } try { object? value = _nviewField.GetValue(smelter); nview = (ZNetView)((value is ZNetView) ? value : null); return (Object)(object)nview != (Object)null && nview.IsValid(); } catch (Exception ex) { if (!_nviewReadFailureLogged) { _nviewReadFailureLogged = true; Log.LogError((object)("Could not read Smelter.m_nview; hopper compatibility is failing closed: " + ex)); } return false; } } internal static bool InvokeSurtlingOnAddFuel(Smelter smelter, ref bool result, Switch fuelSwitch, Humanoid user, ItemData item) { if (!PatchesReady || !IsAllowedMachine(smelter) || (Object)(object)user == (Object)null) { return true; } Inventory inventory = user.GetInventory(); if (inventory == null || !TryReadFuelState(smelter, out var _, out var fuel, out var coreFuel)) { return true; } bool flag = coreFuel > 0; bool flag2 = fuel > 0.01f && !flag; ItemData val = null; if (item != null) { if (IsSurtlingCore(item)) { val = item; } } else { if (!_preferCoresOverCoal.Value && !flag && (Object)(object)smelter.m_fuelItem != (Object)null && inventory.HaveItem(smelter.m_fuelItem.m_itemData.m_shared.m_name, true)) { return true; } foreach (ItemData allItem in inventory.GetAllItems()) { if (IsSurtlingCore(allItem)) { val = allItem; break; } } } if (val == null && flag) { return RejectFuel(user, "Smelter is burning Surtling Cores", ref result); } if (val != null && flag2) { return RejectFuel(user, "Smelter is burning Coal", ref result); } if (val == null) { return true; } if (fuel > (float)smelter.m_maxFuel - 1f) { return RejectFuel(user, "$msg_itsfull", ref result); } result = TryAddAlternativeFuel(smelter, val, inventory); if (result) { ((Character)user).Message((MessageType)2, "Surtling Core added", 0, (Sprite)null, false); } return false; } private static bool IsSurtlingCore(ItemData item) { if (item != null) { if (!((Object)(object)item.m_dropPrefab != (Object)null) || !(((Object)item.m_dropPrefab).name == "SurtlingCore")) { if (item.m_shared != null) { return item.m_shared.m_name == "$item_surtlingcore"; } return false; } return true; } return false; } private static bool RejectFuel(Humanoid user, string message, ref bool result) { ((Character)user).Message((MessageType)2, message, 0, (Sprite)null, false); result = false; return false; } internal static void InvokeSurtlingAwake(Smelter smelter) { if (!TryGetNView(smelter, out var nview)) { return; } nview.Unregister("RPC_SurtlingFuel_AddCoreFuel"); if (!PatchesReady || !IsAllowedMachine(smelter)) { ClearDisallowedCoreFuelMarker(smelter); return; } nview.Register<int>("RPC_SurtlingFuel_AddCoreFuel", (Action<long, int>)delegate(long sender, int amount) { ReceiveCoreFuel(smelter, sender, amount); }); } internal static void ReceiveCoreFuel(Smelter smelter, long sender, int requested) { //IL_00aa: 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_00c6: Unknown result type (might be due to invalid IL or missing references) if (!PatchesReady || !TryGetNView(smelter, out var nview) || !nview.IsOwner()) { return; } if (!IsAllowedMachine(smelter)) { ClearDisallowedCoreFuelMarker(smelter); return; } ZDO zDO = nview.GetZDO(); float num = zDO.GetFloat(ZDOVars.s_fuel, 0f); int num2 = zDO.GetInt(CoreFuelZdoKey, 0); int num3 = FuelRules.AcceptedAmount(num, smelter.m_maxFuel, num2, requested); if (num3 > 0) { zDO.Set(ZDOVars.s_fuel, num + (float)num3); zDO.Set(CoreFuelZdoKey, (int)Math.Min(2147483647L, Math.Max(0L, num2) + num3), false); EffectList fuelAddedEffects = smelter.m_fuelAddedEffects; if (fuelAddedEffects != null) { fuelAddedEffects.Create(((Component)smelter).transform.position, ((Component)smelter).transform.rotation, ((Component)smelter).transform, 1f, -1, ZDOID.None); } } } internal static void InvokeSurtlingHover(Smelter smelter, ref string result) { if (PatchesReady) { object[] array = new object[2] { smelter, result }; if (InvokeSurtlingVoidPatch(_surtlingOnHoverAddFuelPostfix, array, "hover-text")) { result = array[1] as string; } } } internal static void InvokeSurtlingUpdatePrefix(Smelter smelter, ref float state) { if (PatchesReady) { object[] array = new object[2] { smelter, state }; if (InvokeSurtlingVoidPatch(_surtlingUpdateSmelterPrefix, array, "UpdateSmelter prefix")) { state = (float)array[1]; } } } internal static void InvokeSurtlingUpdatePostfix(Smelter smelter, float state) { if (PatchesReady) { InvokeSurtlingVoidPatch(_surtlingUpdateSmelterPostfix, new object[2] { smelter, state }, "UpdateSmelter postfix"); } } private static bool InvokeSurtlingVoidPatch(MethodInfo patch, object[] arguments, string pathName) { try { patch.Invoke(null, arguments); return true; } catch (Exception ex) { Log.LogError((object)("SurtlingFuel " + pathName + " patch failed: " + ex)); return false; } } internal static Smelter GetFirstSmelterArgument(object[] arguments) { if (arguments != null && arguments.Length != 0) { object obj = arguments[0]; return (Smelter)((obj is Smelter) ? obj : null); } return null; } } internal static class SurtlingFuelPatchBridge { internal static bool VoidDependencyPatchPrefix(object[] __args) { Smelter firstSmelterArgument = Plugin.GetFirstSmelterArgument(__args); if (Plugin.IsAllowedMachine(firstSmelterArgument)) { return true; } Plugin.ClearDisallowedCoreFuelMarker(firstSmelterArgument); return false; } internal static bool RegisterDependencyRpcPrefix(object[] __args) { Plugin.InvokeSurtlingAwake(Plugin.GetFirstSmelterArgument(__args)); return false; } internal static void AwakePostfix(Smelter __instance) { Plugin.InvokeSurtlingAwake(__instance); } internal static bool OnAddFuelPrefix(Smelter __instance, ref bool __result, Switch sw, Humanoid user, ItemData item) { if (!Plugin.IsAllowedMachine(__instance)) { Plugin.ClearDisallowedCoreFuelMarker(__instance); if (Plugin.IsAlternativeFuel(item) || ((Object)(object)__instance.m_fuelItem != (Object)null && Plugin.IsAlternativeFuel(__instance.m_fuelItem.m_itemData))) { __result = false; return false; } return true; } return Plugin.InvokeSurtlingOnAddFuel(__instance, ref __result, sw, user, item); } internal static void OnHoverAddFuelPostfix(Smelter __instance, ref string __result) { if (Plugin.IsAllowedMachine(__instance)) { Plugin.InvokeSurtlingHover(__instance, ref __result); } } internal static void UpdateSmelterPrefix(Smelter __instance, ref float __state) { if (Plugin.IsAllowedMachine(__instance)) { Plugin.InvokeSurtlingUpdatePrefix(__instance, ref __state); } else { Plugin.ClearDisallowedCoreFuelMarker(__instance); } } internal static void UpdateSmelterPostfix(Smelter __instance, float __state) { if (Plugin.IsAllowedMachine(__instance)) { Plugin.InvokeSurtlingUpdatePostfix(__instance, __state); } } internal static bool GetFuelPerCorePrefix(Smelter smelter, ref int __result) { if (Plugin.IsAllowedMachine(smelter)) { return true; } __result = 0; Plugin.ClearDisallowedCoreFuelMarker(smelter); return false; } } internal static class FuelRules { internal static int AcceptedAmount(float fuel, int maximum, int coreFuel, int requested) { if (requested <= 0 || maximum <= 0 || float.IsNaN(fuel) || float.IsInfinity(fuel) || fuel < 0f || fuel > (float)maximum - 1f || (fuel > 0.01f && coreFuel <= 0)) { return 0; } int val = maximum - (int)Math.Ceiling(fuel); return Math.Min(requested, val); } } [HarmonyPatch(typeof(SmelterFuelTarget), "CanAddItem")] internal static class SmelterFuelTargetCanAddItemPatch { [HarmonyPrefix] [HarmonyPriority(800)] private static bool Prefix(SmelterFuelTarget __instance, ItemData item, ref bool __result) { Smelter component = ((Component)__instance).GetComponent<Smelter>(); if (Plugin.IsAlternativeFuel(item)) { __result = Plugin.CanAcceptAlternativeFuel(component); return false; } __result = Plugin.CanAcceptOrdinaryFuel(component, item); return false; } } [HarmonyPatch(typeof(SmelterFuelTarget), "AddItem")] internal static class SmelterFuelTargetAddItemPatch { [HarmonyPrefix] [HarmonyPriority(800)] private static bool Prefix(SmelterFuelTarget __instance, ItemData item, Inventory source) { Smelter component = ((Component)__instance).GetComponent<Smelter>(); if (Plugin.IsAlternativeFuel(item)) { Plugin.TryAddAlternativeFuel(component, item, source); return false; } Plugin.TryAddOrdinaryFuel(component, item, source); return false; } } [HarmonyPatch(typeof(SmelterOreTarget), "CanAddItem")] internal static class SmelterOreTargetCanAddItemPatch { [HarmonyPrefix] private static bool Prefix(SmelterOreTarget __instance, ItemData item, ref bool __result) { __result = Plugin.CanAcceptOre(((Component)__instance).GetComponent<Smelter>(), item); return false; } } [HarmonyPatch(typeof(SmelterOreTarget), "AddItem")] internal static class SmelterOreTargetAddItemPatch { [HarmonyPrefix] private static bool Prefix(SmelterOreTarget __instance, ItemData item, Inventory source) { Plugin.TryAddOre(((Component)__instance).GetComponent<Smelter>(), item, source); return false; } } [HarmonyPatch(typeof(Smelter), "RPC_AddFuel", new Type[] { typeof(long) })] internal static class SmelterOrdinaryFuelRpcPatch { [HarmonyPrefix] private static bool Prefix(Smelter __instance) { if (!Plugin.IsAllowedMachine(__instance)) { if (!((Object)(object)__instance.m_fuelItem == (Object)null)) { return !Plugin.IsAlternativeFuel(__instance.m_fuelItem.m_itemData); } return true; } return !Plugin.IsCoreFuelActive(__instance); } } internal static class ValheimApiCompatibility { private const BindingFlags InstanceMembers = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static readonly FieldInfo GridElements = typeof(InventoryGrid).GetField("m_elements", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo CurrentContainer = typeof(InventoryGui).GetField("m_currentContainer", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo ContainerGrid = typeof(InventoryGui).GetField("m_containerGrid", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly MethodInfo AddItemAtPosition = typeof(Inventory).GetMethod("AddItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[5] { typeof(ItemData), typeof(int), typeof(int), typeof(int), typeof(bool) }, null); private static readonly MethodInfo HoneyLevel = typeof(Beehive).GetMethod("GetHoneyLevel", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly MethodInfo PullRemove = typeof(IPullTarget).GetMethod("RemoveItem"); private static readonly MethodInfo PullReplacement = typeof(ValheimApiCompatibility).GetMethod("RemoveFromTarget"); internal static void InstallHopperAdapters(Harmony harmony) { //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Expected O, but got Unknown //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Expected O, but got Unknown if (GridElements == null || CurrentContainer == null || ContainerGrid == null || AddItemAtPosition == null || HoneyLevel == null || PullRemove == null || PullReplacement == null) { throw new MissingMemberException("The supplied Valheim inventory API is not supported by this build."); } MethodInfo methodInfo = AccessTools.Method(typeof(SmelterFuelTarget).Assembly.GetType("ValheimHopper.Patches.InventoryGuiPatch", throwOnError: true), "UpdateContainerPostfix", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(InventoryGui), "UpdateContainer", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { throw new MissingMethodException("The ItemHopper inventory UI patch could not be resolved."); } harmony.Unpatch((MethodBase)methodInfo2, methodInfo); harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(ValheimApiCompatibility), "UpdateContainerPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); MethodInfo methodInfo3 = AccessTools.Method(typeof(Hopper), "PullItems", Type.EmptyTypes, (Type[])null); if (methodInfo3 == null) { throw new MissingMethodException(typeof(Hopper).FullName, "PullItems"); } harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(ValheimApiCompatibility), "RoutePullTransfers", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); } internal static IEnumerable<CodeInstruction> RoutePullTransfers(IEnumerable<CodeInstruction> instructions) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown List<CodeInstruction> list = new List<CodeInstruction>(); int num = 0; foreach (CodeInstruction instruction in instructions) { CodeInstruction val = new CodeInstruction(instruction); if ((val.opcode == OpCodes.Callvirt || val.opcode == OpCodes.Call) && object.Equals(val.operand, PullRemove)) { val.opcode = OpCodes.Call; val.operand = PullReplacement; num++; } list.Add(val); } if (num != 1) { throw new InvalidOperationException("Expected one ItemHopper pull-transfer call, found " + num + "."); } return list; } public static void RemoveFromTarget(IPullTarget target, ItemData item, Inventory destination, Vector2i position, ZDOID sender) { //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_014f: 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_0164: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) if (target == null || item == null || destination == null) { return; } BeehiveTarget val = (BeehiveTarget)(object)((target is BeehiveTarget) ? target : null); if (val != null) { if (!Plugin.PatchesReady) { return; } Beehive component = ((Component)val).GetComponent<Beehive>(); ZNetView val2 = (((Object)(object)component == (Object)null) ? null : ((Component)component).GetComponent<ZNetView>()); if ((Object)(object)val2 == (Object)null || !val2.IsValid()) { return; } if (!val2.IsOwner()) { val2.InvokeRPC("VH_RequestOwnership", new object[0]); return; } int num = (int)HoneyLevel.Invoke(component, null); if (num > 0) { ItemData val3 = item.Clone(); val3.m_stack = 1; if (AddInventoryItem(destination, val3, 1, position.x, position.y)) { val2.GetZDO().Set(ZDOVars.s_level, num - 1, false); PreserveProvenance(destination, position, val3); } } return; } ItemDropTarget val4 = (ItemDropTarget)(object)((target is ItemDropTarget) ? target : null); if (val4 != null) { if (!Plugin.PatchesReady) { return; } ItemDrop component2 = ((Component)val4).GetComponent<ItemDrop>(); ZNetView val5 = (((Object)(object)component2 == (Object)null) ? null : ((Component)component2).GetComponent<ZNetView>()); if ((Object)(object)val5 == (Object)null || !val5.IsValid() || component2.m_itemData == null || component2.m_itemData.m_stack <= 0) { return; } if (!val5.IsOwner()) { component2.RequestOwn(); return; } ItemData val6 = component2.m_itemData.Clone(); val6.m_stack = 1; if (!AddInventoryItem(destination, val6, 1, position.x, position.y)) { return; } ItemData itemAt = destination.GetItemAt(position.x, position.y); if (!component2.RemoveOne()) { if (itemAt == null || !destination.RemoveItem(itemAt, 1)) { Plugin.Log.LogError((object)"Could not roll back a refused hopper item pickup."); } } else { PreserveProvenance(destination, position, val6); } } else { target.RemoveItem(item, destination, position, sender); } } private static void UpdateContainerPostfix(InventoryGui __instance) { //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) object? value = CurrentContainer.GetValue(__instance); Container val = (Container)((value is Container) ? value : null); Hopper val2 = default(Hopper); if ((Object)(object)val == (Object)null || !((Component)val).TryGetComponent<Hopper>(ref val2) || !((ZProperty<bool>)(object)val2.FilterItemsOption).Get()) { return; } object? value2 = ContainerGrid.GetValue(__instance); InventoryGrid val3 = (InventoryGrid)((value2 is InventoryGrid) ? value2 : null); List<InventoryElement> list = (((Object)(object)val3 == (Object)null) ? null : (GridElements.GetValue(val3) as List<InventoryElement>)); if (list == null || (Object)(object)ObjectDB.instance == (Object)null) { return; } foreach (InventoryElement item in list) { if ((Object)(object)item == (Object)null || (Object)(object)item.m_icon == (Object)null || ((Behaviour)item.m_icon).enabled) { continue; } int itemHash = val2.filter.GetItemHash(item.Position); GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(itemHash); ItemDrop val4 = (((Object)(object)itemPrefab == (Object)null) ? null : itemPrefab.GetComponent<ItemDrop>()); if (!((Object)(object)val4 == (Object)null) && val4.m_itemData != null) { ((Behaviour)item.m_icon).enabled = true; item.m_icon.sprite = val4.m_itemData.GetIcon(); ((Graphic)item.m_icon).color = new Color(0.9f, 0.9f, 0.9f, 0.5f); if ((Object)(object)item.m_amount != (Object)null) { ((Behaviour)item.m_amount).enabled = true; item.m_amount.text = "0/" + val4.m_itemData.m_shared.m_maxStackSize; } } } } public static bool AddInventoryItem(Inventory inventory, ItemData item, int amount, int x, int y) { return (bool)AddItemAtPosition.Invoke(inventory, new object[5] { item, amount, x, y, false }); } private static void PreserveProvenance(Inventory destination, Vector2i position, ItemData source) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) ItemData itemAt = destination.GetItemAt(position.x, position.y); if (itemAt != null && source.m_cheated && !itemAt.m_cheated) { itemAt.m_cheated = true; destination.m_onChanged?.Invoke(); } } } }