Decompiled source of EasyCraft v1.0.6

plugins/EasyCraft/EasyCraft.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("EasyCraft")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.6.0")]
[assembly: AssemblyInformationalVersion("1.0.6")]
[assembly: AssemblyProduct("EasyCraft")]
[assembly: AssemblyTitle("EasyCraft")]
[assembly: AssemblyVersion("1.0.6.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 EasyCraft
{
	public interface IFillSource
	{
		int Provide(Component target, Humanoid user, string[] sharedNames, int maxCount, out string sharedName, out string prefabName, out bool cheated);
	}
	[BepInPlugin("easycraft.client.only", "EasyCraft", "1.0.6")]
	public class EasyCraftPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "easycraft.client.only";

		public const string PluginName = "EasyCraft";

		public const string PluginVersion = "1.0.6";

		private const string LegacySourcesDefault = "piece_dvergr_lantern,piece_dvergr_lantern_pole,piece_groundtorch,piece_groundtorchwood,piece_groundtorchgreen,piece_groundtorchblue,piece_firepit,piece_brazierfloor01,piece_brazierceiling01,piece_hearth";

		private const string LegacyExcludeDefault = "piece_firepit";

		internal const string DefaultAllowedContainers = "piece_chestwood,piece_chest,piece_chestblackmetal,piece_chestgrausten,piece_chestbarrel,piece_chestwarderobe";

		private const string LegacyAllowedDefault = "piece_chest_wood,piece_chest";

		internal static EasyCraftPlugin Instance;

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> CfgEnabled;

		internal static ConfigEntry<int> CfgBatchSize;

		internal static ConfigEntry<string> CfgStationOreAllow;

		internal static ConfigEntry<bool> CfgFireEnabled;

		internal static ConfigEntry<string> CfgFireExclude;

		internal static ConfigEntry<string> CfgFireSources;

		internal static ConfigEntry<string> CfgFireOneAtATime;

		internal static ConfigEntry<float> CfgFireHoldInterval;

		internal static ConfigEntry<bool> CfgFireRespectToggle;

		internal static ConfigEntry<bool> CfgNearbyEnabled;

		internal static ConfigEntry<float> CfgRadius;

		internal static ConfigEntry<string> CfgAllowed;

		internal static ConfigEntry<bool> CfgIncludeCarts;

		internal static ConfigEntry<bool> CfgRespectWards;

		internal static ConfigEntry<bool> CfgClaimOwnership;

		internal static ConfigEntry<bool> CfgSkipInUse;

		internal static ConfigEntry<bool> CfgSkipIfPlayerNear;

		internal static ConfigEntry<float> CfgPlayerNearDistance;

		internal static ConfigEntry<bool> CfgDebug;

		internal static HashSet<string> AllowedSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static readonly List<string> AllowedRaw = new List<string>();

		internal static IFillSource ExtraSource;

		private Harmony _harmony;

		private NearbyFillSource _source;

		internal static bool IsActive
		{
			get
			{
				if (CfgEnabled != null)
				{
					return CfgEnabled.Value;
				}
				return false;
			}
		}

		internal static bool DebugOn
		{
			get
			{
				if (CfgDebug != null)
				{
					return CfgDebug.Value;
				}
				return false;
			}
		}

		private void Awake()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Expected O, but got Unknown
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Expected O, but got Unknown
			//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c9: Expected O, but got Unknown
			//IL_0358: Unknown result type (might be due to invalid IL or missing references)
			//IL_0362: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			CfgEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Batch Fill", "Enabled", true, "开启后,每次与熔炉 / 高炉 / 炭窑 / 风车 / 纺车 / Eitr 精炼厂交互,会一次性装入 BatchSize 个材料,取代原版的一次 1 个。关闭则完全回到原版行为。");
			CfgBatchSize = ((BaseUnityPlugin)this).Config.Bind<int>("1 - Batch Fill", "BatchSize", 5, new ConfigDescription("一次交互装入的材料数量。实际装入量会被「剩余容量」限制,绝不会超出上限(站点看 m_maxOre / m_maxFuel,火源看 m_maxFuel)。", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			CfgStationOreAllow = ((BaseUnityPlugin)this).Config.Bind<string>("1 - Batch Fill", "StationOreAllow", "charcoal_kiln=$item_wood", "**站点进料限制**:限定某个炼制站点只允许自动装填指定材料。\n默认值 = 炭窑只烧木材(不放细木 / 圆木):\n  charcoal_kiln=$item_wood\n\n格式:分号分组,等号左边站点、右边材料(逗号分隔):\n  charcoal_kiln=$item_wood ; smelter=$item_copperore,$item_tinore\n\n材料名三种写法都认(忽略大小写):$item_wood / item_wood / Wood\n站点名认 GameObject 名,也认带 piece_ 前缀的形式:\n  炭窑 = charcoal_kiln(本地化键是 piece_charcoalkiln)\n  冶炼炉 = smelter\u3000\u3000高炉 = blastfurnace\n  风车 = windmill\u3000\u3000纺织机 = piece_spinningwheel\u3000\u3000埃达提炼器 = piece_eitrrefinery\n\n行为:\n  · 只影响**本模组的自动装填**(批量 + 就近取料),你手动往站点里放什么都不受限制\n  · 留空 = 不限制任何站点(完全回到原版)\n  · 某个站点写成 `站点=` (右边留空)= 该站点不限制\n  · 若填的材料名在该站点上一个都对不上,会打一条 Warning 列出实际可用材料,\n    并且**这一次不做限制**(避免把站点搞成什么都装不进去)\n  · 生效与否看日志里的「站点进料限制生效:…」那一行");
			CfgFireEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Fire Sources", "Enabled", true, "开启后,给火源加燃料也变成一次 BatchSize 个(原版是一次 1 个)。\n原版按 E 就是加燃料;只有 m_canTurnOff=true 的物件才会把「点按」当成开/关火。");
			CfgFireExclude = ((BaseUnityPlugin)this).Config.Bind<string>("2 - Fire Sources", "Exclude", "fire_pit,piece_firepit", "**默认排除**的火源(填 GameObject 名,逗号分隔,忽略大小写)。\n默认值 fire_pit = Campfire = 官方中文「营火」,也就是能做饭的那个基础火 —— 按要求让它保持原版一次填 1 个。\n⚠\ufe0f 这里填的是 **GameObject 名**,它不一定等于本地化键:\n     营火 = fire_pit(不是 piece_firepit)\n     篝火 = bonfire(不是 piece_bonfire)\n     灶   = hearth(不是 piece_hearth)\n   不确定某个火源叫什么,看日志里的 [火源探测] 那行 —— 它会把「组件在=…」和「ZDO名=…」都打出来。\n想让它也批量,把这行清空即可。");
			CfgFireSources = ((BaseUnityPlugin)this).Config.Bind<string>("2 - Fire Sources", "Sources", "", "可选白名单。**留空 = 所有火源都生效**(只受上面的 Exclude 限制),这是默认行为。\n只有你想「只让某几个火源生效」时才填,例如:\n  piece_groundtorch,piece_dvergr_lantern\n填了之后,不在名单里的火源一律交回原版。");
			CfgFireOneAtATime = ((BaseUnityPlugin)this).Config.Bind<string>("2 - Fire Sources", "OneAtATime", "fire_pit,piece_firepit", "「一次只装 1 个、但允许从附近箱子取料」的火源(GameObject 名,逗号分隔,忽略大小写)。\n默认 fire_pit = 营火:做饭的基础火一次烧 5 个木头太快,所以数量保持原版 1 个,\n但仍然会从附近箱子自动取木头(省得自己翻箱子)。\n⚠\ufe0f 名单里的火源应当同时出现在 Exclude 里(Exclude 负责\"不批量\",本名单负责\"保留取料\"):\n   Exclude 命中且不在本名单 → 完全交回原版(连取料都没有)\n   Exclude 命中且在本名单   → 数量固定 1 + 从箱子取料\n   未命中 Exclude           → 正常批量 BatchSize 个\n想让某个火源彻底回到原版(连取料都不要),把它从本名单移除即可。");
			CfgFireHoldInterval = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Fire Sources", "HoldInterval", 0f, new ConfigDescription("长按 E 时的最小间隔(秒)。**默认 0 = 不兜底**:原版 m_holdRepeatInterval 为 0 时长按本来就不加料,本模组照原版处理(什么都不做)。\n若你确实想让长按也批量,填一个正数(如 0.35)。\n注:m_holdRepeatInterval > 0 的火源,长按一律用它自己的间隔,与本项无关。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
			CfgFireRespectToggle = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Fire Sources", "RespectToggle", true, "是否保留原版的「开/关火」:当火源 m_canTurnOff=true 且燃料>0 时,点按 E 是熄火/点火,本模组不接管。\n默认 true(不改变原版行为)。若你发现某个火源点按只会被熄火、不会批量装填,把这项设成 false 即可强制批量。\n(日志里的 [火源探测] 会打印每个火源的 m_canTurnOff 实际值。)");
			CfgNearbyEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Nearby Chests", "Enabled", true, "开启后,玩家背包材料不足时,会自动从附近箱子补足。\n(原 EasyNearbyFill 的开关;关掉后只从背包取料。)");
			CfgRadius = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Nearby Chests", "Radius", 30f, new ConfigDescription("搜索半径(米)。以【填料机器】和【玩家】各自为圆心,落在任一个半径内的箱子都会被算作可用料源。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>()));
			CfgAllowed = ((BaseUnityPlugin)this).Config.Bind<string>("3 - Nearby Chests", "AllowedContainers", "piece_chestwood,piece_chest,piece_chestblackmetal,piece_chestgrausten,piece_chestbarrel,piece_chestwarderobe", "允许取料的箱子类型(逗号分隔)。\n\n✅ **匹配已归一化:忽略大小写 + 去 piece_ 前缀 + 去所有下划线。**\n   所以下面这些写法完全等价,你随便挑一种写:\n     piece_chest_blackmetal == piece_chestblackmetal == CHESTBLACKMETAL\n   ⚠\ufe0f 游戏里 GameObject 名与本地化键**不是同一个字符串**(前者带下划线、后者不带),\n      而且游戏自身命名也不统一 —— 所以靠归一化比精确比对可靠得多,不用纠结写法。\n\n各箱子的归一化键(= 实际参与匹配的键):\n  chestwood        宝箱(木箱)\n  chest            加固型宝箱(大箱子)\n  chestblackmetal  黑金宝箱\n  chestgrausten    灰石箱\n  chestbarrel      桶\n  chestwarderobe   衣柜\n有意不在默认列表里(需要才自己加):\n  chestprivate     个人宝箱(私人箱)\n  chesttreasure    珍宝箱\n\n· 留空 = 不取任何普通箱子(若同时开着 IncludeCarts,则只剩板车可取)\n· 生效与否看启动自检那两行:「允许的箱子 [...]」和「生效匹配键=[...]」");
			CfgIncludeCarts = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Nearby Chests", "IncludeCarts", true, "**把板车也算作取料来源**(默认开)。\n板车(Cart)与雪橇(Sled)都用 `Vagon` 组件,内部带一个容器,\n本项开启后它们会和箱子一样被自动取料(静止停放时)。\n\n几个说明:\n  · 判据是「这个容器挂在板车上」(`Container.m_wagon`),**不是按名字匹配**,\n    所以不受改名/层级变化影响,也**不需要**把 Cart 加进 AllowedContainers。\n  · **正在被拖行的板车会被跳过** —— 位置一直在变,而且此时所有权在拖车人手里,\n    去接管所有权有可能把车从人家手上抢掉。停下不动了才会被取料。\n  · 有人正开着板车容器时也不会取(与箱子的处理一致,受 4 - Safety 控制)。\n  · 取板车的东西同样会走「守卫石权限检查」与「接管所有权」流程。");
			CfgRespectWards = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Nearby Chests", "RespectWards", true, "额外做一次守卫石(私人区域)权限检查:玩家没有权限的箱子不会被取料。");
			CfgClaimOwnership = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Nearby Chests", "ClaimOwnership", true, "取料前先接管该箱子的 ZDO 所有权(与游戏原生'打开箱子'完全一样的机制)。这是必须的:不接管所有权的话,箱子的物品变化不会被保存,会出现'东西进了炉子但箱子里还在'的复制问题。");
			CfgSkipInUse = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Safety", "SkipInUse", true, "本地标记显示箱子\"正在被使用\"(Container.IsInUse())时跳过它。\n注意:这个标记是本地字段、不写进 ZDO,所以它只能反映【本机主人】和【你自己】的状态,看不到远处客户端是否正开着这个箱子 —— 那种情况要靠下面的 SkipIfPlayerNear。");
			CfgSkipIfPlayerNear = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Safety", "SkipIfPlayerNear", true, "有其他玩家站在箱子附近时跳过它(认为他正在用这个箱子)。\n这是在联机里避免\"抢走别人正开着箱子的所有权\"的唯一可行手段,因为游戏没有把箱子占用状态同步给其他客户端。");
			CfgPlayerNearDistance = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Safety", "PlayerNearDistance", 4f, new ConfigDescription("\"别人正在用这个箱子\"的判定距离(米)。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 20f), Array.Empty<object>()));
			CfgDebug = ((BaseUnityPlugin)this).Config.Bind<bool>("5 - Debug", "DebugLogging", false, "输出每次装填 / 取料的详细过程(选中的材料、背包数量、从哪些箱子取了多少、最终装填数)。\n注意:火源的「一次性探测日志」、「跳过原因」和「首次装填明细」**不受这个开关控制**,默认就会打印。");
			FireSources.Reload(NormalizeExclude(CfgFireExclude.Value), NormalizeSources(CfgFireSources.Value), CfgFireOneAtATime.Value);
			FireSources.SelfReport();
			StationOreFilter.Reload(CfgStationOreAllow.Value);
			MigrateAllowedContainers();
			ReloadAllowed();
			_source = new NearbyFillSource();
			ExtraSource = _source;
			_harmony = new Harmony("easycraft.client.only");
			PatchAll();
			SelfCheck();
		}

		private string NormalizeSources(string csv)
		{
			if (string.IsNullOrEmpty(csv))
			{
				return csv;
			}
			if (string.Equals(csv.Trim(), "piece_dvergr_lantern,piece_dvergr_lantern_pole,piece_groundtorch,piece_groundtorchwood,piece_groundtorchgreen,piece_groundtorchblue,piece_firepit,piece_brazierfloor01,piece_brazierceiling01,piece_hearth", StringComparison.OrdinalIgnoreCase))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"FireSources.Sources 仍是 1.1.0 的默认白名单(10 项),已按新语义当作「留空 = 全部火源生效」处理。想恢复白名单语义请自行改写该行。");
				return "";
			}
			return csv;
		}

		private string NormalizeExclude(string csv)
		{
			if (string.IsNullOrEmpty(csv))
			{
				return csv;
			}
			if (string.Equals(csv.Trim(), "piece_firepit", StringComparison.OrdinalIgnoreCase))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"FireSources.Exclude 仍是 1.1.2 的旧默认值(piece_firepit),但营火的真实 GameObject 名是 fire_pit —— 旧值匹配不到任何火源。已按新默认值处理:fire_pit,piece_firepit");
				return "fire_pit,piece_firepit";
			}
			return csv;
		}

		private void MigrateAllowedContainers()
		{
			string value = CfgAllowed.Value;
			if (string.IsNullOrEmpty(value) || !string.Equals(value.Trim(), "piece_chest_wood,piece_chest", StringComparison.OrdinalIgnoreCase))
			{
				return;
			}
			((BaseUnityPlugin)this).Logger.LogWarning((object)"AllowedContainers 仍是 1.0.3 的旧默认值(piece_chest_wood,piece_chest),但带下划线的名字在游戏里并不存在(真实名是 piece_chestwood 等)——旧值导致木箱等箱子一直取不了料。已自动修正为:piece_chestwood,piece_chest,piece_chestblackmetal,piece_chestgrausten,piece_chestbarrel,piece_chestwarderobe\n   (若你此前手动改过这一行且恰好等于旧默认值,请重新按新名单调整。)");
			try
			{
				CfgAllowed.Value = "piece_chestwood,piece_chest,piece_chestblackmetal,piece_chestgrausten,piece_chestbarrel,piece_chestwarderobe";
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("写入新默认值失败(本次仅内存生效):" + ex.Message));
			}
		}

		internal static string NormalizeContainerName(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return string.Empty;
			}
			string text = name.Trim().ToLowerInvariant();
			if (text.StartsWith("$"))
			{
				text = text.Substring(1);
			}
			if (text.StartsWith("piece_"))
			{
				text = text.Substring("piece_".Length);
			}
			if (text.IndexOf('_') >= 0)
			{
				text = text.Replace("_", string.Empty);
			}
			return text;
		}

		internal static void ReloadAllowed()
		{
			AllowedSet.Clear();
			AllowedRaw.Clear();
			string text = ((CfgAllowed != null) ? CfgAllowed.Value : null);
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			string[] array = text.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length == 0)
				{
					continue;
				}
				string text3 = NormalizeContainerName(text2);
				if (text3.Length != 0)
				{
					AllowedSet.Add(text3);
					if (!AllowedRaw.Contains(text2))
					{
						AllowedRaw.Add(text2);
					}
				}
			}
		}

		private static string[] ToSortedArray(HashSet<string> set)
		{
			if (set == null)
			{
				return new string[0];
			}
			string[] array = new string[set.Count];
			set.CopyTo(array);
			Array.Sort(array, (IComparer<string>?)StringComparer.OrdinalIgnoreCase);
			return array;
		}

		private static string[] ToSortedArray(IEnumerable<string> seq)
		{
			if (seq == null)
			{
				return new string[0];
			}
			List<string> list = new List<string>(seq);
			list.Sort(StringComparer.OrdinalIgnoreCase);
			return list.ToArray();
		}

		private void PatchAll()
		{
			Type[] array = new Type[7]
			{
				typeof(Smelter_OnAddOre_Patch),
				typeof(Smelter_OnAddFuel_Patch),
				typeof(Fireplace_Interact_Patch),
				typeof(Container_Awake_Patch),
				typeof(Container_OnDestroyed_Patch),
				typeof(Vagon_Awake_Patch),
				typeof(Vagon_OnDestroy_Patch)
			};
			int num = 0;
			Type[] array2 = array;
			foreach (Type type in array2)
			{
				try
				{
					_harmony.CreateClassProcessor(type).Patch();
					((BaseUnityPlugin)this).Logger.LogInfo((object)("已挂载补丁:" + type.Name));
					num++;
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("挂载失败 " + type.Name + ":" + ex));
				}
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} 补丁 {2}/{3} 挂载成功。", "EasyCraft", "1.0.6", num, array.Length));
		}

		private void SelfCheck()
		{
			try
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)Describe(typeof(Smelter), "OnAddOre"));
				((BaseUnityPlugin)this).Logger.LogInfo((object)Describe(typeof(Smelter), "OnAddFuel"));
				((BaseUnityPlugin)this).Logger.LogInfo((object)Describe(typeof(Fireplace), "Interact"));
				((BaseUnityPlugin)this).Logger.LogInfo((object)Describe(typeof(Container), "Awake"));
				((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("BatchSize={0};火源批量={1},生效排除表=[{2}],生效白名单=[{3}](空=全部火源),一次一个名单=[{4}],RespectToggle={5},长按兜底间隔={6:0.##}s", CfgBatchSize.Value, CfgFireEnabled.Value ? "开" : "关", FireSources.EffectiveExclude, FireSources.EffectiveWhitelist, FireSources.EffectiveOneAtATime, CfgFireRespectToggle.Value, CfgFireHoldInterval.Value));
				((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("就近取料={0}:半径 {1:0.#} 米,允许的箱子 {2} 种 [{3}](板车={7}),守卫石检查={4},接管所有权={5},占用保护={6}。", CfgNearbyEnabled.Value ? "开" : "关", CfgRadius.Value, AllowedRaw.Count, string.Join(",", ToSortedArray(AllowedRaw)), CfgRespectWards.Value, CfgClaimOwnership.Value, (CfgSkipInUse.Value || CfgSkipIfPlayerNear.Value) ? "开" : "关", CfgIncludeCarts.Value ? "开" : "关"));
				((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("  箱子名匹配已归一化(去 piece_ 前缀 + 去所有下划线 + 忽略大小写):生效匹配键=[{0}]", string.Join(",", ToSortedArray(AllowedSet))));
				((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("站点进料限制:{0} 组规则;生效值=[{1}](空=不限制任何站点)。生效明细会在首次装填该站点时打印。", StationOreFilter.StationCount, string.IsNullOrEmpty(StationOreFilter.EffectiveSpec) ? "" : StationOreFilter.EffectiveSpec));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("自检失败:" + ex.Message));
			}
		}

		private string Describe(Type type, string method)
		{
			MethodInfo methodInfo = AccessTools.Method(type, method, (Type[])null, (Type[])null);
			Patches val = ((methodInfo != null) ? Harmony.GetPatchInfo((MethodBase)methodInfo) : null);
			return $"自检:{type.Name}.{method} prefix={((val != null && val.Prefixes != null) ? val.Prefixes.Count : 0)} postfix={((val != null && val.Postfixes != null) ? val.Postfixes.Count : 0)}";
		}

		private void OnDestroy()
		{
			try
			{
				if (ExtraSource == _source)
				{
					ExtraSource = null;
				}
				if (_harmony != null)
				{
					_harmony.UnpatchSelf();
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("卸载失败:" + ex.Message));
			}
		}
	}
	internal static class ContainerRegistry
	{
		private static readonly List<Container> List = new List<Container>(128);

		internal static int Count => List.Count;

		internal static void Add(Container c)
		{
			if (!((Object)(object)c == (Object)null) && !List.Contains(c))
			{
				List.Add(c);
			}
		}

		internal static void Remove(Container c)
		{
			if (!((Object)(object)c == (Object)null))
			{
				List.Remove(c);
			}
		}

		internal static List<Container> Snapshot()
		{
			List<Container> list = new List<Container>(List.Count);
			for (int num = List.Count - 1; num >= 0; num--)
			{
				Container val = List[num];
				if ((Object)(object)val == (Object)null)
				{
					List.RemoveAt(num);
				}
				else
				{
					list.Add(val);
				}
			}
			return list;
		}
	}
	internal static class CartRegistry
	{
		private static readonly List<Vagon> List = new List<Vagon>(8);

		internal static int Count => List.Count;

		internal static void Add(Vagon v)
		{
			if (!((Object)(object)v == (Object)null) && !List.Contains(v))
			{
				List.Add(v);
			}
		}

		internal static void Remove(Vagon v)
		{
			if (!((Object)(object)v == (Object)null))
			{
				List.Remove(v);
			}
		}

		internal static Vagon FindByContainer(Container c)
		{
			if ((Object)(object)c == (Object)null)
			{
				return null;
			}
			for (int num = List.Count - 1; num >= 0; num--)
			{
				Vagon val = List[num];
				if ((Object)(object)val == (Object)null)
				{
					List.RemoveAt(num);
				}
				else
				{
					Container val2 = null;
					try
					{
						val2 = val.m_container;
					}
					catch
					{
					}
					if (!((Object)(object)val2 == (Object)null) && val2 == c)
					{
						return val;
					}
				}
			}
			return null;
		}
	}
	internal enum FillOutcome
	{
		Filled,
		HandledNoFill,
		Fallback
	}
	internal static class Engine
	{
		internal struct Take
		{
			internal string SharedName;

			internal string PrefabName;

			internal bool Cheated;

			internal int Total;

			internal int FromExtra;
		}

		private static readonly FieldInfo F_Conversion = AccessTools.Field(typeof(Smelter), "m_conversion");

		private static readonly FieldInfo F_AddedOreTime = AccessTools.Field(typeof(Smelter), "m_addedOreTime");

		private static readonly FieldInfo F_SmelterNview = AccessTools.Field(typeof(Smelter), "m_nview");

		private static readonly MethodInfo M_GetQueueSize = AccessTools.Method(typeof(Smelter), "GetQueueSize", (Type[])null, (Type[])null);

		private static readonly MethodInfo M_GetFuel = AccessTools.Method(typeof(Smelter), "GetFuel", (Type[])null, (Type[])null);

		private static readonly MethodInfo M_SetAnimation = AccessTools.Method(typeof(Smelter), "SetAnimation", (Type[])null, (Type[])null);

		private static readonly MethodInfo M_IsItemAllowed = AccessTools.Method(typeof(Smelter), "IsItemAllowed", new Type[1] { typeof(ItemData) }, (Type[])null);

		private static readonly MethodInfo M_Message = FindMessageMethod();

		private static readonly FieldInfo F_FireNview = AccessTools.Field(typeof(Fireplace), "m_nview");

		private static float _lastFireFillTime = -999f;

		private static float _lastFireMsgTime = -999f;

		private static readonly HashSet<string> FillLogged = new HashSet<string>();

		internal static FillOutcome FillOre(Smelter smelter, Humanoid user, ItemData item)
		{
			return FillSmelter(smelter, user, item, fuel: false);
		}

		internal static FillOutcome FillFuel(Smelter smelter, Humanoid user, ItemData item)
		{
			return FillSmelter(smelter, user, item, fuel: true);
		}

		private static FillOutcome FillSmelter(Smelter smelter, Humanoid user, ItemData item, bool fuel)
		{
			string text = (fuel ? "燃料口" : "进料口");
			if ((Object)(object)smelter == (Object)null || (Object)(object)user == (Object)null)
			{
				return FillOutcome.Fallback;
			}
			ZNetView smelterNView = GetSmelterNView(smelter);
			if ((Object)(object)smelterNView == (Object)null || !smelterNView.IsValid())
			{
				Note(smelter, text + ":拿不到有效的 ZNetView");
				return FillOutcome.Fallback;
			}
			if (user.GetInventory() == null)
			{
				Note(smelter, text + ":GetInventory() 返回 null");
				return FillOutcome.Fallback;
			}
			int value = EasyCraftPlugin.CfgBatchSize.Value;
			int num;
			string[] array;
			if (fuel)
			{
				string text2 = SmelterFuelName(smelter);
				if (string.IsNullOrEmpty(text2))
				{
					Note(smelter, text + ":m_fuelItem 为空,读不到燃料名");
					return FillOutcome.Fallback;
				}
				if (item != null && item.m_shared != null && item.m_shared.m_name != text2)
				{
					Msg(user, "$msg_wrongitem");
					return FillOutcome.HandledNoFill;
				}
				num = (int)Mathf.Floor((float)smelter.m_maxFuel - GetSmelterFuel(smelter));
				if (num <= 0)
				{
					Msg(user, "$msg_itsfull");
					return FillOutcome.HandledNoFill;
				}
				array = new string[1] { text2 };
			}
			else
			{
				if (item != null && !IsItemAllowed(smelter, item))
				{
					Msg(user, "$msg_wontwork");
					return FillOutcome.HandledNoFill;
				}
				num = smelter.m_maxOre - GetQueueSize(smelter);
				if (num <= 0)
				{
					Msg(user, "$msg_itsfull");
					return FillOutcome.HandledNoFill;
				}
				array = OreSharedNames(smelter);
				if (array.Length == 0)
				{
					Note(smelter, text + ":m_conversion 里读不到任何可熔材料名");
					return FillOutcome.Fallback;
				}
			}
			int num2 = Math.Min(value, num);
			if (num2 <= 0)
			{
				return FillOutcome.HandledNoFill;
			}
			ItemData explicitItem = ((!fuel && item != null && item.m_shared != null) ? item : null);
			Take r = TakeItems((Component)(object)smelter, user, explicitItem, array, num2);
			if (r.Total <= 0 || r.SharedName == null)
			{
				if (fuel)
				{
					Msg(user, "$msg_donthaveany " + (r.SharedName ?? SmelterFuelName(smelter)));
				}
				else
				{
					Msg(user, "$msg_noprocessableitems");
				}
				return FillOutcome.HandledNoFill;
			}
			try
			{
				if (fuel)
				{
					for (int i = 0; i < r.Total; i++)
					{
						smelterNView.InvokeRPC("RPC_AddFuel", Array.Empty<object>());
					}
				}
				else
				{
					if (string.IsNullOrEmpty(r.PrefabName))
					{
						EasyCraftPlugin.Log.LogWarning((object)("拿不到预制体名,本已扣掉的 " + r.Total + " 个料无法下发,请把这条日志发我:" + r.SharedName));
						Msg(user, "$msg_noprocessableitems");
						return FillOutcome.HandledNoFill;
					}
					for (int j = 0; j < r.Total; j++)
					{
						smelterNView.InvokeRPC("RPC_AddOre", new object[2] { r.PrefabName, r.Cheated });
					}
				}
			}
			catch (Exception ex)
			{
				EasyCraftPlugin.Log.LogError((object)("下发 RPC 失败:" + ex));
				Msg(user, "$msg_noprocessableitems");
				return FillOutcome.HandledNoFill;
			}
			if (!fuel)
			{
				try
				{
					if (F_AddedOreTime != null)
					{
						F_AddedOreTime.SetValue(smelter, Time.time);
					}
					if (smelter.m_addOreAnimationDuration > 0f && M_SetAnimation != null)
					{
						M_SetAnimation.Invoke(smelter, new object[1] { true });
					}
				}
				catch
				{
				}
			}
			Msg(user, "$msg_added " + r.SharedName + " x" + r.Total);
			LogFill("站点" + text, ((Component)smelter).gameObject, r, num);
			return FillOutcome.Filled;
		}

		internal static float GetFireFuel(Fireplace fp)
		{
			ZNetView fireNView = GetFireNView(fp);
			try
			{
				if ((Object)(object)fireNView != (Object)null && fireNView.IsValid())
				{
					return fireNView.GetZDO().GetFloat(ZDOVars.s_fuel, 0f);
				}
			}
			catch
			{
			}
			return 0f;
		}

		internal static ZNetView GetFireNView(Fireplace fp)
		{
			if ((Object)(object)fp == (Object)null)
			{
				return null;
			}
			try
			{
				if (F_FireNview != null)
				{
					object? value = F_FireNview.GetValue(fp);
					ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
				}
			}
			catch
			{
			}
			try
			{
				ZNetView component = ((Component)fp).GetComponent<ZNetView>();
				if ((Object)(object)component != (Object)null)
				{
					return component;
				}
			}
			catch
			{
			}
			try
			{
				return ((Component)fp).GetComponentInParent<ZNetView>();
			}
			catch
			{
			}
			return null;
		}

		internal static string FireFuelName(Fireplace fp)
		{
			if ((Object)(object)fp == (Object)null || (Object)(object)fp.m_fuelItem == (Object)null)
			{
				return null;
			}
			ItemData itemData = fp.m_fuelItem.m_itemData;
			if (itemData == null || itemData.m_shared == null)
			{
				return null;
			}
			return itemData.m_shared.m_name;
		}

		internal static FillOutcome FillFireplace(Fireplace fp, Humanoid user, bool hold, int wantOverride = 0)
		{
			if ((Object)(object)fp == (Object)null || (Object)(object)user == (Object)null)
			{
				return FillOutcome.Fallback;
			}
			if (hold)
			{
				float num = ((fp.m_holdRepeatInterval > 0f) ? fp.m_holdRepeatInterval : EasyCraftPlugin.CfgFireHoldInterval.Value);
				if (num <= 0f)
				{
					FireSources.ReportBail(((Component)fp).gameObject, "长按 + m_holdRepeatInterval=0(原版长按本来就不加料,本模组照原版)");
					return FillOutcome.Fallback;
				}
				if (Time.time - _lastFireFillTime < num)
				{
					return FillOutcome.HandledNoFill;
				}
			}
			ZNetView fireNView = GetFireNView(fp);
			if ((Object)(object)fireNView == (Object)null || !fireNView.IsValid())
			{
				FireSources.ReportBail(((Component)fp).gameObject, "拿不到有效的 ZNetView");
				return FillOutcome.Fallback;
			}
			if (user.GetInventory() == null)
			{
				FireSources.ReportBail(((Component)fp).gameObject, "GetInventory() 返回 null");
				return FillOutcome.Fallback;
			}
			string text = FireFuelName(fp);
			if (string.IsNullOrEmpty(text))
			{
				FireSources.ReportBail(((Component)fp).gameObject, "m_fuelItem 为空,读不到燃料名");
				return FillOutcome.Fallback;
			}
			try
			{
				if (!fireNView.HasOwner())
				{
					fireNView.ClaimOwnership();
				}
			}
			catch
			{
			}
			float maxFuel = fp.m_maxFuel;
			int num2 = Mathf.CeilToInt(GetFireFuel(fp));
			if ((float)num2 >= maxFuel)
			{
				MsgThrottled(user, FireSources.Loc("$msg_cantaddmore", text));
				return FillOutcome.HandledNoFill;
			}
			int num3 = Mathf.FloorToInt(maxFuel) - num2;
			if (num3 <= 0)
			{
				MsgThrottled(user, FireSources.Loc("$msg_cantaddmore", text));
				return FillOutcome.HandledNoFill;
			}
			int count = Math.Min((wantOverride > 0) ? wantOverride : EasyCraftPlugin.CfgBatchSize.Value, num3);
			Take r = TakeItems((Component)(object)fp, user, null, new string[1] { text }, count);
			if (r.Total <= 0)
			{
				MsgThrottled(user, "$msg_outof " + text);
				return FillOutcome.HandledNoFill;
			}
			_lastFireFillTime = Time.time;
			try
			{
				fireNView.InvokeRPC("RPC_AddFuelAmount", new object[1] { (float)r.Total });
			}
			catch (Exception ex)
			{
				EasyCraftPlugin.Log.LogError((object)("下发 RPC_AddFuelAmount 失败:" + ex));
				return FillOutcome.HandledNoFill;
			}
			Msg(user, FireSources.Loc("$msg_fireadding", r.SharedName) + " x" + r.Total);
			LogFill("火源", ((Component)fp).gameObject, r, num3);
			return FillOutcome.Filled;
		}

		private static void MsgThrottled(Humanoid user, string text)
		{
			if (!(Time.time - _lastFireMsgTime < 0.5f))
			{
				_lastFireMsgTime = Time.time;
				Msg(user, text);
			}
		}

		internal static Take TakeItems(Component target, Humanoid user, ItemData explicitItem, string[] candidates, int count)
		{
			Take result = default(Take);
			if (count <= 0 || (Object)(object)user == (Object)null)
			{
				return result;
			}
			Inventory inventory = user.GetInventory();
			if (inventory == null)
			{
				return result;
			}
			if (explicitItem != null && explicitItem.m_shared != null)
			{
				result.SharedName = explicitItem.m_shared.m_name;
				result.PrefabName = PrefabNameOf(explicitItem);
				result.Cheated = explicitItem.m_cheated;
			}
			else if (candidates != null)
			{
				foreach (string text in candidates)
				{
					if (!string.IsNullOrEmpty(text))
					{
						ItemData item = inventory.GetItem(text, -1, false);
						if (item != null)
						{
							result.SharedName = item.m_shared.m_name;
							result.PrefabName = PrefabNameOf(item);
							result.Cheated = item.m_cheated;
							break;
						}
					}
				}
			}
			if (result.SharedName != null)
			{
				int val = inventory.CountItems(result.SharedName, -1, false);
				int num = Math.Min(count, val);
				if (num > 0)
				{
					inventory.RemoveItem(result.SharedName, num, -1, false);
					result.Total = num;
				}
			}
			if (result.Total < count)
			{
				IFillSource extraSource = EasyCraftPlugin.ExtraSource;
				if (extraSource != null)
				{
					string[] sharedNames = ((result.SharedName == null) ? candidates : new string[1] { result.SharedName });
					string sharedName = null;
					string prefabName = null;
					bool cheated = false;
					int num2 = 0;
					try
					{
						num2 = extraSource.Provide(target, user, sharedNames, count - result.Total, out sharedName, out prefabName, out cheated);
					}
					catch (Exception ex)
					{
						EasyCraftPlugin.Log.LogError((object)("扩展料源报错:" + ex));
					}
					if (num2 > 0)
					{
						result.FromExtra = num2;
						result.Total += num2;
						if (result.SharedName == null)
						{
							result.SharedName = sharedName;
						}
						if (result.PrefabName == null)
						{
							result.PrefabName = prefabName;
						}
						if (!result.Cheated)
						{
							result.Cheated = cheated;
						}
					}
				}
				else if (EasyCraftPlugin.DebugOn)
				{
					EasyCraftPlugin.Log.LogInfo((object)("背包只有 " + result.Total + " 个,但没有注册扩展料源。"));
				}
			}
			return result;
		}

		internal static string SmelterFuelName(Smelter smelter)
		{
			if ((Object)(object)smelter == (Object)null || (Object)(object)smelter.m_fuelItem == (Object)null)
			{
				return null;
			}
			ItemData itemData = smelter.m_fuelItem.m_itemData;
			if (itemData == null || itemData.m_shared == null)
			{
				return null;
			}
			return itemData.m_shared.m_name;
		}

		internal static string[] OreSharedNames(Smelter smelter)
		{
			List<StationOreFilter.Option> list = new List<StationOreFilter.Option>();
			if ((Object)(object)smelter == (Object)null || F_Conversion == null)
			{
				FireSources.NoteOnce("m_conversion|null", "[站点] 反射不到 Smelter.m_conversion 字段");
				return new string[0];
			}
			IEnumerable enumerable;
			try
			{
				enumerable = F_Conversion.GetValue(smelter) as IEnumerable;
			}
			catch (Exception ex)
			{
				EasyCraftPlugin.Log.LogWarning((object)("读取 m_conversion 失败:" + ex.Message));
				return new string[0];
			}
			if (enumerable == null)
			{
				return new string[0];
			}
			foreach (object item in enumerable)
			{
				if (item == null)
				{
					continue;
				}
				ItemDrop itemDropField = GetItemDropField(item, "m_from");
				if ((Object)(object)itemDropField == (Object)null)
				{
					continue;
				}
				ItemData itemData = itemDropField.m_itemData;
				if (itemData == null || itemData.m_shared == null)
				{
					continue;
				}
				string name = itemData.m_shared.m_name;
				if (string.IsNullOrEmpty(name))
				{
					continue;
				}
				string prefab = null;
				try
				{
					prefab = (((Object)(object)((Component)itemDropField).gameObject != (Object)null) ? ((Object)((Component)itemDropField).gameObject).name : ((Object)itemDropField).name);
				}
				catch
				{
				}
				bool flag = false;
				foreach (StationOreFilter.Option item2 in list)
				{
					if (string.Equals(item2.SharedName, name, StringComparison.Ordinal))
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					list.Add(new StationOreFilter.Option
					{
						SharedName = name,
						Prefab = prefab
					});
				}
			}
			GameObject stationGo = null;
			try
			{
				stationGo = ((Component)smelter).gameObject;
			}
			catch
			{
			}
			return StationOreFilter.Apply(stationGo, list);
		}

		private static ItemDrop GetItemDropField(object owner, string field)
		{
			try
			{
				Type type = owner.GetType();
				while (type != null)
				{
					FieldInfo field2 = type.GetField(field, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field2 != null)
					{
						object? value = field2.GetValue(owner);
						return (ItemDrop)((value is ItemDrop) ? value : null);
					}
					type = type.BaseType;
				}
			}
			catch
			{
			}
			return null;
		}

		internal static string PrefabNameOf(ItemData item)
		{
			if (item == null)
			{
				return null;
			}
			try
			{
				if ((Object)(object)item.m_dropPrefab != (Object)null)
				{
					return ((Object)item.m_dropPrefab).name;
				}
			}
			catch
			{
			}
			return null;
		}

		internal static ZNetView GetSmelterNView(Smelter smelter)
		{
			try
			{
				if (F_SmelterNview != null)
				{
					object? value = F_SmelterNview.GetValue(smelter);
					ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
				}
			}
			catch
			{
			}
			try
			{
				return ((Component)smelter).GetComponent<ZNetView>();
			}
			catch
			{
			}
			return null;
		}

		internal static float GetSmelterFuel(Smelter smelter)
		{
			try
			{
				if (M_GetFuel != null)
				{
					return (float)M_GetFuel.Invoke(smelter, null);
				}
			}
			catch
			{
			}
			return 0f;
		}

		internal static int GetQueueSize(Smelter smelter)
		{
			try
			{
				if (M_GetQueueSize != null)
				{
					return (int)M_GetQueueSize.Invoke(smelter, null);
				}
			}
			catch
			{
			}
			return 0;
		}

		private static bool IsItemAllowed(Smelter smelter, ItemData item)
		{
			if (item == null || item.m_shared == null)
			{
				return false;
			}
			try
			{
				if (M_IsItemAllowed != null)
				{
					return (bool)M_IsItemAllowed.Invoke(smelter, new object[1] { item });
				}
			}
			catch
			{
			}
			return true;
		}

		private static MethodInfo FindMessageMethod()
		{
			MethodInfo result = null;
			int num = -1;
			try
			{
				MethodInfo[] methods = typeof(Character).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo in methods)
				{
					if (!(methodInfo.Name != "Message"))
					{
						ParameterInfo[] parameters = methodInfo.GetParameters();
						if (parameters.Length >= 2 && parameters[0].ParameterType.IsEnum && !(parameters[1].ParameterType != typeof(string)) && parameters.Length > num)
						{
							num = parameters.Length;
							result = methodInfo;
						}
					}
				}
			}
			catch
			{
			}
			return result;
		}

		private static void Msg(Humanoid user, string text)
		{
			if ((Object)(object)user == (Object)null || M_Message == null)
			{
				return;
			}
			try
			{
				ParameterInfo[] parameters = M_Message.GetParameters();
				object[] array = new object[parameters.Length];
				array[0] = Enum.ToObject(parameters[0].ParameterType, 2);
				array[1] = text;
				for (int i = 2; i < parameters.Length; i++)
				{
					array[i] = (parameters[i].ParameterType.IsValueType ? Activator.CreateInstance(parameters[i].ParameterType) : null);
				}
				M_Message.Invoke(user, array);
			}
			catch (Exception ex)
			{
				if (EasyCraftPlugin.DebugOn)
				{
					EasyCraftPlugin.Log.LogDebug((object)("提示消息失败:" + ex.Message));
				}
			}
		}

		private static void Note(Smelter smelter, string reason)
		{
			string text = (((Object)(object)smelter != (Object)null) ? ((Object)((Component)smelter).gameObject).name : "<null>");
			FireSources.NoteOnce("smelter|" + text + "|" + reason, "[站点跳过] " + text + " → " + reason + "(本次交回原版处理)");
		}

		private static void LogFill(string kind, GameObject go, Take r, int capacity)
		{
			string text = FireSources.BestPrefabName(go);
			if (string.IsNullOrEmpty(text))
			{
				text = "<无名>";
			}
			if (FillLogged.Add(kind + "|" + text) || EasyCraftPlugin.DebugOn)
			{
				EasyCraftPlugin.Log.LogInfo((object)$"[{kind}装填] {text}|{r.SharedName} 装了 {r.Total} 个(本次上限余量 {capacity}|背包 {r.Total - r.FromExtra}|箱子等外部来源 {r.FromExtra})");
			}
		}
	}
	internal static class FireSources
	{
		internal sealed class NameSet
		{
			private readonly HashSet<string> _items;

			internal int Count => _items.Count;

			internal string Joined => string.Join(",", _items);

			internal NameSet(IEnumerable<string> items)
			{
				_items = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
				if (items == null)
				{
					return;
				}
				foreach (string item in items)
				{
					if (!string.IsNullOrEmpty(item))
					{
						_items.Add(item.Trim());
					}
				}
			}

			internal bool Contains(string name)
			{
				if (!string.IsNullOrEmpty(name))
				{
					return _items.Contains(name);
				}
				return false;
			}
		}

		internal const string DefaultExclude = "fire_pit,piece_firepit";

		internal const string DefaultOneAtATime = "fire_pit,piece_firepit";

		private static NameSet _exclude = new NameSet(new string[1] { "fire_pit,piece_firepit" });

		private static NameSet _oneAtATime = new NameSet(new string[1] { "fire_pit,piece_firepit" });

		private static NameSet _whitelist = new NameSet(new string[0]);

		private static readonly MethodInfo M_PrefabName = FindPrefabNameMethod();

		private static readonly HashSet<string> Probed = new HashSet<string>();

		private static readonly HashSet<string> Notes = new HashSet<string>();

		private static MethodInfo _mZdoPrefabName;

		private static bool _zdoProbed;

		private static PropertyInfo _locInstance;

		private static MethodInfo _localizeArgs;

		private static bool _locResolved;

		internal static int ExcludeCount => _exclude.Count;

		internal static int WhitelistCount => _whitelist.Count;

		internal static string EffectiveExclude => _exclude.Joined;

		internal static string EffectiveWhitelist => _whitelist.Joined;

		internal static string EffectiveOneAtATime => _oneAtATime.Joined;

		internal static void Reload(string excludeCsv, string whitelistCsv, string oneAtATimeCsv)
		{
			_exclude = new NameSet(Split(excludeCsv));
			_oneAtATime = new NameSet(Split(oneAtATimeCsv));
			_whitelist = new NameSet(Split(whitelistCsv));
		}

		private static List<string> Split(string csv)
		{
			List<string> list = new List<string>();
			if (string.IsNullOrEmpty(csv))
			{
				return list;
			}
			string[] array = csv.Split(new char[1] { ',' });
			foreach (string text in array)
			{
				if (text.Trim().Length > 0)
				{
					list.Add(text.Trim());
				}
			}
			return list;
		}

		internal static bool IsAllowed(GameObject go)
		{
			if ((Object)(object)go == (Object)null)
			{
				return false;
			}
			Transform val = go.transform;
			int num = 0;
			while ((Object)(object)val != (Object)null && num++ < 8)
			{
				string text = PrefabName(((Component)val).gameObject);
				if (!string.IsNullOrEmpty(text))
				{
					if (_exclude.Contains(text))
					{
						return false;
					}
					if (_whitelist.Count <= 0)
					{
						return true;
					}
					if (_whitelist.Contains(text))
					{
						return true;
					}
				}
				val = val.parent;
			}
			return false;
		}

		internal static bool IsOneAtATime(GameObject go)
		{
			if ((Object)(object)go == (Object)null || _oneAtATime.Count == 0)
			{
				return false;
			}
			Transform val = go.transform;
			int num = 0;
			while ((Object)(object)val != (Object)null && num++ < 8)
			{
				string text = PrefabName(((Component)val).gameObject);
				if (!string.IsNullOrEmpty(text) && _oneAtATime.Contains(text))
				{
					return true;
				}
				val = val.parent;
			}
			return false;
		}

		internal static string BestPrefabName(GameObject go)
		{
			if ((Object)(object)go == (Object)null)
			{
				return null;
			}
			Transform val = go.transform;
			int num = 0;
			string text = null;
			while ((Object)(object)val != (Object)null && num++ < 8)
			{
				string text2 = PrefabName(((Component)val).gameObject);
				if (!string.IsNullOrEmpty(text2))
				{
					if (text2.StartsWith("piece_", StringComparison.OrdinalIgnoreCase))
					{
						return text2;
					}
					if (text == null)
					{
						text = text2;
					}
				}
				val = val.parent;
			}
			return text;
		}

		internal static void NoteOnce(string key, string message)
		{
			if (string.IsNullOrEmpty(key))
			{
				key = "?";
			}
			if (!Notes.Add(key))
			{
				return;
			}
			try
			{
				EasyCraftPlugin.Log.LogInfo((object)message);
			}
			catch
			{
			}
		}

		internal static void ReportBail(GameObject go, string reason)
		{
			string text = BestPrefabName(go);
			if (string.IsNullOrEmpty(text))
			{
				text = "<无名>";
			}
			NoteOnce(text + "|" + reason, "[火源跳过] " + text + " → " + reason + "(本次交回原版处理)");
		}

		internal static void Probe(Fireplace fp)
		{
			if ((Object)(object)fp == (Object)null)
			{
				return;
			}
			string text = BestPrefabName(((Component)fp).gameObject);
			if (string.IsNullOrEmpty(text))
			{
				text = "<无名>";
			}
			if (!Probed.Add(text))
			{
				return;
			}
			try
			{
				ZNetView component = ((Component)fp).gameObject.GetComponent<ZNetView>();
				float num = 0f;
				bool flag = (Object)(object)component != (Object)null && component.IsValid();
				if (flag)
				{
					num = component.GetZDO().GetFloat(ZDOVars.s_fuel, 0f);
				}
				string text2 = "<m_fuelItem 为 null>";
				if ((Object)(object)fp.m_fuelItem != (Object)null)
				{
					ItemData itemData = fp.m_fuelItem.m_itemData;
					text2 = ((itemData != null && itemData.m_shared != null) ? itemData.m_shared.m_name : "<m_fuelItem.m_itemData.m_shared 为 null>");
				}
				EasyCraftPlugin.Log.LogInfo((object)string.Format("[火源探测] {0}|组件在={1}|ZDO名={2}|ZNetView={3} 有效={4} 燃料={5:0.##}|可加燃料={6} 可开关={7} 无限燃料={8} 长按重复间隔={9:0.##}|上限={10:0.#} 燃料物品={11}|结论={12}", text, ((Object)((Component)fp).gameObject).name, ZdoPrefabName(component) ?? "<取不到>", ((Object)(object)component != (Object)null) ? "有" : "无", flag, num, fp.m_canRefill, fp.m_canTurnOff, fp.m_infiniteFuel, fp.m_holdRepeatInterval, fp.m_maxFuel, text2, DescribeDecision(((Component)fp).gameObject)));
			}
			catch (Exception ex)
			{
				EasyCraftPlugin.Log.LogWarning((object)("[火源探测] " + text + " 读取状态失败:" + ex.Message));
			}
		}

		private static string DescribeDecision(GameObject go)
		{
			if (IsOneAtATime(go))
			{
				return "本模组接管:一次只装 1 个(OneAtATime),料源可用";
			}
			if (IsAllowed(go))
			{
				return "本模组接管";
			}
			return "交回原版(被 Exclude 排除,或不在 Sources 白名单里)";
		}

		private static string ZdoPrefabName(ZNetView nv)
		{
			if ((Object)(object)nv == (Object)null)
			{
				return null;
			}
			if (!_zdoProbed)
			{
				_zdoProbed = true;
				try
				{
					Type type = AccessTools.TypeByName("ZNetView");
					if (type != null)
					{
						_mZdoPrefabName = type.GetMethod("GetPrefabName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
					}
				}
				catch
				{
				}
			}
			if (_mZdoPrefabName == null)
			{
				return null;
			}
			try
			{
				return (string)_mZdoPrefabName.Invoke(nv, null);
			}
			catch
			{
				return null;
			}
		}

		private static MethodInfo FindPrefabNameMethod()
		{
			try
			{
				Type type = AccessTools.TypeByName("Utils");
				if (type == null)
				{
					return null;
				}
				return AccessTools.Method(type, "GetPrefabName", new Type[1] { typeof(GameObject) }, (Type[])null);
			}
			catch
			{
				return null;
			}
		}

		internal static string PrefabName(GameObject go)
		{
			if ((Object)(object)go == (Object)null)
			{
				return null;
			}
			if (M_PrefabName != null)
			{
				try
				{
					string text = (string)M_PrefabName.Invoke(null, new object[1] { go });
					if (!string.IsNullOrEmpty(text))
					{
						return text.Trim();
					}
				}
				catch
				{
				}
			}
			string text2 = ((Object)go).name;
			int num = text2.IndexOf("(Clone)");
			if (num >= 0)
			{
				text2 = text2.Substring(0, num);
			}
			num = text2.IndexOf(' ');
			if (num >= 0)
			{
				text2 = text2.Substring(0, num);
			}
			return text2.Trim();
		}

		private static void ResolveLocalization()
		{
			if (_locResolved)
			{
				return;
			}
			_locResolved = true;
			try
			{
				Type type = AccessTools.TypeByName("Localization");
				if (type == null)
				{
					EasyCraftPlugin.Log.LogWarning((object)"未找到 Localization 类型,火源提示将退回原始键名。");
					return;
				}
				_locInstance = type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public);
				MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public);
				foreach (MethodInfo methodInfo in methods)
				{
					if (!(methodInfo.Name != "Localize"))
					{
						ParameterInfo[] parameters = methodInfo.GetParameters();
						if (parameters.Length == 2 && !(parameters[0].ParameterType != typeof(string)) && !(parameters[1].ParameterType != typeof(string[])))
						{
							_localizeArgs = methodInfo;
							break;
						}
					}
				}
				EasyCraftPlugin.Log.LogInfo((object)("本地化支持:" + ((_locInstance != null) ? "instance OK " : "instance 缺 ") + ((_localizeArgs != null) ? "Localize(string,string[]) OK" : "Localize 两参重载缺失")));
			}
			catch (Exception ex)
			{
				EasyCraftPlugin.Log.LogWarning((object)("解析 Localization 失败:" + ex.Message));
			}
		}

		internal static void SelfReport()
		{
			ResolveLocalization();
		}

		internal static string Loc(string key, string arg)
		{
			ResolveLocalization();
			try
			{
				if (_locInstance != null && _localizeArgs != null)
				{
					object value = _locInstance.GetValue(null, null);
					if (value != null)
					{
						return (string)_localizeArgs.Invoke(value, new object[2]
						{
							key,
							new string[1] { arg }
						});
					}
				}
			}
			catch (Exception ex)
			{
				EasyCraftPlugin.Log.LogDebug((object)("本地化失败 " + key + ":" + ex.Message));
			}
			return key + " " + arg;
		}
	}
	internal sealed class NearbyFillSource : IFillSource
	{
		private static readonly MethodInfo M_PrefabName = FindPrefabNameMethod();

		private static readonly MethodInfo M_ContainerLoad = AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null) ?? ResolveContainerLoad();

		private static readonly FieldInfo F_ContainerNView = AccessTools.Field(typeof(Container), "m_nview");

		private static readonly FieldInfo F_VagonAttachJoin = AccessTools.Field(typeof(Vagon), "m_attachJoin");

		private static MethodInfo ResolveContainerLoad()
		{
			try
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(Container), "Load", Type.EmptyTypes, (Type[])null);
				if (methodInfo != null)
				{
					return methodInfo;
				}
				foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(Container)))
				{
					if (declaredMethod.Name == "Load" && declaredMethod.GetParameters().Length == 0)
					{
						return declaredMethod;
					}
				}
			}
			catch
			{
			}
			return null;
		}

		public int Provide(Component target, Humanoid user, string[] sharedNames, int maxCount, out string sharedName, out string prefabName, out bool cheated)
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			sharedName = null;
			prefabName = null;
			cheated = false;
			if (EasyCraftPlugin.CfgNearbyEnabled == null || !EasyCraftPlugin.CfgNearbyEnabled.Value)
			{
				return 0;
			}
			if ((Object)(object)target == (Object)null || (Object)(object)user == (Object)null)
			{
				return 0;
			}
			if (sharedNames == null || sharedNames.Length == 0)
			{
				return 0;
			}
			if (maxCount <= 0)
			{
				return 0;
			}
			if (EasyCraftPlugin.AllowedSet.Count == 0 && !EasyCraftPlugin.CfgIncludeCarts.Value)
			{
				if (EasyCraftPlugin.DebugOn)
				{
					EasyCraftPlugin.Log.LogWarning((object)"AllowedContainers 为空、且板车未启用,没有料源可用。");
				}
				return 0;
			}
			Vector3 position = target.transform.position;
			Vector3 position2 = ((Component)user).transform.position;
			float value = EasyCraftPlugin.CfgRadius.Value;
			float num = value * value;
			List<KeyValuePair<float, Container>> list = new List<KeyValuePair<float, Container>>();
			int num2 = 0;
			StringBuilder stringBuilder = (EasyCraftPlugin.DebugOn ? new StringBuilder() : null);
			int num3 = 0;
			foreach (Container item2 in ContainerRegistry.Snapshot())
			{
				if ((Object)(object)item2 == (Object)null)
				{
					continue;
				}
				Transform transform = ((Component)item2).transform;
				if ((Object)(object)transform == (Object)null)
				{
					continue;
				}
				Vector3 val = transform.position - position;
				float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
				val = transform.position - position2;
				float sqrMagnitude2 = ((Vector3)(ref val)).sqrMagnitude;
				if (sqrMagnitude > num && sqrMagnitude2 > num)
				{
					continue;
				}
				if (!IsAllowed(item2))
				{
					num3++;
					stringBuilder?.Append("\n    · [未通过白名单] ").Append(DescribeNames((Component)(object)item2));
					continue;
				}
				if (!HasAccess(item2))
				{
					num3++;
					stringBuilder?.Append("\n    · [守卫石无权限] ").Append(DescribeNames((Component)(object)item2));
					continue;
				}
				if (IsBusy(item2, user))
				{
					num2++;
					stringBuilder?.Append("\n    · [有人正在用] ").Append(DescribeNames((Component)(object)item2));
					continue;
				}
				if (IsCart(item2))
				{
					stringBuilder?.Append("\n    · [板车被跳过] ").Append(DescribeNames((Component)(object)item2)).Append(" → ")
						.Append((!EasyCraftPlugin.CfgIncludeCarts.Value) ? "IncludeCarts 开关是关的" : ("被判定为'正在被拖行':" + DescribeAttachSignal(item2)));
				}
				list.Add(new KeyValuePair<float, Container>(Mathf.Min(sqrMagnitude, sqrMagnitude2), item2));
			}
			list.Sort((KeyValuePair<float, Container> a, KeyValuePair<float, Container> b) => a.Key.CompareTo(b.Key));
			int num4 = 0;
			int num5 = 0;
			foreach (KeyValuePair<float, Container> item3 in list)
			{
				if (num4 >= maxCount)
				{
					break;
				}
				Container value2 = item3.Value;
				Inventory inventory = value2.GetInventory();
				if (inventory == null)
				{
					stringBuilder?.Append("\n    · ").Append(PrefabOfComponent((Component)(object)value2)).Append(" → GetInventory() 为 null");
					continue;
				}
				bool flag = false;
				string reason = null;
				if (!EnsureOwner(value2, out reason))
				{
					stringBuilder?.Append("\n    · ").Append(DescribeNames((Component)(object)value2)).Append(" → 接管所有权失败(")
						.Append(reason)
						.Append(")");
					continue;
				}
				RefreshChestInventory(value2, stringBuilder);
				foreach (string text in sharedNames)
				{
					if (string.IsNullOrEmpty(text))
					{
						continue;
					}
					ItemData item = inventory.GetItem(text, -1, false);
					if (item == null)
					{
						continue;
					}
					int val2 = inventory.CountItems(text, -1, false);
					int num7 = Math.Min(maxCount - num4, val2);
					if (num7 > 0)
					{
						inventory.RemoveItem(text, num7, -1, false);
						num4 += num7;
						num5++;
						flag = true;
						if (!VerifyOwnerAfterTake(value2))
						{
							EasyCraftPlugin.Log.LogWarning((object)("[就近料源] " + DescribeNames((Component)(object)value2) + " 取走 " + num7 + " 个 " + text + " 后仍未取得 ZDO 写权 —— 这次改动可能不会被保存,箱子里可能仍在(刷新即可看到回弹)。请把这条日志发给我。"));
						}
						if (sharedName == null)
						{
							sharedName = ((item.m_shared != null) ? item.m_shared.m_name : text);
							prefabName = PrefabOfItem(item);
							cheated = item.m_cheated;
						}
						break;
					}
				}
				if (stringBuilder != null && !flag)
				{
					stringBuilder.Append("\n    · ").Append(DescribeNames((Component)(object)value2)).Append(" → 不含目标材料,箱内共 ")
						.Append(DescribeChest(inventory));
				}
			}
			if (num4 > 0)
			{
				EasyCraftPlugin.Log.LogInfo((object)string.Format("[就近料源] {0}|从 {1} 个箱子取了 {2} 个 {3}(半径 {4:0.#}m,候选箱 {5} 个,跳过占用中 {6} 个)", ((Object)target.gameObject).name, num5, num4, sharedName ?? "-", value, list.Count, num2));
			}
			else if (EasyCraftPlugin.DebugOn)
			{
				EasyCraftPlugin.Log.LogInfo((object)string.Format("[就近料源] {0}|没取到料(半径 {1:0.#}m,候选箱 {2} 个,半径内被拦 {3} 个,跳过占用中 {4} 个,场上板车 {7} 辆)|要的是 [{5}]{6}", ((Object)target.gameObject).name, value, list.Count, num3, num2, string.Join(",", sharedNames), (stringBuilder != null && stringBuilder.Length > 0) ? stringBuilder.ToString() : "(半径内没有任何容器)", CartRegistry.Count));
			}
			return num4;
		}

		private static string DescribeChest(Inventory inv)
		{
			if (inv == null)
			{
				return "<无库存>";
			}
			List<string> list = new List<string>();
			try
			{
				List<ItemData> allItems = inv.GetAllItems();
				if (allItems == null || allItems.Count == 0)
				{
					return "<空箱>";
				}
				HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
				foreach (ItemData item in allItems)
				{
					if (item == null || item.m_shared == null)
					{
						continue;
					}
					string name = item.m_shared.m_name;
					if (!string.IsNullOrEmpty(name) && hashSet.Add(name))
					{
						list.Add(name + "×" + inv.CountItems(name, -1, false));
						if (list.Count >= 8)
						{
							list.Add("…");
							break;
						}
					}
				}
			}
			catch (Exception ex)
			{
				return "<读取失败:" + ex.Message + ">";
			}
			if (list.Count != 0)
			{
				return string.Join(" ", list.ToArray());
			}
			return "<空箱>";
		}

		private static bool IsBusy(Container c, Humanoid me)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			if (EasyCraftPlugin.CfgSkipInUse.Value)
			{
				try
				{
					if (c.IsInUse())
					{
						return true;
					}
				}
				catch
				{
				}
			}
			if (EasyCraftPlugin.CfgSkipIfPlayerNear.Value)
			{
				float value = EasyCraftPlugin.CfgPlayerNearDistance.Value;
				float num = value * value;
				Vector3 position = ((Component)c).transform.position;
				List<Player> list = null;
				try
				{
					list = Player.GetAllPlayers();
				}
				catch
				{
				}
				if (list != null)
				{
					foreach (Player item in list)
					{
						if (!((Object)(object)item == (Object)null) && !((Object)(object)item == (Object)(object)me) && !((Object)(object)item == (Object)(object)Player.m_localPlayer))
						{
							Vector3 val = ((Component)item).transform.position - position;
							if (((Vector3)(ref val)).sqrMagnitude <= num)
							{
								return true;
							}
						}
					}
				}
			}
			return false;
		}

		private static bool IsAllowed(Container c)
		{
			Vagon val = null;
			try
			{
				val = c.m_wagon;
			}
			catch
			{
			}
			if ((Object)(object)val == (Object)null)
			{
				try
				{
					val = CartRegistry.FindByContainer(c);
				}
				catch
				{
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				if (!EasyCraftPlugin.CfgIncludeCarts.Value)
				{
					return false;
				}
				try
				{
					if (val.IsAttached())
					{
						return false;
					}
				}
				catch
				{
				}
				return true;
			}
			string text = PrefabOfComponent((Component)(object)c);
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			return EasyCraftPlugin.AllowedSet.Contains(EasyCraftPlugin.NormalizeContainerName(text));
		}

		private static string DescribeAttachSignal(Container c)
		{
			Vagon val = null;
			try
			{
				val = c.m_wagon;
			}
			catch
			{
			}
			if ((Object)(object)val == (Object)null)
			{
				try
				{
					val = CartRegistry.FindByContainer(c);
				}
				catch
				{
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				return "取不到 Vagon";
			}
			bool flag = false;
			try
			{
				flag = F_VagonAttachJoin != null && F_VagonAttachJoin.GetValue(val) != null;
			}
			catch
			{
			}
			bool flag2 = false;
			try
			{
				ZNetView component = ((Component)val).GetComponent<ZNetView>();
				if ((Object)(object)component != (Object)null && component.IsValid())
				{
					flag2 = component.GetZDO().GetBool("attachJoint", false);
				}
			}
			catch
			{
			}
			if (flag && flag2)
			{
				return "本地关节 m_attachJoin 非空 + ZDO 标志为真(真的连着)";
			}
			if (flag)
			{
				return "本地关节 m_attachJoin 非空(本机看得到实体关节 —— 真的连着)";
			}
			if (flag2)
			{
				return "只有 ZDO 标志为真、本机没有关节(别人在拖,或标志残留)";
			}
			return "两条子信号都是假 —— 但 IsAttached() 返回了 true(异常,请把这行发我)";
		}

		private static bool IsCart(Container c)
		{
			if ((Object)(object)c == (Object)null)
			{
				return false;
			}
			try
			{
				if ((Object)(object)c.m_wagon != (Object)null)
				{
					return true;
				}
			}
			catch
			{
			}
			try
			{
				return (Object)(object)CartRegistry.FindByContainer(c) != (Object)null;
			}
			catch
			{
			}
			return false;
		}

		private static bool HasAccess(Container c)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if (!EasyCraftPlugin.CfgRespectWards.Value)
			{
				return true;
			}
			try
			{
				return PrivateArea.CheckAccess(((Component)c).transform.position, 0f, false, true);
			}
			catch
			{
				return true;
			}
		}

		private static ZNetView ResolveNView(Container c)
		{
			if ((Object)(object)c == (Object)null)
			{
				return null;
			}
			try
			{
				if (F_ContainerNView != null)
				{
					object? value = F_ContainerNView.GetValue(c);
					ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
				}
			}
			catch
			{
			}
			try
			{
				ZNetView rootObjectOverride = c.m_rootObjectOverride;
				if ((Object)(object)rootObjectOverride != (Object)null)
				{
					return rootObjectOverride;
				}
			}
			catch
			{
			}
			try
			{
				return ((Component)c).GetComponent<ZNetView>();
			}
			catch
			{
			}
			return null;
		}

		private static bool EnsureOwner(Container c, out string reason)
		{
			reason = null;
			ZNetView val = ResolveNView(c);
			if ((Object)(object)val == (Object)null)
			{
				reason = "本物体与其根对象上都没有 ZNetView";
				return false;
			}
			if (!val.IsValid())
			{
				reason = "ZNetView 无效(物体未初始化)";
				return false;
			}
			if (val.IsOwner())
			{
				return true;
			}
			if (!EasyCraftPlugin.CfgClaimOwnership.Value)
			{
				reason = "所有权不在本机,且 ClaimOwnership 开关已关";
				if (EasyCraftPlugin.DebugOn)
				{
					EasyCraftPlugin.Log.LogWarning((object)("箱子 " + PrefabOfComponent((Component)(object)c) + " 的所有权不在本机,且已关闭自动接管,跳过。"));
				}
				return false;
			}
			try
			{
				val.ClaimOwnership();
			}
			catch (Exception ex)
			{
				reason = "ClaimOwnership 抛异常:" + ex.Message;
				return false;
			}
			return true;
		}

		private static bool VerifyOwnerAfterTake(Container c)
		{
			try
			{
				ZNetView val = ResolveNView(c);
				if ((Object)(object)val == (Object)null || !val.IsValid())
				{
					return false;
				}
				if (val.IsOwner())
				{
					return true;
				}
				return val.IsOwner();
			}
			catch
			{
				return false;
			}
		}

		private static void RefreshChestInventory(Container c, StringBuilder diag)
		{
			if ((Object)(object)c == (Object)null)
			{
				return;
			}
			try
			{
				if (M_ContainerLoad == null)
				{
					diag?.Append("(无法反射 Container.Load,跳过刷新)");
					return;
				}
				object obj = M_ContainerLoad.Invoke(c, null);
				diag?.Append((obj is bool && (bool)obj) ? "(已刷新库存)" : "(库存未变化)");
			}
			catch (Exception ex)
			{
				diag?.Append("(刷新库存失败:" + ex.Message + ")");
			}
		}

		private static MethodInfo FindPrefabNameMethod()
		{
			try
			{
				Type type = AccessTools.TypeByName("Utils");
				if (type == null)
				{
					return null;
				}
				return AccessTools.Method(type, "GetPrefabName", new Type[1] { typeof(GameObject) }, (Type[])null);
			}
			catch
			{
				return null;
			}
		}

		private static string PrefabOfComponent(Component c)
		{
			if ((Object)(object)c == (Object)null)
			{
				return null;
			}
			if (M_PrefabName != null)
			{
				try
				{
					return (string)M_PrefabName.Invoke(null, new object[1] { c.gameObject });
				}
				catch
				{
				}
			}
			string text = ((Object)c.gameObject).name;
			int num = text.IndexOf("(Clone)");
			if (num >= 0)
			{
				text = text.Substring(0, num);
			}
			return text.Trim();
		}

		private static string DescribeNames(Component c)
		{
			if ((Object)(object)c == (Object)null)
			{
				return "<null>";
			}
			string text = null;
			string text2 = null;
			string text3 = null;
			string text4 = null;
			try
			{
				text = ((Object)c.gameObject).name;
			}
			catch
			{
			}
			try
			{
				text = PrefabOfComponent(c);
			}
			catch
			{
			}
			try
			{
				text4 = EasyCraftPlugin.NormalizeContainerName(text);
			}
			catch
			{
			}
			try
			{
				Container val = (Container)(object)((c is Container) ? c : null);
				ZNetView val2 = (((Object)(object)val != (Object)null) ? ResolveNView(val) : c.GetComponent<ZNetView>());
				if ((Object)(object)val2 != (Object)null && val2.IsValid())
				{
					text2 = val2.GetZDO().GetString("prefab", (string)null) ?? "<按哈希存,无字符串>";
				}
			}
			catch
			{
			}
			try
			{
				Piece component = c.GetComponent<Piece>();
				if ((Object)(object)component != (Object)null)
				{
					text3 = component.m_name;
				}
			}
			catch
			{
			}
			string text5 = "否";
			try
			{
				if ((Object)(object)CartRegistry.FindByContainer((Container)(object)((c is Container) ? c : null)) != (Object)null)
				{
					text5 = "是(Vagon登记表)";
				}
				else
				{
					Container val3 = (Container)(object)((c is Container) ? c : null);
					if ((Object)(object)val3 != (Object)null && (Object)(object)val3.m_wagon != (Object)null)
					{
						text5 = "是(Container.m_wagon)";
					}
				}
			}
			catch
			{
			}
			return "GameObject[" + (text ?? "-") + "] 归一化[" + (text4 ?? "-") + "] 本地化键[" + (text3 ?? "-") + "] ZDO[" + (text2 ?? "-") + "] 板车[" + text5 + "]";
		}

		private static string PrefabOfItem(ItemData item)
		{
			if (item == null)
			{
				return null;
			}
			try
			{
				if ((Object)(object)item.m_dropPrefab != (Object)null)
				{
					return ((Object)item.m_dropPrefab).name;
				}
			}
			catch
			{
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(Container), "Awake")]
	internal static class Container_Awake_Patch
	{
		[HarmonyPostfix]
		private static void Postfix(Container __instance)
		{
			ContainerRegistry.Add(__instance);
		}
	}
	[HarmonyPatch(typeof(Container), "OnDestroyed")]
	internal static class Container_OnDestroyed_Patch
	{
		[HarmonyPrefix]
		private static void Prefix(Container __instance)
		{
			ContainerRegistry.Remove(__instance);
		}
	}
	[HarmonyPatch(typeof(Vagon), "Awake")]
	internal static class Vagon_Awake_Patch
	{
		[HarmonyPostfix]
		private static void Postfix(Vagon __instance)
		{
			CartRegistry.Add(__instance);
		}
	}
	[HarmonyPatch(typeof(Vagon), "OnDestroy")]
	internal static class Vagon_OnDestroy_Patch
	{
		[HarmonyPrefix]
		private static void Prefix(Vagon __instance)
		{
			CartRegistry.Remove(__instance);
		}
	}
	[HarmonyPatch(typeof(Smelter), "OnAddOre")]
	internal static class Smelter_OnAddOre_Patch
	{
		[HarmonyPrefix]
		private static bool Prefix(Smelter __instance, Humanoid user, ItemData item, ref bool __result)
		{
			if ((Object)(object)__instance == (Object)null || (Object)(object)user == (Object)null)
			{
				return true;
			}
			if (!EasyCraftPlugin.IsActive)
			{
				return true;
			}
			if (!(user is Player))
			{
				return true;
			}
			FillOutcome fillOutcome = Engine.FillOre(__instance, user, item);
			if (fillOutcome == FillOutcome.Fallback)
			{
				return true;
			}
			__result = fillOutcome == FillOutcome.Filled;
			return false;
		}
	}
	[HarmonyPatch(typeof(Smelter), "OnAddFuel")]
	internal static class Smelter_OnAddFuel_Patch
	{
		[HarmonyPrefix]
		private static bool Prefix(Smelter __instance, Humanoid user, ItemData item, ref bool __result)
		{
			if ((Object)(object)__instance == (Object)null || (Object)(object)user == (Object)null)
			{
				return true;
			}
			if (!EasyCraftPlugin.IsActive)
			{
				return true;
			}
			if (!(user is Player))
			{
				return true;
			}
			FillOutcome fillOutcome = Engine.FillFuel(__instance, user, item);
			if (fillOutcome == FillOutcome.Fallback)
			{
				return true;
			}
			__result = fillOutcome == FillOutcome.Filled;
			return false;
		}
	}
	[HarmonyPatch(typeof(Fireplace), "Interact")]
	internal static class Fireplace_Interact_Patch
	{
		[HarmonyPrefix]
		private static bool Prefix(Fireplace __instance, Humanoid user, bool hold, bool alt, ref bool __result)
		{
			if ((Object)(object)__instance == (Object)null || (Object)(object)user == (Object)null)
			{
				return true;
			}
			if (!EasyCraftPlugin.IsActive)
			{
				return true;
			}
			if (!(user is Player))
			{
				return true;
			}
			GameObject gameObject = ((Component)__instance).gameObject;
			FireSources.Probe(__instance);
			bool flag = FireSources.IsOneAtATime(gameObject);
			if (EasyCraftPlugin.CfgBatchSize.Value <= 1 && !flag)
			{
				FireSources.ReportBail(gameObject, "BatchSize <= 1");
				return true;
			}
			if (!EasyCraftPlugin.CfgFireEnabled.Value)
			{
				FireSources.ReportBail(gameObject, "FireSources.Enabled = false");
				return true;
			}
			if (!FireSources.IsAllowed(gameObject) && !flag)
			{
				FireSources.ReportBail(gameObject, "被 Exclude 排除,或不在 Sources 白名单里");
				return true;
			}
			if (!__instance.m_canRefill)
			{
				FireSources.ReportBail(gameObject, "m_canRefill = false(原版也不允许往里加燃料)");
				return true;
			}
			if (__instance.m_infiniteFuel)
			{
				FireSources.ReportBail(gameObject, "m_infiniteFuel = true(这个火不需要燃料)");
				return true;
			}
			if (EasyCraftPlugin.CfgFireRespectToggle.Value && __instance.m_canTurnOff && !hold && !alt && Engine.GetFireFuel(__instance) > 0f)
			{
				FireSources.ReportBail(gameObject, "点按 = 原版开/关火(m_canTurnOff=true 且燃料>0;要批量加料请把 RespectToggle 设成 false)");
				return true;
			}
			FillOutcome fillOutcome = Engine.FillFireplace(__instance, user, hold, flag ? 1 : 0);
			if (fillOutcome == FillOutcome.Fallback)
			{
				return true;
			}
			__result = fillOutcome == FillOutcome.Filled;
			return false;
		}
	}
	internal static class StationOreFilter
	{
		internal struct Option
		{
			internal string SharedName;

			internal string Prefab;
		}

		internal const string DefaultSpec = "charcoal_kiln=$item_wood";

		private static Dictionary<string, List<string>> _rules = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);

		private static readonly HashSet<string> Logged = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static string EffectiveSpec { get; private set; }

		internal static int StationCount => _rules.Count;

		internal static void Reload(string spec)
		{
			EffectiveSpec = spec ?? string.Empty;
			Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
			if (!string.IsNullOrEmpty(spec))
			{
				string[] array = spec.Split(new char[1] { ';' });
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text.Length == 0)
					{
						continue;
					}
					int num = text.IndexOf('=');
					if (num <= 0)
					{
						EasyCraftPlugin.Log.LogWarning((object)("站点进料限制:这一组缺少 '=',已忽略 → " + text));
						continue;
					}
					string text2 = text.Substring(0, num).Trim();
					string text3 = text.Substring(num + 1).Trim();
					if (text2.Length == 0)
					{
						continue;
					}
					List<string> list = new List<string>();
					if (text3.Length > 0)
					{
						string[] array2 = text3.Split(new char[1] { ',' });
						for (int j = 0; j < array2.Length; j++)
						{
							string text4 = array2[j].Trim();
							if (text4.Length > 0)
							{
								list.Add(text4);
							}
						}
					}
					dictionary[text2] = list;
				}
			}
			_rules = dictionary;
			Logged.Clear();
		}

		internal static string[] Apply(GameObject stationGo, List<Option> options)
		{
			List<string> list = new List<string>();
			if (options != null)
			{
				foreach (Option option in options)
				{
					if (!string.IsNullOrEmpty(option.SharedName) && !list.Contains(option.SharedName))
					{
						list.Add(option.SharedName);
					}
				}
			}
			List<string> list2 = FindRule(stationGo);
			if (list2 == null || list2.Count == 0 || list.Count == 0)
			{
				return list.ToArray();
			}
			List<string> list3 = new List<string>();
			foreach (Option option2 in options)
			{
				if (!string.IsNullOrEmpty(option2.SharedName) && !list3.Contains(option2.SharedName) && Matches(list2, option2))
				{
					list3.Add(option2.SharedName);
				}
			}
			string text = StationName(stationGo);
			if (list3.Count == 0)
			{
				if (Logged.Add(text + "|nomatch"))
				{
					EasyCraftPlugin.Log.LogWarning((object)string.Format("站点进料限制:{0} 的规则 [{1}] 与该站点可用材料对不上,本次**未做限制**(会按原版装填)。\n  该站点实际可用材料:{2}\n  请把上面任一个名字($item_xxx / item_xxx / 预制体名 都认)填进 1 - Batch Fill → StationOreAllow", text, string.Join(",", list2.ToArray()), string.Join(",", list.ToArray())));
				}
				return list.ToArray();
			}
			if (Logged.Add(text))
			{
				EasyCraftPlugin.Log.LogInfo((object)string.Format("站点进料限制生效:{0} 只允许装填 [{1}](该站点可用共 {2} 种:{3})", text, string.Join(",", list3.ToArray()), list.Count, string.Join(",", list.ToArray())));
			}
			return list3.ToArray();
		}

		private static List<string> FindRule(GameObject go)
		{
			if ((Object)(object)go == (Object)null || _rules.Count == 0)
			{
				return null;
			}
			Transform val = go.transform;
			int num = 0;
			while ((Object)(object)val != (Object)null && num++ < 8)
			{
				string text = FireSources.PrefabName(((Component)val).gameObject);
				if (!string.IsNullOrEmpty(text))
				{
					if (_rules.TryGetValue(text, out var value))
					{
						return value;
					}
					if (_rules.TryGetValue("piece_" + text, out value))
					{
						return value;
					}
					string text2 = Normalize(text);
					if (text2 != null)
					{
						foreach (KeyValuePair<string, List<string>> rule in _rules)
						{
							if (string.Equals(Normalize(rule.Key), text2, StringComparison.Ordinal))
							{
								return rule.Value;
							}
						}
					}
				}
				val = val.parent;
			}
			return null;
		}

		private static string Normalize(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return null;
			}
			string text = name.Trim().ToLowerInvariant();
			if (text.StartsWith("piece_", StringComparison.Ordinal))
			{
				text = text.Substring(6);
			}
			return text.Replace("_", string.Empty);
		}

		private static string StationName(GameObject go)
		{
			string text = FireSources.BestPrefabName(go);
			if (!string.IsNullOrEmpty(text))
			{
				return text;
			}
			return "?";
		}

		private static bool Matches(List<string> rule, Option o)
		{
			string text = o.SharedName ?? string.Empty;
			string b = (text.StartsWith("$", StringComparison.Ordinal) ? text.Substring(1) : text);
			string text2 = o.Prefab ?? string.Empty;
			foreach (string item in rule)
			{
				if (item.Length != 0)
				{
					string a = (item.StartsWith("$", StringComparison.Ordinal) ? item.Substring(1) : item);
					if (string.Equals(item, text, StringComparison.OrdinalIgnoreCase))
					{
						return true;
					}
					if (string.Equals(a, b, StringComparison.OrdinalIgnoreCase))
					{
						return true;
					}
					if (text2.Length > 0 && string.Equals(item, text2, StringComparison.OrdinalIgnoreCase))
					{
						return true;
					}
					if (text2.Length > 0 && string.Equals(a, text2, StringComparison.OrdinalIgnoreCase))
					{
						return true;
					}
				}
			}
			return false;
		}
	}
}