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 AzuCraftyBoxesBuildingOnly v1.6.4
BepInEx/plugins/AzuCraftyBoxesBuildingOnly/AzuCraftyBoxesBuildingOnly.dll
Decompiled a day agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Collections.Generic; using MonoMod.Cil; using MonoMod.Utils; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("AzuCraftyBoxes Fixes & Config")] [assembly: AssemblyProduct("AzuCraftyBoxes Fixes & Config")] [assembly: AssemblyFileVersion("1.6.3.0")] [assembly: AssemblyVersion("1.6.3.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 AzuCraftyBoxesBuildingOnly { [BepInPlugin("local.valheim.azucraftyboxesbuildingonly", "AzuCraftyBoxes Fixes & Config", "1.6.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInIncompatibility("local.valheim.compatibilityfixes")] public sealed class Plugin : BaseUnityPlugin { private enum LegacyMode { Full, BuildingOnly, Disabled } private static class AzuShouldPreventPatch { public static void Postfix(ref bool __result) { if (IsReady && IsReady && ModEnabled.Value && !AzuCallContext.InInfrastructure && !FeaturePolicy.Allowed(AzuCallContext.InCrafting, AzuCallContext.InBuilding || PlacementConsumptionMarker.Active, AzuCallContext.InMachine, CraftTransaction.Active, CraftTransaction.AllowNearbyContainers, BuildingFromContainers.Value, CraftingFromContainers.Value, MachinesFromContainers.Value)) { __result = true; } } } public const string PluginGuid = "local.valheim.azucraftyboxesbuildingonly"; public const string PluginName = "AzuCraftyBoxes Fixes & Config"; public const string PluginVersion = "1.6.3"; public const string AzuCraftyBoxesGuid = "Azumatt.AzuCraftyBoxes"; public const string CombinedPluginGuid = "local.valheim.compatibilityfixes"; internal static ConfigEntry<bool> ModEnabled; internal static ConfigEntry<bool> BuildingFromContainers; internal static ConfigEntry<bool> CraftingFromContainers; internal static ConfigEntry<bool> MachinesFromContainers; internal static ConfigEntry<KeyboardShortcut> ToggleBuilding; internal static ConfigEntry<KeyboardShortcut> ToggleCrafting; internal static ConfigEntry<KeyboardShortcut> ToggleMachines; internal static ManualLogSource Log; private Harmony _harmony; private PatchSession _patches; private StartupSequence _startup; private static StartupSequence _currentStartup; private bool _versionRegistrationStarted; private FileSystemWatcher _configWatcher; private readonly object _configReloadLock = new object(); private DateTime _lastConfigReloadTime; private const long ConfigReloadDelayTicks = 5000000L; internal static bool IsReady { get { if (_currentStartup != null) { return _currentStartup.Ready; } return false; } } private void Awake() { Log = ((BaseUnityPlugin)this).Logger; _startup = new StartupSequence(delegate(string message) { ((BaseUnityPlugin)this).Logger.LogError((object)message); }); _currentStartup = _startup; Assembly azu = null; if (_startup.Run(delegate { azu = BaseModHealth.RequireInitialized(); }, delegate { //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown _startup.Step("binding local configuration", BindConfiguration); _startup.Step("validating game contracts", RuntimeContract.Validate); List<Tuple<MethodBase, bool>> targets = AzuGameApiAdapter.Prepare(azu, _startup); _startup.Step("initializing the Azu runtime bridge", delegate { AzuRuntimeBridge.Initialize(((BaseUnityPlugin)this).Logger); }); _harmony = new Harmony("local.valheim.azucraftyboxesbuildingonly"); _patches = new PatchSession(_harmony, _startup, delegate(string message) { ((BaseUnityPlugin)this).Logger.LogError((object)message); }); AzuGameApiAdapter.Install(_patches, targets); foreach (Type item in IntegrationPatchTypes(azu.GetName())) { _patches.ApplyClass(item); } InstallAzuCraftyBoxesGate(); _startup.Step("starting the config watcher", SetupConfigWatcher); _startup.Step("registering multiplayer version checks", delegate { _versionRegistrationStarted = true; AzuServerSyncBridge.Initialize(((BaseUnityPlugin)this).Logger); }); }, () => Cleanup(startupFailure: true))) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Loaded successfully: AzuCraftyBoxes Fixes & Config 1.6.3. All required initialization and " + _patches.InstalledCount + " patch installations completed. Base profile=" + BaseModCompatibility.Resolve(azu.GetName()).ToString() + ". Per-player settings: building=" + BuildingFromContainers.Value + ", crafting=" + CraftingFromContainers.Value + ", machines=" + MachinesFromContainers.Value + ".")); } } internal static IEnumerable<Type> IntegrationPatchTypes(AssemblyName baseAssembly) { bool replaceFermenter = BaseModCompatibility.RequiresFermenterReplacement(baseAssembly); Type[] array = new Type[18] { typeof(MachineItemRemovalMetadataPatch), typeof(MachineSingleItemMetadataPatch), typeof(CookingMetadataPatch), typeof(FermenterTransferCompatibilityPatch), typeof(BuildingOperationContextPatch), typeof(PlacementConsumptionMarkerPatch), typeof(BuildingConsumptionSafetyPatch), typeof(UpgraderRequirementFilterPatch), typeof(UpgraderRecipeFilterPatch), typeof(UpgraderRecipeAmountFilterPatch), typeof(CraftingOperationContextPatch), typeof(CraftTransactionStartPatch), typeof(CraftTransactionFinishPatch), typeof(CraftTransactionCancelPatch), typeof(AzuCraftingStateCleanupPatch), typeof(MachineOperationContextPatch), typeof(AzuInfrastructureContextPatch), typeof(ContainerCheatedResourcesPatch) }; foreach (Type type in array) { if (!(type == typeof(FermenterTransferCompatibilityPatch)) || replaceFermenter) { yield return type; } } } private void BindConfiguration() { //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) ModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Mod Enabled", true, "Master switch for this add-on on this player. When off, this add-on stops filtering AzuCraftyBoxes and AzuCraftyBoxes behaves normally. Local player only; not synchronized."); BuildingFromContainers = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Per-Player Features", "Building From Nearby Chests", true, "Allow this player to count and consume nearby-container materials while placing building pieces. Changes take effect immediately. Local player only; not synchronized."); CraftingFromContainers = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Per-Player Features", "Crafting From Nearby Chests", false, "Allow this player to count and consume nearby-container materials for crafting, upgrading, and supported crafting-like interfaces. Changes take effect immediately. Local player only; not synchronized."); MachinesFromContainers = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Per-Player Features", "Machines From Nearby Chests", false, "Allow this player to fill smelters, cooking stations, fires, fermenters, shield generators, turrets, and other AzuCraftyBoxes-supported machines from nearby containers. Changes take effect immediately. Local player only; not synchronized."); ToggleBuilding = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("3 - Per-Player Hotkeys", "Toggle Building", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Toggle Building From Nearby Chests for this player. Rebind freely or set to None to disable the hotkey. Local player only; not synchronized."); ToggleCrafting = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("3 - Per-Player Hotkeys", "Toggle Crafting", new KeyboardShortcut((KeyCode)290, Array.Empty<KeyCode>()), "Toggle Crafting From Nearby Chests for this player. Rebind freely or set to None to disable the hotkey. Local player only; not synchronized."); ToggleMachines = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("3 - Per-Player Hotkeys", "Toggle Machines", new KeyboardShortcut((KeyCode)291, Array.Empty<KeyCode>()), "Toggle Machines From Nearby Chests for this player. Rebind freely or set to None to disable the hotkey. Local player only; not synchronized."); MigrateLegacyModeConfig(); } private void Update() { if (IsReady) { UpdatePlayerInput(); } } [MethodImpl(MethodImplOptions.NoInlining)] private void UpdatePlayerInput() { if (!((Object)(object)Player.m_localPlayer == (Object)null) && !Console.IsVisible() && !TextInput.IsVisible() && (!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus())) { HandleToggle(ToggleBuilding, BuildingFromContainers, "Building from nearby chests"); HandleToggle(ToggleCrafting, CraftingFromContainers, "Crafting from nearby chests"); HandleToggle(ToggleMachines, MachinesFromContainers, "Machines from nearby chests"); } } private static void HandleToggle(ConfigEntry<KeyboardShortcut> shortcut, ConfigEntry<bool> setting, string label) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (shortcut == null || setting == null) { return; } KeyboardShortcut value = shortcut.Value; if ((int)((KeyboardShortcut)(ref value)).MainKey != 0) { value = shortcut.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { setting.Value = !setting.Value; ShowFeatureMessage(label + ": " + (setting.Value ? "ON" : "OFF")); } } } private void SetupConfigWatcher() { _configWatcher = new FileSystemWatcher(Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath), Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath)); _configWatcher.Changed += ReloadConfig; _configWatcher.Created += ReloadConfig; _configWatcher.Renamed += ReloadConfig; _configWatcher.IncludeSubdirectories = false; _configWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; _configWatcher.EnableRaisingEvents = true; } private void ReloadConfig(object sender, FileSystemEventArgs args) { if (!IsReady) { return; } DateTime utcNow = DateTime.UtcNow; if (utcNow.Ticks - _lastConfigReloadTime.Ticks < 5000000) { return; } _lastConfigReloadTime = utcNow; lock (_configReloadLock) { try { ((BaseUnityPlugin)this).Config.Reload(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Reloaded local per-player configuration: building=" + BuildingFromContainers.Value + ", crafting=" + CraftingFromContainers.Value + ", machines=" + MachinesFromContainers.Value + ".")); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Failed to reload configuration: " + ex.Message)); } } } private void MigrateLegacyModeConfig() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Expected O, but got Unknown //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Expected O, but got Unknown try { if (!(typeof(ConfigFile).GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(((BaseUnityPlugin)this).Config, null) is IDictionary<ConfigDefinition, string> dictionary)) { return; } ConfigDefinition key = new ConfigDefinition("2 - AzuCraftyBoxes", "Mode"); bool flag = false; if (dictionary.TryGetValue(key, out var value) && Enum.TryParse<LegacyMode>(value.Trim(), ignoreCase: true, out var result)) { bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; try { BuildingFromContainers.Value = result != LegacyMode.Disabled; CraftingFromContainers.Value = result == LegacyMode.Full; MachinesFromContainers.Value = result == LegacyMode.Full; } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } dictionary.Remove(key); flag = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Migrated legacy shared Mode=" + result.ToString() + " to the new local per-player feature toggles.")); } flag |= dictionary.Remove(new ConfigDefinition("1 - General", "Lock Configuration")); if (flag | dictionary.Remove(new ConfigDefinition("2 - AzuCraftyBoxes", "Toggle Full and Building Only"))) { ((BaseUnityPlugin)this).Config.Save(); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not clean up legacy 1.1.0 config entries: " + ex.Message)); } } private bool Cleanup(bool startupFailure) { bool result = true; if (_configWatcher != null) { try { _configWatcher.Dispose(); _configWatcher = null; } catch (Exception ex) { result = false; ((BaseUnityPlugin)this).Logger.LogError((object)("Separate config-watcher cleanup failure: " + ex)); } } if (_patches != null && !_patches.Cleanup()) { result = false; } if (_versionRegistrationStarted && startupFailure) { result = false; ((BaseUnityPlugin)this).Logger.LogError((object)"Version registration was reached. Bundled ServerSync has no supported unregister operation; restart to clear that registration."); } return result; } private void OnDestroy() { _startup?.Stop(); if (_startup == null) { return; } try { if (!Cleanup(startupFailure: false)) { ((BaseUnityPlugin)this).Logger.LogError((object)"Add-on shutdown cleanup is incomplete or unverified; restart before playing."); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Separate add-on shutdown cleanup failure: " + ex)); } } private static void ShowFeatureMessage(string message) { Log.LogInfo((object)message); if ((Object)(object)Player.m_localPlayer != (Object)null) { ((Character)Player.m_localPlayer).Message((MessageType)2, message, 0, (Sprite)null, false); } } private void InstallAzuCraftyBoxesGate() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method("AzuCraftyBoxes.Util.Functions.MiscFunctions:ShouldPrevent", (Type[])null, (Type[])null); if (methodInfo == null) { throw new MissingMethodException("AzuCraftyBoxes ShouldPrevent was not found."); } HarmonyMethod val = new HarmonyMethod(typeof(AzuShouldPreventPatch), "Postfix", (Type[])null); val.priority = 0; val.after = new string[1] { "Azumatt.AzuCraftyBoxes" }; HarmonyMethod postfix = val; _patches.Apply(methodInfo, null, postfix); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Installed the local per-player AzuCraftyBoxes building/crafting/machine gate."); } } internal static class FeaturePolicy { internal static bool Allowed(bool crafting, bool building, bool machine, bool activeCraft, bool craftSnapshot, bool buildingChoice, bool craftingChoice, bool machineChoice) { if (crafting) { if (!activeCraft) { return craftingChoice; } return craftSnapshot; } if (building) { return buildingChoice; } if (machine) { return machineChoice; } if (!activeCraft) { return craftingChoice; } return craftSnapshot; } } internal static class RuntimeContract { internal static void Validate() { Require(typeof(Player), "UpdatePlacement", typeof(bool), typeof(float)); Require(typeof(Player), "TryPlacePiece", typeof(Piece)); Require(typeof(Player), "ConsumeResources", typeof(Requirement[]), typeof(int), typeof(int), typeof(int)); Require(typeof(InventoryGui), "OnCraftPressed"); Require(typeof(InventoryGui), "DoCrafting", typeof(Player)); Require(typeof(InventoryGui), "OnCraftCancelPressed"); Require(typeof(Inventory), "ItemCheated", typeof(Requirement[]), typeof(int), typeof(bool)); Require(typeof(Smelter), "RPC_AddOre", typeof(long), typeof(string), typeof(bool)); Require(typeof(Fermenter), "RPC_AddItem", typeof(long), typeof(int), typeof(bool)); Require(typeof(Fermenter), "GetStatus"); Require(typeof(Fermenter), "IsItemAllowed", typeof(ItemData)); Require(typeof(Character), "Message", typeof(MessageType), typeof(string), typeof(int), typeof(Sprite), typeof(bool)); Require(typeof(Inventory), "Changed", typeof(bool), typeof(bool)); Require(typeof(SEMan), "AddStatusEffect", typeof(StatusEffect), typeof(bool), typeof(int), typeof(float), typeof(short)); if (AccessTools.Field(typeof(InventoryGui), "m_craftTimer")?.FieldType != typeof(float)) { throw new MissingFieldException("InventoryGui.m_craftTimer float field is required."); } } private static void Require(Type type, string method, params Type[] parameters) { if (AccessTools.Method(type, method, parameters, (Type[])null) == null) { throw new MissingMethodException(type.FullName, method); } } } internal static class AzuGameApiAdapter { internal static List<Tuple<MethodBase, bool>> Prepare(Assembly azu, StartupSequence startup) { List<Tuple<MethodBase, bool>> targets = new List<Tuple<MethodBase, bool>>(); startup.Step("discovering Azu compatibility targets", delegate { targets.AddRange(Discover(azu)); }); if (Chainloader.PluginInfos.TryGetValue("Azumatt.BuildCameraCHE", out var camera) && startup.TryOptionalCheck("checking optional Build Camera compatibility", delegate { if (camera == null || camera.Instance == null) { throw new InvalidOperationException("Build Camera is present but has no initialized instance."); } Assembly cameraAssembly = ((object)camera.Instance).GetType().Assembly; startup.Step("discovering optional Build Camera targets", delegate { if (Discover(cameraAssembly).Count != 0) { throw new NotSupportedException("Optional camera validation must not install dependency adapters."); } }); startup.Step("checking optional Build Camera hooks", delegate { BaseModHealth.RequireInstalledCallbacks(cameraAssembly, "Azumatt.BuildCameraCHE", new string[3] { "Player_Update_Patch", "PlayerController_TakeInput_Patch", "GameCamera_UpdateCamera_Patch" }); }); }, delegate(string message) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)message); } })) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Optional Build Camera API and installed hooks checked. Placement uses the normal Player material safeguards."); } } List<Exception> failures = new List<Exception>(); foreach (Tuple<MethodBase, bool> target in targets) { startup.Step("preflighting compatibility target " + PatchSession.Describe(target.Item1), delegate { try { ProbeTarget(target.Item1, target.Item2); } catch (Exception innerException) { failures.Add(new NotSupportedException("Cannot read/adapt the original method " + PatchSession.Describe(target.Item1) + " with " + typeof(Harmony).Assembly.GetName()?.ToString() + " and " + typeof(DynamicMethodDefinition).Assembly.GetName()?.ToString() + ". A later ILManipulator cannot repair a failure that occurs before it runs.", innerException)); } }); } if (failures.Count != 0) { throw new AggregateException("Compatibility preflight rejected " + failures.Count + " method(s). No add-on patches or ServerSync registrations were attempted. The installed dependency must have runtime-readable methods before this adapter can be used.", failures); } return targets; } internal static void ProbeTarget(MethodBase target, bool ore) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Invalid comparison between Unknown and I4 //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 //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown DynamicMethodDefinition val = new DynamicMethodDefinition(target); try { Enumerator<Instruction> enumerator = val.Definition.Body.Instructions.GetEnumerator(); try { while (enumerator.MoveNext()) { Instruction current = enumerator.Current; OpCode opCode = current.OpCode; if ((int)((OpCode)(ref opCode)).OperandType != 4) { opCode = current.OpCode; if ((int)((OpCode)(ref opCode)).OperandType != 1) { continue; } } if (current.Operand == null) { throw new MissingMemberException("MonoMod left an unresolved member at IL_" + current.Offset.ToString("x4") + "."); } } } finally { ((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } ILContext val2 = new ILContext(val.Definition); try { val2.Invoke((Manipulator)delegate(ILContext context) { AdaptCore(context, ore); }); } finally { ((IDisposable)val2)?.Dispose(); } val.Generate(); } finally { ((IDisposable)val)?.Dispose(); } } internal static void Install(PatchSession patches, List<Tuple<MethodBase, bool>> targets) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown foreach (Tuple<MethodBase, bool> target in targets) { patches.Apply(target.Item1, null, null, null, null, new HarmonyMethod(typeof(AzuGameApiAdapter), target.Item2 ? "AdaptOre" : "Adapt", (Type[])null)); } } private static List<Tuple<MethodBase, bool>> Discover(Assembly azu) { //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: 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) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_02ec: Unknown result type (might be due to invalid IL or missing references) //IL_02f1: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //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) bool flag = azu.GetName().Name == "AzuCraftyBoxes"; if (!flag && azu.GetName().Name != "Build Camera") { throw new NotSupportedException("Unknown compatibility assembly " + azu.GetName().Name); } if (flag) { BaseModCompatibility.Resolve(azu.GetName()); } else if (azu.GetName().Version != new Version(1, 2, 10, 0)) { throw new NotSupportedException("This compatibility profile requires Build Camera 1.2.10.0; detected " + azu.GetName().Version?.ToString() + "."); } int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; HashSet<MethodBase> hashSet = new HashSet<MethodBase>(); List<Tuple<MethodBase, bool>> list = new List<Tuple<MethodBase, bool>>(); AssemblyDefinition val = AssemblyDefinition.ReadAssembly(azu.Location); try { foreach (TypeDefinition item2 in AllTypes((IEnumerable<TypeDefinition>)val.MainModule.Types)) { Enumerator<MethodDefinition> enumerator2 = item2.Methods.GetEnumerator(); try { while (enumerator2.MoveNext()) { MethodDefinition current2 = enumerator2.Current; if (!current2.HasBody) { continue; } bool flag2 = false; bool flag3 = ((MemberReference)item2).FullName == "AzuCraftyBoxes.Patches.SmelterOnAddOrePatch"; Enumerator<Instruction> enumerator3 = current2.Body.Instructions.GetEnumerator(); MetadataToken metadataToken; try { while (enumerator3.MoveNext()) { Instruction current3 = enumerator3.Current; object operand = current3.Operand; int num5 = Classify((MethodReference)((operand is MethodReference) ? operand : null)); if (num5 >= 1 && num5 <= 3) { throw new NotSupportedException("Obsolete game API in " + ((MemberReference)current2).FullName + ". Replace the original dependency with the supported Valheim 1.0 rebuild; this add-on does not patch unreadable legacy methods."); } object operand2 = current3.Operand; MethodReference val2 = (MethodReference)((operand2 is MethodReference) ? operand2 : null); if (((val2 != null) ? ((MemberReference)((MemberReference)val2).DeclaringType).FullName : null) == "Character" && ((MemberReference)val2).Name == "Message" && val2.Parameters.Count == 5) { num3++; if (!flag) { Module manifestModule = azu.ManifestModule; metadataToken = ((MemberReference)current2).MetadataToken; hashSet.Add(manifestModule.ResolveMethod(((MetadataToken)(ref metadataToken)).ToInt32())); } } if (!flag && ((val2 != null) ? ((MemberReference)((MemberReference)val2).DeclaringType).FullName : null) == "Player" && ((MemberReference)val2).Name == "UpdatePlacement" && val2.Parameters.Count == 2 && ((MemberReference)((ParameterReference)val2.Parameters[0]).ParameterType).FullName == "System.Boolean" && ((MemberReference)((ParameterReference)val2.Parameters[1]).ParameterType).FullName == "System.Single") { num4++; Module manifestModule2 = azu.ManifestModule; metadataToken = ((MemberReference)current2).MetadataToken; hashSet.Add(manifestModule2.ResolveMethod(((MetadataToken)(ref metadataToken)).ToInt32())); } if (num5 == 4 && flag3) { flag2 = true; num2++; } } } finally { ((IDisposable)enumerator3/*cast due to .constrained prefix*/).Dispose(); } if (flag2) { Module manifestModule3 = azu.ManifestModule; metadataToken = ((MemberReference)current2).MetadataToken; MethodBase item = manifestModule3.ResolveMethod(((MetadataToken)(ref metadataToken)).ToInt32()); list.Add(Tuple.Create(item, flag3)); num++; } } } finally { ((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose(); } } } finally { ((IDisposable)val)?.Dispose(); } if (num2 != (flag ? 2 : 0) || num != (flag ? 1 : 0) || (!flag && (num3 != 2 || num4 != 1 || hashSet.Count != 3))) { throw new InvalidOperationException("Unexpected " + azu.GetName().Name + " API call-site counts; no partial adaptation is accepted."); } foreach (MethodBase item3 in hashSet) { ProbeTarget(item3, ore: false); } return list; } private static IEnumerable<TypeDefinition> AllTypes(IEnumerable<TypeDefinition> roots) { foreach (TypeDefinition type in roots) { yield return type; foreach (TypeDefinition item in AllTypes((IEnumerable<TypeDefinition>)type.NestedTypes)) { yield return item; } } } private static int Classify(MethodReference method) { if (method == null) { return 0; } string fullName = ((MemberReference)((MemberReference)method).DeclaringType).FullName; if (fullName == "Character" && ((MemberReference)method).Name == "Message" && method.Parameters.Count == 4) { return 1; } if (fullName == "Inventory" && ((MemberReference)method).Name == "Changed" && method.Parameters.Count == 0) { return 2; } if (fullName == "SEMan" && ((MemberReference)method).Name == "AddStatusEffect" && method.Parameters.Count == 4 && ((MemberReference)((ParameterReference)method.Parameters[0]).ParameterType).FullName == "StatusEffect") { return 3; } if (fullName == "ZNetView" && ((MemberReference)method).Name == "InvokeRPC" && method.Parameters.Count == 2 && ((MemberReference)((ParameterReference)method.Parameters[0]).ParameterType).FullName == "System.String" && ((MemberReference)((ParameterReference)method.Parameters[1]).ParameterType).FullName == "System.Object[]") { return 4; } return 0; } private static void Adapt(ILContext il) { AdaptCore(il, ore: false); } private static void AdaptOre(ILContext il) { AdaptCore(il, ore: true); } private static void AdaptCore(ILContext il, bool ore) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (!ore) { return; } Enumerator<Instruction> enumerator = il.Body.Instructions.GetEnumerator(); try { while (enumerator.MoveNext()) { Instruction current = enumerator.Current; object operand = current.Operand; if (Classify((MethodReference)((operand is MethodReference) ? operand : null)) == 4) { current.OpCode = OpCodes.Call; current.Operand = il.Import((MethodBase)typeof(MachineTransferMetadata).GetMethod("InvokeRPC", new Type[3] { typeof(ZNetView), typeof(string), typeof(object[]) })); } } } finally { ((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } } } internal static class MachineTransferMetadata { [ThreadStatic] private static HashSet<string> _cheatedNames; internal static void Clear() { if (_cheatedNames == null) { _cheatedNames = new HashSet<string>(StringComparer.Ordinal); } else { _cheatedNames.Clear(); } } internal static bool RemovedCheated(string name) { if (name != null && _cheatedNames != null) { return _cheatedNames.Contains(name); } return false; } internal static void Record(string name, bool cheated) { if (AzuCallContext.InMachine && cheated && name != null) { _cheatedNames.Add(name); } } public static void InvokeRPC(ZNetView view, string method, object[] parameters) { if (AzuCallContext.InMachine && method == "RPC_AddOre" && parameters != null && parameters.Length == 2 && parameters[0] is string && parameters[1] is bool) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab((string)parameters[0]); string name = (((Object)(object)itemPrefab == (Object)null) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_name); parameters = WithRemovedMetadata(parameters, RemovedCheated(name)); } view.InvokeRPC(method, parameters); } internal static object[] WithRemovedMetadata(object[] parameters, bool removedCheated) { if (parameters == null || parameters.Length != 2 || !(parameters[0] is string) || !(parameters[1] is bool) || !removedCheated || (bool)parameters[1]) { return parameters; } return new object[2] { parameters[0], true }; } } [HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[] { typeof(string), typeof(int), typeof(int), typeof(bool) })] internal static class MachineItemRemovalMetadataPatch { [HarmonyPrefix] private static void Prefix(Inventory __instance, string name, int itemQuality, bool worldLevelBased) { if (Plugin.IsReady && AzuCallContext.InMachine) { MachineTransferMetadata.Record(name, __instance.ItemCheated(name, itemQuality, worldLevelBased)); } } } [HarmonyPatch(typeof(Inventory), "RemoveOneItem", new Type[] { typeof(ItemData) })] internal static class MachineSingleItemMetadataPatch { [HarmonyPrefix] private static void Prefix(ItemData item) { if (Plugin.IsReady && item != null) { MachineTransferMetadata.Record(item.m_shared?.m_name, item.m_cheated); } } } [HarmonyPatch(typeof(CookingStation), "FindCookableItem")] internal static class CookingMetadataPatch { [HarmonyPostfix] [HarmonyPriority(0)] [HarmonyAfter(new string[] { "Azumatt.AzuCraftyBoxes" })] private static void Postfix(ItemData __result) { if (Plugin.IsReady && __result != null && MachineTransferMetadata.RemovedCheated(__result.m_shared?.m_name)) { __result.m_cheated = true; } } } [HarmonyPatch] internal static class FermenterTransferCompatibilityPatch { private static readonly MethodInfo Status = typeof(Fermenter).GetMethod("GetStatus", BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null); private static readonly MethodInfo Allowed = typeof(Fermenter).GetMethod("IsItemAllowed", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[1] { typeof(ItemData) }, null); private static MethodBase TargetMethod() { return AccessTools.Method("AzuCraftyBoxes.Patches.SearchContainersAsWell:Postfix", (Type[])null, (Type[])null); } [HarmonyPrefix] private static bool Prefix(object[] __args) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown if (!Plugin.IsReady) { return true; } Fermenter val = (Fermenter)__args[0]; Inventory val2 = (Inventory)__args[1]; Player localPlayer = Player.m_localPlayer; if (__args[2] != null || (Object)(object)localPlayer == (Object)null || val2 != ((Humanoid)localPlayer).GetInventory() || AzuRuntimeBridge.PullingIsPrevented() || Convert.ToInt32(Status.Invoke(val, null)) != 0) { return false; } foreach (object nearbyContainer in AzuRuntimeBridge.GetNearbyContainers((Component)(object)val)) { Inventory inventory = AzuRuntimeBridge.GetInventory(nearbyContainer); if (inventory == null || inventory == val2) { continue; } foreach (ItemConversion item2 in val.m_conversion) { if ((Object)(object)item2.m_from == (Object)null) { continue; } string name = item2.m_from.m_itemData.m_shared.m_name; if (!AzuRuntimeBridge.CanPullItem(nearbyContainer, ((Object)item2.m_from).name) || AzuRuntimeBridge.GetAllowedRemoval(inventory.CountItems(name, -1, true)) <= 0) { continue; } ItemData item = inventory.GetItem(name, -1, false); if (item != null && !((Object)(object)item.m_dropPrefab == (Object)null) && (bool)Allowed.Invoke(val, new object[1] { item })) { ZNetView component = ((Component)val).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid() || !inventory.RemoveOneItem(item)) { return false; } component.InvokeRPC("RPC_AddItem", new object[2] { StringExtensionMethods.GetStableHashCode(((Object)item.m_dropPrefab).name), item.m_cheated }); __args[2] = null; return false; } } } return false; } } internal static class AzuServerSyncBridge { private static object _configSync; internal static void Initialize(ManualLogSource log) { try { Type type = AccessTools.TypeByName("AzuCraftyBoxes.AzuCraftyBoxesPlugin"); if (type == null) { throw new TypeLoadException("AzuCraftyBoxes.AzuCraftyBoxesPlugin was not found."); } Type type2 = type.Assembly.GetType("ServerSync.ConfigSync", throwOnError: false); if (type2 == null) { throw new TypeLoadException("AzuCraftyBoxes' bundled ServerSync.ConfigSync was not found."); } _configSync = Activator.CreateInstance(type2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new object[1] { "local.valheim.azucraftyboxesbuildingonly" }, null); if (_configSync == null) { throw new InvalidOperationException("ServerSync.ConfigSync could not be created."); } SetMember(type2, "DisplayName", "AzuCraftyBoxes Fixes & Config"); SetMember(type2, "CurrentVersion", "1.6.3"); SetMember(type2, "MinimumRequiredVersion", "1.6.3"); SetMember(type2, "ModRequired", true); log.LogInfo((object)"Registered required-mod version checking only; per-player gameplay settings are intentionally not synchronized."); } catch (Exception innerException) { throw new InvalidOperationException("Add-on version-check registration failed.", innerException); } } private static void SetMember(Type configSyncType, string name, object value) { FieldInfo field = configSyncType.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(_configSync, value); return; } PropertyInfo? property = configSyncType.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property == null) { throw new MissingMemberException(configSyncType.FullName, name); } property.SetValue(_configSync, value, null); } } internal static class AzuRuntimeBridge { private sealed class ContainerMethods { internal readonly MethodInfo ItemCount; internal readonly MethodInfo GetPrefabName; internal readonly MethodInfo ProcessContainerInventory; internal readonly MethodInfo GetInventory; internal bool IsComplete { get { if (ItemCount != null && GetPrefabName != null) { return ProcessContainerInventory != null; } return false; } } internal ContainerMethods(Type type) { ItemCount = type.GetMethod("ItemCount", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null); GetPrefabName = type.GetMethod("GetPrefabName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); GetInventory = type.GetMethod("GetInventory", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); ProcessContainerInventory = type.GetMethod("ProcessContainerInventory", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[3] { typeof(string), typeof(int), typeof(int) }, null); } } private static MethodInfo _shouldPrevent; private static MethodInfo _getNearbyContainers; private static MethodInfo _canItemBePulled; private static MethodInfo _checkAndDecrement; private static FieldInfo _rangeField; private static FieldInfo _skipField; private static PropertyInfo _pendingConsumptionsProperty; private static readonly Dictionary<Type, ContainerMethods> ContainerMethodCache = new Dictionary<Type, ContainerMethods>(); private static ManualLogSource _log; internal static void Initialize(ManualLogSource log) { _log = log; try { Type type = AccessTools.TypeByName("AzuCraftyBoxes.AzuCraftyBoxesPlugin"); Type type2 = AccessTools.TypeByName("AzuCraftyBoxes.Util.Functions.MiscFunctions"); Type type3 = AccessTools.TypeByName("AzuCraftyBoxes.Util.Functions.Boxes"); if (type == null || type2 == null || type3 == null) { throw new TypeLoadException("One or more AzuCraftyBoxes runtime types are missing."); } _shouldPrevent = type2.GetMethod("ShouldPrevent", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); _canItemBePulled = type3.GetMethod("CanItemBePulled", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[3] { typeof(string), typeof(string), typeof(string) }, null); _checkAndDecrement = type3.GetMethod("CheckAndDecrement", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(int) }, null); _rangeField = type.GetField("mRange", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _skipField = type.GetField("skip", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo[] methods = type3.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == "GetNearbyContainers" && methodInfo.IsGenericMethodDefinition && methodInfo.GetParameters().Length == 2) { _getNearbyContainers = methodInfo.MakeGenericMethod(typeof(Component)); break; } } _pendingConsumptionsProperty = AccessTools.TypeByName("AzuCraftyBoxes.Util.Functions.ConsumptionManager")?.GetProperty("PendingConsumptions", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (_shouldPrevent == null || _getNearbyContainers == null || _canItemBePulled == null || _checkAndDecrement == null || _rangeField == null || _skipField == null || _pendingConsumptionsProperty == null) { throw new MissingMemberException("A required member of the supported AzuCraftyBoxes runtime contract is missing."); } } catch (Exception innerException) { throw new InvalidOperationException("AzuCraftyBoxes runtime bridge initialization failed.", innerException); } } internal static bool PullingIsPrevented() { try { return (bool)_shouldPrevent.Invoke(null, null); } catch (Exception ex) { _log.LogError((object)("Could not read AzuCraftyBoxes pulling state: " + ex.Message)); return true; } } internal static List<object> GetNearbyContainers(Component player) { List<object> list = new List<object>(); try { object value = _rangeField.GetValue(null); float num = Convert.ToSingle((value?.GetType().GetProperty("Value"))?.GetValue(value, null)); if (!(_getNearbyContainers.Invoke(null, new object[2] { player, num }) is IEnumerable enumerable)) { return list; } foreach (object item in enumerable) { if (item != null) { list.Add(item); } } } catch (Exception ex) { _log.LogError((object)("Could not query nearby AzuCraftyBoxes containers: " + ex.Message)); } return list; } internal static int GetItemCount(object container, string sharedName) { try { ContainerMethods containerMethods = GetContainerMethods(container); return (containerMethods != null) ? Convert.ToInt32(containerMethods.ItemCount.Invoke(container, new object[1] { sharedName })) : 0; } catch { return 0; } } internal static Inventory GetInventory(object container) { object? obj = GetContainerMethods(container)?.GetInventory?.Invoke(container, null); return (Inventory)((obj is Inventory) ? obj : null); } internal static bool CanPullItem(object container, string itemPrefabName) { try { ContainerMethods containerMethods = GetContainerMethods(container); if (containerMethods == null) { return false; } string text = containerMethods.GetPrefabName.Invoke(container, null) as string; if (string.IsNullOrWhiteSpace(text)) { return false; } return (bool)_canItemBePulled.Invoke(null, new object[3] { text, itemPrefabName, string.Empty }); } catch { return false; } } internal static int GetAllowedRemoval(int itemCount) { try { return Convert.ToInt32(_checkAndDecrement.Invoke(null, new object[1] { itemCount })); } catch { return 0; } } internal static int RemoveFromContainer(object container, string sharedName, int amount) { if (amount <= 0) { return 0; } try { ContainerMethods containerMethods = GetContainerMethods(container); if (containerMethods == null) { return 0; } return Convert.ToInt32(containerMethods.ProcessContainerInventory.Invoke(container, new object[3] { sharedName, 0, amount })); } catch (Exception ex) { _log.LogError((object)("Could not remove a building material from a nearby container: " + ex.Message)); return 0; } } internal static void ResetTransientCraftingState() { try { _skipField?.SetValue(null, false); } catch { } } internal static void ClearPendingConsumptions() { try { object obj = _pendingConsumptionsProperty?.GetValue(null, null); if (obj == null) { return; } MethodInfo method = obj.GetType().GetMethod("TryTake"); if (!(method == null)) { object[] array = new object[1]; while ((bool)method.Invoke(obj, array)) { array[0] = null; } } } catch { } } private static ContainerMethods GetContainerMethods(object container) { if (container == null) { return null; } Type type = container.GetType(); if (!ContainerMethodCache.TryGetValue(type, out var value)) { value = new ContainerMethods(type); ContainerMethodCache[type] = value; } if (!value.IsComplete) { return null; } return value; } } internal static class AzuCallContext { [ThreadStatic] private static int _buildingDepth; [ThreadStatic] private static int _machineDepth; [ThreadStatic] private static int _craftingDepth; [ThreadStatic] private static int _infrastructureDepth; internal static bool InBuilding => _buildingDepth > 0; internal static bool InMachine => _machineDepth > 0; internal static bool InCrafting => _craftingDepth > 0; internal static bool InInfrastructure => _infrastructureDepth > 0; internal static void EnterBuilding() { _buildingDepth++; } internal static void ExitBuilding() { if (_buildingDepth > 0) { _buildingDepth--; } } internal static void EnterMachine() { if (_machineDepth == 0) { MachineTransferMetadata.Clear(); } _machineDepth++; } internal static void ExitMachine() { if (_machineDepth > 0) { _machineDepth--; } } internal static void EnterCrafting() { _craftingDepth++; } internal static void ExitCrafting() { if (_craftingDepth > 0) { _craftingDepth--; } } internal static void EnterInfrastructure() { _infrastructureDepth++; } internal static void ExitInfrastructure() { if (_infrastructureDepth > 0) { _infrastructureDepth--; } } } [HarmonyPatch] internal static class BuildingOperationContextPatch { private static IEnumerable<MethodBase> TargetMethods() { HashSet<MethodBase> hashSet = new HashSet<MethodBase>(); AddDeclaredMethods(hashSet, typeof(Player), "UpdatePlacement", "TryPlacePiece", "PlacePiece"); MethodBase methodBase = AccessTools.Method(typeof(Player), "HaveRequirements", new Type[2] { typeof(Piece), typeof(RequirementMode) }, (Type[])null); if (methodBase != null) { hashSet.Add(methodBase); } AddDeclaredMethods(hashSet, typeof(Hud), "SetupPieceInfo"); return hashSet; } private static void AddDeclaredMethods(ISet<MethodBase> methods, Type type, params string[] methodNames) { foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type)) { foreach (string text in methodNames) { if (declaredMethod.Name == text) { methods.Add(declaredMethod); break; } } } } [HarmonyPrefix] [HarmonyPriority(800)] private static void Prefix() { if (Plugin.IsReady) { AzuCallContext.EnterBuilding(); } } [HarmonyFinalizer] [HarmonyPriority(0)] private static Exception Finalizer(Exception __exception) { if (!Plugin.IsReady) { return __exception; } AzuCallContext.ExitBuilding(); return __exception; } } internal static class PlacementConsumptionMarker { [ThreadStatic] private static Requirement[] _requirements; [ThreadStatic] private static int _frame; internal static bool Active { get { if (_requirements != null) { return _frame == Time.frameCount; } return false; } } internal static void Mark(Piece piece) { _requirements = piece?.m_resources; _frame = Time.frameCount; } internal static bool Matches(Requirement[] requirements) { if (Active) { return _requirements == requirements; } return false; } internal static void ClearIfMatches(Requirement[] requirements) { if (Matches(requirements)) { _requirements = null; _frame = -1; } } } [HarmonyPatch(typeof(Player), "TryPlacePiece", new Type[] { typeof(Piece) })] internal static class PlacementConsumptionMarkerPatch { [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix(Player __instance, Piece piece, bool __result) { if (Plugin.IsReady && __result && (Object)(object)__instance != (Object)null && (Object)(object)__instance == (Object)(object)Player.m_localPlayer) { PlacementConsumptionMarker.Mark(piece); } } } [HarmonyPatch(typeof(Player), "ConsumeResources", new Type[] { typeof(Requirement[]), typeof(int), typeof(int), typeof(int) })] internal static class BuildingConsumptionSafetyPatch { private static bool _reportedFallback; [HarmonyPrefix] [HarmonyPriority(800)] private static void Prefix(Player __instance, Requirement[] requirements, int qualityLevel, out BuildingConsumptionSnapshot __state, int itemQuality = -1, int multiplier = 1) { if (!Plugin.IsReady) { __state = null; return; } __state = null; if ((AzuCallContext.InBuilding || PlacementConsumptionMarker.Matches(requirements)) && !((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && Plugin.ModEnabled.Value && Plugin.BuildingFromContainers.Value && !AzuRuntimeBridge.PullingIsPrevented()) { __state = BuildingConsumptionSnapshot.Capture(__instance, requirements, qualityLevel, itemQuality, multiplier); } } [HarmonyFinalizer] [HarmonyPriority(0)] private static Exception Finalizer(Exception __exception, BuildingConsumptionSnapshot __state, Requirement[] requirements) { try { if (__exception == null && __state != null && __state.RepairMissingConsumption() > 0 && !_reportedFallback) { _reportedFallback = true; Plugin.Log.LogWarning((object)"The building-consumption safety fallback was needed and removed the missing nearby-chest materials. This message is shown once."); } } catch (Exception ex) { Plugin.Log.LogError((object)("Building material consumption could not be verified safely: " + ex)); } finally { PlacementConsumptionMarker.ClearIfMatches(requirements); } return __exception; } } internal sealed class BuildingConsumptionSnapshot { private sealed class ResourceAudit { internal string SharedName; internal string PrefabName; internal int RequiredAmount; internal int CountBefore; } private readonly Player _player; private readonly Inventory _inventory; private readonly List<object> _containers; private readonly List<ResourceAudit> _resources; private readonly int _itemQuality; private BuildingConsumptionSnapshot(Player player, List<object> containers, List<ResourceAudit> resources, int itemQuality) { _player = player; _inventory = ((Humanoid)player).GetInventory(); _containers = containers; _resources = resources; _itemQuality = itemQuality; } internal static BuildingConsumptionSnapshot Capture(Player player, Requirement[] requirements, int qualityLevel, int itemQuality, int multiplier) { if ((Object)(object)player == (Object)null || requirements == null || multiplier <= 0) { return null; } List<object> nearbyContainers = AzuRuntimeBridge.GetNearbyContainers((Component)(object)player); Dictionary<string, ResourceAudit> dictionary = new Dictionary<string, ResourceAudit>(StringComparer.Ordinal); foreach (Requirement val in requirements) { if ((Object)(object)val?.m_resItem == (Object)null || val.m_resItem.m_itemData?.m_shared == null || !UpgraderRequirements.Matches(val, player)) { continue; } int num = val.GetAmount(qualityLevel) * multiplier; if (num > 0) { string name = val.m_resItem.m_itemData.m_shared.m_name; if (!dictionary.TryGetValue(name, out var value)) { value = new ResourceAudit { SharedName = name, PrefabName = GetPrefabName(((Object)val.m_resItem).name) }; dictionary.Add(name, value); } value.RequiredAmount += num; } } List<ResourceAudit> list = new List<ResourceAudit>(dictionary.Values); BuildingConsumptionSnapshot buildingConsumptionSnapshot = new BuildingConsumptionSnapshot(player, nearbyContainers, list, itemQuality); foreach (ResourceAudit item in list) { item.CountBefore = buildingConsumptionSnapshot.CountTotal(item.SharedName); } return buildingConsumptionSnapshot; } internal int RepairMissingConsumption() { if ((Object)(object)_player == (Object)null || (Object)(object)_player != (Object)(object)Player.m_localPlayer || _inventory == null) { return 0; } int num = 0; foreach (ResourceAudit resource in _resources) { int num2 = CountTotal(resource.SharedName); int num3 = Math.Max(0, resource.CountBefore - num2); int num4 = resource.RequiredAmount - num3; if (num4 > 0) { int num5 = RemoveMissing(resource, num4); num += num5; if (num5 < num4) { Plugin.Log.LogError((object)("A placed piece still has " + (num4 - num5) + " unconsumed " + resource.SharedName + " after the AzuCraftyBoxes safety fallback. Check the BepInEx log and nearby-container permissions.")); } } } return num; } private int CountTotal(string sharedName) { Inventory inventory = _inventory; int num = ((inventory != null) ? inventory.CountItems(sharedName, -1, true) : 0); foreach (object container in _containers) { num += Math.Max(0, AzuRuntimeBridge.GetItemCount(container, sharedName)); } return num; } private int RemoveMissing(ResourceAudit resource, int missing) { int num = 0; int num2 = _inventory.CountItems(resource.SharedName, -1, true); int num3 = Math.Min(missing, num2); if (num3 > 0) { _inventory.RemoveItem(resource.SharedName, num3, _itemQuality, true); num += Math.Max(0, num2 - _inventory.CountItems(resource.SharedName, -1, true)); } foreach (object container in _containers) { int num4 = missing - num; if (num4 <= 0) { break; } if (AzuRuntimeBridge.CanPullItem(container, resource.PrefabName)) { int allowedRemoval = AzuRuntimeBridge.GetAllowedRemoval(AzuRuntimeBridge.GetItemCount(container, resource.SharedName)); int num5 = Math.Min(num4, Math.Max(0, allowedRemoval)); num += Math.Min(num5, AzuRuntimeBridge.RemoveFromContainer(container, resource.SharedName, num5)); } } return num; } private static string GetPrefabName(string name) { if (string.IsNullOrEmpty(name)) { return string.Empty; } int num = name.IndexOfAny(new char[2] { '(', ' ' }); if (num >= 0) { return name.Substring(0, num); } return name; } } internal static class UpgraderRequirements { internal static bool Matches(Requirement requirement, Player player) { CraftingStation val = (((Object)(object)player == (Object)null) ? null : player.GetCurrentCraftingStation()); if (requirement != null) { return requirement.m_upgraderResource == ((Object)(object)val != (Object)null && val.m_upgrader); } return false; } internal static Requirement[] Filter(Requirement[] requirements, Player player) { if (requirements == null || (Object)(object)player == (Object)null) { return requirements; } if (requirements.Length == 0) { return requirements; } CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); bool flag = (Object)(object)currentCraftingStation != (Object)null && currentCraftingStation.m_upgrader; int num = 0; Requirement[] array = requirements; foreach (Requirement val in array) { if (val != null && val.m_upgraderResource == flag) { num++; } } if (num == requirements.Length) { return requirements; } if (num == 0) { return Array.Empty<Requirement>(); } Requirement[] array2 = (Requirement[])(object)new Requirement[num]; int num2 = 0; array = requirements; foreach (Requirement val2 in array) { if (val2 != null && val2.m_upgraderResource == flag) { array2[num2++] = val2; } } return array2; } } [HarmonyPatch(typeof(Player), "ConsumeResources", new Type[] { typeof(Requirement[]), typeof(int), typeof(int), typeof(int) })] internal static class UpgraderRequirementFilterPatch { [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyBefore(new string[] { "Azumatt.AzuCraftyBoxes" })] private static void Prefix(Player __instance, ref Requirement[] requirements) { if (Plugin.IsReady) { requirements = UpgraderRequirements.Filter(requirements, __instance); } } } [HarmonyPatch] internal static class UpgraderRecipeFilterPatch { private sealed class State { internal Recipe Recipe; internal Requirement[] Resources; } private static IEnumerable<MethodBase> TargetMethods() { foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(Player))) { if (declaredMethod.Name != "HaveRequirements" && declaredMethod.Name != "HaveRequirementItems" && declaredMethod.Name != "GetFirstRequiredItem") { continue; } ParameterInfo[] parameters = declaredMethod.GetParameters(); for (int i = 0; i < parameters.Length; i++) { if (parameters[i].ParameterType == typeof(Recipe)) { yield return declaredMethod; break; } } } } [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyBefore(new string[] { "Azumatt.AzuCraftyBoxes" })] private static void Prefix(Player __instance, object[] __args, out State __state) { if (!Plugin.IsReady) { __state = null; return; } __state = null; foreach (object obj in __args) { Recipe val = (Recipe)((obj is Recipe) ? obj : null); if (!((Object)(object)val == (Object)null)) { __state = new State { Recipe = val, Resources = val.m_resources }; val.m_resources = UpgraderRequirements.Filter(__state.Resources, __instance); break; } } } [HarmonyFinalizer] [HarmonyPriority(0)] private static Exception Finalizer(State __state, Exception __exception) { if (__state != null && (Object)(object)__state.Recipe != (Object)null) { __state.Recipe.m_resources = __state.Resources; } return __exception; } } [HarmonyPatch(typeof(Recipe), "GetAmount")] internal static class UpgraderRecipeAmountFilterPatch { [HarmonyPrefix] [HarmonyPriority(800)] private static void Prefix(Recipe __instance, out Requirement[] __state) { if (!Plugin.IsReady) { __state = null; return; } __state = __instance.m_resources; __instance.m_resources = UpgraderRequirements.Filter(__state, Player.m_localPlayer); } [HarmonyFinalizer] [HarmonyPriority(0)] private static Exception Finalizer(Recipe __instance, Requirement[] __state, Exception __exception) { if (__state != null) { __instance.m_resources = __state; } return __exception; } } [HarmonyPatch(typeof(Inventory), "ItemCheated", new Type[] { typeof(Requirement[]), typeof(int), typeof(bool) })] internal static class ContainerCheatedResourcesPatch { [HarmonyPostfix] private static void Postfix(Inventory __instance, Requirement[] resources, int quality, bool matchWorldLevel, ref bool __result) { if (!Plugin.IsReady) { return; } Player localPlayer = Player.m_localPlayer; if (__result || (Object)(object)localPlayer == (Object)null || __instance != ((Humanoid)localPlayer).GetInventory() || resources == null || AzuRuntimeBridge.PullingIsPrevented()) { return; } List<object> nearbyContainers = AzuRuntimeBridge.GetNearbyContainers((Component)(object)localPlayer); foreach (Requirement val in resources) { if (!UpgraderRequirements.Matches(val, localPlayer) || (Object)(object)val.m_resItem == (Object)null) { continue; } string name = val.m_resItem.m_itemData.m_shared.m_name; int num = (AzuCallContext.InCrafting ? CraftTransaction.QualityLevel : 0); int num2 = ((!AzuCallContext.InCrafting) ? 1 : CraftTransaction.Multiplier); int num3 = val.GetAmount(num) * num2 - __instance.CountItems(name, quality, matchWorldLevel); if (num3 <= 0) { continue; } string itemPrefabName = ((Object)val.m_resItem).name.Replace("(Clone)", "").Trim(); foreach (object item in nearbyContainers) { if (!AzuRuntimeBridge.CanPullItem(item, itemPrefabName)) { continue; } Inventory inventory = AzuRuntimeBridge.GetInventory(item); if (inventory == null || inventory == __instance) { continue; } int allowedRemoval = AzuRuntimeBridge.GetAllowedRemoval(inventory.CountItems(name, quality, matchWorldLevel)); if (allowedRemoval > 0) { if (inventory.ItemCheated(name, quality, matchWorldLevel)) { __result = true; return; } num3 -= allowedRemoval; if (num3 <= 0) { break; } } } } } } internal static class CraftTransaction { internal static bool Active { get; private set; } internal static bool AllowNearbyContainers { get; private set; } internal static int QualityLevel { get; private set; } = 1; internal static int Multiplier { get; private set; } = 1; internal static void ReadAmounts(InventoryGui gui) { object? obj = typeof(InventoryGui).GetField("m_craftUpgradeItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(gui); ItemData val = (ItemData)((obj is ItemData) ? obj : null); QualityLevel = ((val == null) ? 1 : (val.m_quality + 1)); Multiplier = ((!(bool)(typeof(InventoryGui).GetField("m_multiCrafting", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(gui) ?? ((object)false))) ? 1 : ((int)(typeof(InventoryGui).GetField("m_multiCraftAmount", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(gui) ?? ((object)1)))); } internal static void Begin(bool allowNearbyContainers) { Active = true; AllowNearbyContainers = allowNearbyContainers; } internal static void Clear() { Active = false; AllowNearbyContainers = false; } } [HarmonyPatch] internal static class CraftingOperationContextPatch { private static IEnumerable<MethodBase> TargetMethods() { HashSet<MethodBase> hashSet = new HashSet<MethodBase>(); foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(Player))) { if ((!(declaredMethod.Name != "HaveRequirements") || !(declaredMethod.Name != "HaveRequirementItems") || !(declaredMethod.Name != "GetFirstRequiredItem") || !(declaredMethod.Name != "UpdateKnownRecipesList")) && (declaredMethod.Name == "UpdateKnownRecipesList" || HasRecipeParameter(declaredMethod))) { hashSet.Add(declaredMethod); } } string[] array = new string[8] { "SetupCrafting", "UpdateCraftingPanel", "UpdateRecipeList", "AddRecipeToList", "OnSelectedRecipe", "UpdateRecipe", "SetupRequirementList", "SetupRequirement" }; foreach (MethodInfo declaredMethod2 in AccessTools.GetDeclaredMethods(typeof(InventoryGui))) { string[] array2 = array; foreach (string text in array2) { if (declaredMethod2.Name == text) { hashSet.Add(declaredMethod2); break; } } } return hashSet; } private static bool HasRecipeParameter(MethodInfo method) { ParameterInfo[] parameters = method.GetParameters(); for (int i = 0; i < parameters.Length; i++) { if (parameters[i].ParameterType == typeof(Recipe)) { return true; } } return false; } [HarmonyPrefix] [HarmonyPriority(800)] private static void Prefix() { if (Plugin.IsReady) { AzuCallContext.EnterCrafting(); } } [HarmonyFinalizer] [HarmonyPriority(0)] private static Exception Finalizer(Exception __exception) { if (!Plugin.IsReady) { return __exception; } AzuCallContext.ExitCrafting(); return __exception; } } [HarmonyPatch(typeof(InventoryGui), "OnCraftPressed")] internal static class CraftTransactionStartPatch { private struct State { internal float TimerBefore; internal bool AllowNearbyContainers; } private static readonly FieldInfo CraftTimerField = AccessTools.Field(typeof(InventoryGui), "m_craftTimer"); [HarmonyPrefix] [HarmonyPriority(800)] private static void Prefix(InventoryGui __instance, out State __state) { if (!Plugin.IsReady) { __state = default(State); return; } AzuCallContext.EnterCrafting(); AzuRuntimeBridge.ClearPendingConsumptions(); __state = new State { TimerBefore = ReadCraftTimer(__instance), AllowNearbyContainers = Plugin.CraftingFromContainers.Value }; } [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix(InventoryGui __instance, State __state) { if (Plugin.IsReady) { float num = ReadCraftTimer(__instance); if (num >= 0f && (__state.TimerBefore < 0f || !CraftTransaction.Active)) { CraftTransaction.Begin(__state.AllowNearbyContainers); } else if (num < 0f) { AzuRuntimeBridge.ClearPendingConsumptions(); CraftTransaction.Clear(); } } } [HarmonyFinalizer] [HarmonyPriority(0)] private static Exception Finalizer(Exception __exception) { if (!Plugin.IsReady) { return __exception; } AzuCallContext.ExitCrafting(); return __exception; } private static float ReadCraftTimer(InventoryGui inventoryGui) { try { return (CraftTimerField == null) ? (-1f) : Convert.ToSingle(CraftTimerField.GetValue(inventoryGui)); } catch { return -1f; } } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting", new Type[] { typeof(Player) })] internal static class CraftTransactionFinishPatch { [HarmonyPrefix] [HarmonyPriority(800)] private static void Prefix(InventoryGui __instance) { if (Plugin.IsReady) { AzuCallContext.EnterCrafting(); CraftTransaction.ReadAmounts(__instance); if (!CraftTransaction.Active) { CraftTransaction.Begin(Plugin.CraftingFromContainers.Value); } } } [HarmonyFinalizer] [HarmonyPriority(0)] private static Exception Finalizer(Exception __exception) { if (!Plugin.IsReady) { return __exception; } AzuRuntimeBridge.ResetTransientCraftingState(); AzuRuntimeBridge.ClearPendingConsumptions(); CraftTransaction.Clear(); AzuCallContext.ExitCrafting(); return __exception; } } [HarmonyPatch] internal static class CraftTransactionCancelPatch { private static IEnumerable<MethodBase> TargetMethods() { MethodBase methodBase = AccessTools.Method(typeof(InventoryGui), "OnCraftCancelPressed", (Type[])null, (Type[])null); MethodBase hide = AccessTools.Method(typeof(InventoryGui), "Hide", (Type[])null, (Type[])null); if (methodBase != null) { yield return methodBase; } if (hide != null) { yield return hide; } } [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix() { if (Plugin.IsReady) { AzuRuntimeBridge.ResetTransientCraftingState(); AzuRuntimeBridge.ClearPendingConsumptions(); CraftTransaction.Clear(); } } } [HarmonyPatch(typeof(Player), "UpdateKnownRecipesList")] internal static class AzuCraftingStateCleanupPatch { [HarmonyFinalizer] [HarmonyPriority(0)] private static Exception Finalizer(Exception __exception) { if (!Plugin.IsReady) { return __exception; } AzuRuntimeBridge.ResetTransientCraftingState(); return __exception; } } [HarmonyPatch] internal static class MachineOperationContextPatch { private static IEnumerable<MethodBase> TargetMethods() { HashSet<MethodBase> hashSet = new HashSet<MethodBase>(); AddDeclaredMethods(hashSet, typeof(CookingStation), "OnAddFuelSwitch", "FindCookableItem"); AddDeclaredMethods(hashSet, typeof(Fermenter), "GetHoverText", "FindCookableItem"); AddDeclaredMethods(hashSet, typeof(Fireplace), "Interact", "GetHoverText"); AddDeclaredMethods(hashSet, typeof(Smelter), "OnHoverAddOre", "OnHoverAddFuel", "OnAddOre", "OnAddFuel"); AddDeclaredMethods(hashSet, typeof(ShieldGenerator), "OnHoverAddFuel", "OnAddFuel"); AddDeclaredMethods(hashSet, typeof(Turret), "UseItem", "GetHoverText"); return hashSet; } private static void AddDeclaredMethods(ISet<MethodBase> methods, Type type, params string[] methodNames) { foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type)) { foreach (string text in methodNames) { if (declaredMethod.Name == text) { methods.Add(declaredMethod); break; } } } } [HarmonyPrefix] [HarmonyPriority(800)] private static void Prefix() { if (Plugin.IsReady) { AzuCallContext.EnterMachine(); } } [HarmonyFinalizer] [HarmonyPriority(0)] private static Exception Finalizer(Exception __exception) { if (!Plugin.IsReady) { return __exception; } AzuCallContext.ExitMachine(); return __exception; } } [HarmonyPatch] internal static class AzuInfrastructureContextPatch { private static IEnumerable<MethodBase> TargetMethods() { MethodBase[] array = new MethodBase[9] { AccessTools.Method(typeof(Container), "Awake", (Type[])null, (Type[])null), AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null), AccessTools.Method(typeof(Container), "OnDestroyed", (Type[])null, (Type[])null), AccessTools.Method(typeof(WearNTear), "OnDestroy", (Type[])null, (Type[])null), AccessTools.Method(typeof(Player), "UpdateTeleport", (Type[])null, (Type[])null), AccessTools.Method(typeof(ObjectDB), "Awake", (Type[])null, (Type[])null), AccessTools.Method(typeof(Player), "SetLocalPlayer", (Type[])null, (Type[])null), AccessTools.Method(typeof(Player), "OnSpawned", (Type[])null, (Type[])null), AccessTools.Method(typeof(Player), "OnRespawn", (Type[])null, (Type[])null) }; MethodBase[] array2 = array; foreach (MethodBase methodBase in array2) { if (methodBase != null) { yield return methodBase; } } } [HarmonyPrefix] [HarmonyPriority(800)] private static void Prefix() { if (Plugin.IsReady) { AzuCallContext.EnterInfrastructure(); } } [HarmonyFinalizer] [HarmonyPriority(0)] private static Exception Finalizer(Exception __exception) { if (!Plugin.IsReady) { return __exception; } AzuCallContext.ExitInfrastructure(); return __exception; } } internal enum BaseModProfile { LegacyFork1818, Official1819 } internal static class BaseModCompatibility { internal static BaseModProfile Resolve(AssemblyName assembly) { if (assembly != null && assembly.Name == "AzuCraftyBoxes") { if (assembly.Version == new Version(1, 8, 19, 0)) { return BaseModProfile.Official1819; } if (assembly.Version == new Version(1, 8, 18, 0)) { return BaseModProfile.LegacyFork1818; } } throw new NotSupportedException("No verified AzuCraftyBoxes compatibility profile exists for " + ((assembly == null) ? "an unknown assembly" : assembly.FullName) + ". This release supports official Azumatt AzuCraftyBoxes 1.8.19 and the legacy fedorovdgap 1.8.18 rebuild."); } internal static bool RequiresFermenterReplacement(AssemblyName assembly) { return Resolve(assembly) == BaseModProfile.LegacyFork1818; } } internal sealed class StartupSequence { private readonly Action<string> _error; internal bool Ready { get; private set; } internal string Stage { get; private set; } = "dependency health check"; internal StartupSequence(Action<string> error) { _error = error; } internal void Step(string stage, Action action) { Stage = stage; action(); } internal void Stop() { Ready = false; } internal bool TryOptionalCheck(string stage, Action check, Action<string> warning) { string stage2 = Stage; try { Step(stage, check); return true; } catch (Exception ex) { warning("Optional check failed during " + Stage + ". Required CraftyBoxes initialization will continue. Build Camera compatibility is not validated; this add-on has neither repaired nor disabled that camera. Keep a working compatible camera copy, or disable the incompatible camera in your mod manager. Original optional-check exception:\n" + ex); return false; } finally { Stage = stage2; } } internal bool Run(Action checkDependency, Action initialize, Func<bool> cleanup) { Ready = false; try { Step("dependency health check", checkDependency); initialize(); Ready = true; Stage = "ready"; return true; } catch (Exception ex) { _error("Add-on startup stopped during " + Stage + ". ORIGINAL EXCEPTION:\n" + ex); bool flag = false; try { flag = cleanup(); } catch (Exception ex2) { _error("Separate cleanup failure; the original exception is above:\n" + ex2); } if (!flag) { _error("Cleanup could not be verified. The add-on is inactive; partial hooks may remain. Restart before playing."); } return false; } } } internal static class BaseModHealth { private const BindingFlags Fields = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private const string Explanation = "AzuCraftyBoxes did not establish a complete, supported startup. This add-on will not register ServerSync or install integration patches. Check the earlier AzuCraftyBoxes/ServerSync error in the client or server log. Use official Azumatt-AzuCraftyBoxes-1.8.19 (recommended), or the supported legacy fedorovdgap-AzuCraftyBoxes-1.8.18, never both. This add-on does not repair a failed base mod or treat a version registration as startup success."; internal static Assembly RequireInitialized() { try { if (!Chainloader.PluginInfos.TryGetValue("Azumatt.AzuCraftyBoxes", out var value) || value == null || value.Instance == null) { throw new InvalidOperationException("The dependency has no loaded plug-in instance."); } Type type = ((object)value.Instance).GetType(); if (type.FullName != "AzuCraftyBoxes.AzuCraftyBoxesPlugin") { throw new NotSupportedException("No startup-completion contract has been verified for " + type.Assembly.GetName()?.ToString() + "."); } BaseModCompatibility.Resolve(type.Assembly.GetName()); RequireWatcher(type, value.Instance, "_watcher"); RequireWatcher(type, value.Instance, "_yamlwatcher"); object? value2 = RequiredField(type.Assembly.GetType("AzuCraftyBoxes.Patches.SE_ContainerPull", throwOnError: true), "SE_ContainerPulling").GetValue(null); StatusEffect val = (StatusEffect)((value2 is StatusEffect) ? value2 : null); if (val == null || val.m_name != "Preventing Pulling" || val.m_tooltip != "Prevents pulling from nearby containers & backpacks") { throw new InvalidOperationException("The final Awake status-effect initialization was not completed."); } RequireInstalledCallbacks(type.Assembly, "Azumatt.AzuCraftyBoxes", new string[8] { "ContainerAwakePatch", "ContainerLoadPatch", "PlayerHaveRequirementsPatch", "ConsumeResourcesPatch", "ConsumeLaterConsumptionItemsInventoryGuiDoCraftingPatch", "SmelterOnAddOrePatch", "SmelterOnAddFuelPatch", "SearchContainersAsWell" }); return type.Assembly; } catch (Exception innerException) { throw new InvalidOperationException("AzuCraftyBoxes did not establish a complete, supported startup. This add-on will not register ServerSync or install integration patches. Check the earlier AzuCraftyBoxes/ServerSync error in the client or server log. Use official Azumatt-AzuCraftyBoxes-1.8.19 (recommended), or the supported legacy fedorovdgap-AzuCraftyBoxes-1.8.18, never both. This add-on does not repair a failed base mod or treat a version registration as startup success.", innerException); } } private static FieldInfo RequiredField(Type type, string name) { return type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingFieldException(type.FullName, name); } private static void RequireWatcher(Type type, object instance, string name) { if (!(RequiredField(type, name).GetValue(instance) is FileSystemWatcher { EnableRaisingEvents: not false })) { throw new InvalidOperationException("Base startup marker " + name + " is absent or not running."); } } internal static void RequireInstalledCallbacks(Assembly assembly, string owner, IEnumerable<string> required) { HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); foreach (MethodBase allPatchedMethod in Harmony.GetAllPatchedMethods()) { Patches patchInfo = Harmony.GetPatchInfo(allPatchedMethod); if (patchInfo == null) { continue; } List<Patch> list = new List<Patch>(); list.AddRange(patchInfo.Prefixes); list.AddRange(patchInfo.Postfixes); list.AddRange(patchInfo.Transpilers); list.AddRange(patchInfo.Finalizers); foreach (Patch item in list) { if (item.owner == owner && item.PatchMethod.DeclaringType?.Assembly == assembly) { hashSet.Add(item.PatchMethod.DeclaringType.Name); } } } foreach (string item2 in required) { if (!hashSet.Contains(item2)) { throw new InvalidOperationException("Required base callback " + item2 + " is not installed."); } } } } internal sealed class PatchSession { private readonly Harmony _harmony; private readonly StartupSequence _startup; private readonly Action<string> _error; private readonly List<MethodBase> _attempted = new List<MethodBase>(); private bool _uncertainDetour; internal int InstalledCount { get; private set; } internal PatchSession(Harmony harmony, StartupSequence startup, Action<string> error) { _harmony = harmony; _startup = startup; _error = error; } internal void Apply(MethodBase target, HarmonyMethod prefix = null, HarmonyMethod postfix = null, HarmonyMethod transpiler = null, HarmonyMethod finalizer = null, HarmonyMethod ilmanipulator = null) { if (target == null) { throw new MissingMethodException("A required patch target was not found."); } _startup.Step("reading patch target " + Describe(target), delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown DynamicMethodDefinition val = new DynamicMethodDefinition(target); try { } finally { ((IDisposable)val)?.Dispose(); } }); _startup.Step("installing patch on " + Describe(target), delegate { if (!_attempted.Contains(target)) { _attempted.Add(target); } try { if (_harmony.Patch(target, prefix, postfix, transpiler, finalizer, ilmanipulator) == null || !HasOwner(target)) { throw new InvalidOperationException("Harmony did not confirm the installed patch."); } int installedCount = InstalledCount; InstalledCount = installedCount + 1; } catch { _uncertainDetour = true; throw; } }); } internal void ApplyClass(Type patchType) { _startup.Step("resolving patch class " + patchType.FullName, delegate { ApplyResolvedClass(patchType); }); } private void ApplyResolvedClass(Type type) { List<MethodBase> list = ResolveTargets(type); HarmonyMethod prefix = Callback<HarmonyPrefix>(type); HarmonyMethod postfix = Callback<HarmonyPostfix>(type); HarmonyMethod transpiler = Callback<HarmonyTranspiler>(type); HarmonyMethod finalizer = Callback<HarmonyFinalizer>(type); foreach (MethodBase item in list) { Apply(item, prefix, postfix, transpiler, finalizer); } } internal static List<MethodBase> ResolveTargets(Type type) { //IL_007f: Unknown result type (might be due to invalid IL or missing references) MethodInfo method = type.GetMethod("TargetMethods", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo method2 = type.GetMethod("TargetMethod", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); List<MethodBase> list = new List<MethodBase>(); if (method != null) { list.AddRange((IEnumerable<MethodBase>)method.Invoke(null, null)); } else if (method2 != null) { list.Add((MethodBase)method2.Invoke(null, null)); } else { object[] customAttributes = type.GetCustomAttributes(typeof(HarmonyPatch), inherit: true); for (int i = 0; i < customAttributes.Length; i++) { HarmonyMethod info = ((HarmonyAttribute)(HarmonyPatch)customAttributes[i]).info; if (info.declaringType != null && info.methodName != null) { list.Add((info.argumentTypes == null) ? info.declaringType.GetMethod(info.methodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) : info.declaringType.GetMethod(info.methodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, info.argumentTypes, null)); } } } if (list.Count == 0) { throw new MissingMethodException("No targets resolved for " + type.FullName); } if (list.Contains(null)) { throw new MissingMethodException("A required target did not resolve for " + type.FullName); } return list; } private static HarmonyMethod Callback<T>(Type type) where T : Attribute { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.IsDefined(typeof(T), inherit: true)) { return new HarmonyMethod(methodInfo); } } return null; } private bool HasOwner(MethodBase target) { Patches patchInfo = Harmony.GetPatchInfo(target); if (patchInfo == null) { return false; } return patchInfo.Owners.Contains(_harmony.Id); } internal static string Describe(MethodBase target) { return target.DeclaringType?.FullName + "." + target; } internal bool Cleanup() { bool flag = true; for (int num = _attempted.Count - 1; num >= 0; num--) { MethodBase methodBase = _attempted[num]; try { _harmony.Unpatch(methodBase, (HarmonyPatchType)0, _harmony.Id); if (HasOwner(methodBase)) { throw new InvalidOperationException("Our Harmony registration remains after unpatching."); } } catch (Exception ex) { flag = false; _error("Separate cleanup failure for " + Describe(methodBase) + "; original startup error is above:\n" + ex); } } if (_uncertainDetour) { flag = false; _error("A detour installation failed. Removing its Harmony registration alone cannot prove native rollback; a restart is required."); } if (flag) { _attempted.Clear(); } return flag; } } }