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 VoidChest v0.5.1
VoidChest.dll
Decompiled 5 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; 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 Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: AssemblyCompany("VoidChest")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.5.1.0")] [assembly: AssemblyInformationalVersion("0.5.1+7c4a9fd5f0844d92f39b3984aea7928fc9d4af1c")] [assembly: AssemblyProduct("VoidChest")] [assembly: AssemblyTitle("VoidChest")] [assembly: AssemblyVersion("0.5.1.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 VoidChest { internal static class ExtraSlotsCompat { private static bool _initialized; private static MethodInfo _getEquipped; private static bool _warned; private static bool _slotLookupInitialized; private static MethodInfo _getItemSlot; private static bool _slotLookupWarned; private static MethodInfo GetMethod() { if (_initialized) { return _getEquipped; } _initialized = true; Type type = AccessTools.TypeByName("ExtraSlots.ExtraUtilitySlots"); if (type == null) { VLog.Debug("未检测到 ExtraSlots。"); return null; } _getEquipped = AccessTools.Method(type, "GetEquippedItems", new Type[1] { typeof(Humanoid) }, (Type[])null); if (_getEquipped != null) { VLog.Info("检测到 ExtraSlots,额外 Utility 槽参与检测。"); } else { VLog.Warn("检测到 ExtraSlots 但未找到 ExtraUtilitySlots.GetEquippedItems,跳过额外槽检测。"); } return _getEquipped; } private static MethodInfo GetItemSlotMethod() { if (_slotLookupInitialized) { return _getItemSlot; } _slotLookupInitialized = true; Type type = AccessTools.TypeByName("ExtraSlots.Slots"); if (type == null) { return null; } _getItemSlot = AccessTools.Method(type, "GetItemSlot", new Type[1] { typeof(ItemData) }, (Type[])null); if (_getItemSlot == null) { VLog.Warn("未找到 ExtraSlots.Slots.GetItemSlot,专用槽位物品过滤降级。"); } return _getItemSlot; } internal static bool IsInExtraSlot(ItemData item) { if (item == null) { return false; } MethodInfo itemSlotMethod = GetItemSlotMethod(); if (itemSlotMethod == null) { return false; } try { return itemSlotMethod.Invoke(null, new object[1] { item }) != null; } catch (Exception ex) { if (!_slotLookupWarned) { _slotLookupWarned = true; VLog.Warn("ExtraSlots.GetItemSlot 调用失败,专用槽位过滤降级: " + ex.Message); } return false; } } internal static List<ItemData> GetEquippedItems(Humanoid humanoid) { MethodInfo method = GetMethod(); if (method == null || (Object)(object)humanoid == (Object)null) { return null; } try { return method.Invoke(null, new object[1] { humanoid }) as List<ItemData>; } catch (Exception ex) { if (!_warned) { _warned = true; VLog.Warn("ExtraSlots 检测失败,降级跳过额外槽: " + ex.Message); } return null; } } } [HarmonyPatch(typeof(Container), "Awake")] internal static class ContainerAwakePatch { private static bool Prefix(Container __instance) { if (__instance is VirtualContainer virtualContainer) { virtualContainer.InitVirtual(); return false; } return true; } } [HarmonyPatch(typeof(Container), "IsOwner")] internal static class ContainerIsOwnerPatch { private static bool Prefix(Container __instance, ref bool __result) { if (__instance is VirtualContainer) { __result = true; return false; } return true; } } [HarmonyPatch(typeof(Container), "SetInUse")] internal static class ContainerSetInUsePatch { private static bool Prefix(Container __instance) { return !(__instance is VirtualContainer); } } [HarmonyPatch(typeof(Container), "IsInUse")] internal static class ContainerIsInUsePatch { private static bool Prefix(Container __instance, ref bool __result) { if (__instance is VirtualContainer) { __result = false; return false; } return true; } } [HarmonyPatch(typeof(Container), "CheckForChanges")] internal static class ContainerCheckForChangesPatch { private static bool Prefix(Container __instance) { return !(__instance is VirtualContainer); } } [HarmonyPatch(typeof(Player), "Save")] internal static class PlayerSavePatch { private static void Prefix(Player __instance) { VoidChestManager.FlushActive(__instance); } } [HarmonyPatch(typeof(Inventory), "StackAll")] internal static class InventoryStackAllPatch { private static bool Prefix(Inventory __instance, Inventory fromInventory, ref int __result) { if (!VoidChestNearbyStore.FilterActive) { return true; } __result = VoidChestFilter.StackAllFiltered(__instance, fromInventory); return false; } } [HarmonyPatch(typeof(Container), "RPC_StackResponse")] internal static class ContainerRpcStackResponsePatch { private static void Postfix(long uid, bool granted) { VoidChestNearbyStore.OnStackResponse(granted); } } [HarmonyPatch(typeof(InventoryGui), "Show")] internal static class InventoryGuiShowPatch { private static void Postfix(Container container) { VoidChestUi.OnContainerShown(container); VoidChestDiagnostics.DumpRecipesAndInventory(); } } [HarmonyPatch] internal static class InventoryMoveItemToThisAmountPatch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(Inventory), "MoveItemToThis", new Type[5] { typeof(Inventory), typeof(ItemData), typeof(int), typeof(int), typeof(int) }, (Type[])null); } private static bool Prefix(Inventory __instance, ItemData item, ref int amount) { if (item == null || !VoidChestWeight.IsVirtualInventory(__instance)) { return true; } int num = VoidChestWeight.AllowedAmount(__instance, item, amount); if (num <= 0) { return false; } amount = num; return true; } } [HarmonyPatch] internal static class InventoryMoveItemToThisAllPatch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(Inventory), "MoveItemToThis", new Type[2] { typeof(Inventory), typeof(ItemData) }, (Type[])null); } private static bool Prefix(Inventory __instance, Inventory fromInventory, ItemData item) { if (item == null || fromInventory == null || !VoidChestWeight.IsVirtualInventory(__instance)) { return true; } int num = VoidChestWeight.AllowedAmount(__instance, item, item.m_stack); if (num <= 0) { return false; } if (num >= item.m_stack) { return true; } VoidChestWeight.MovePartialToContainer(__instance, fromInventory, item, num); return false; } } [HarmonyPatch(typeof(InventoryGui), "UpdateContainerWeight")] internal static class InventoryGuiContainerWeightPatch { private static void Postfix(InventoryGui __instance) { VirtualContainer currentContainer = VoidChestManager.CurrentContainer; if (!((Object)(object)currentContainer == (Object)null) && !((Object)(object)__instance == (Object)null) && !((Object)(object)VoidChestManager.CurrentOpenedContainer(__instance) != (Object)(object)currentContainer)) { Inventory inventory = ((Container)currentContainer).GetInventory(); if (inventory != null) { int num = Mathf.CeilToInt(inventory.GetTotalWeight()); __instance.m_containerWeight.text = ((currentContainer.MaxWeight > 0f) ? $"{num}/{Mathf.RoundToInt(currentContainer.MaxWeight)}" : num.ToString()); } } } } [BepInPlugin("trigger.valheim.voidchest", "Void Chest", "0.5.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class VoidChestPlugin : BaseUnityPlugin { public const string Guid = "trigger.valheim.voidchest"; public const string PluginName = "Void Chest"; public const string PluginVersion = "0.5.1"; internal static VoidChestPlugin Instance; internal static ManualLogSource Log; internal static ConfigEntry<KeyCode> OpenHotkey; internal static ConfigEntry<bool> DebugLog; internal static ConfigEntry<int> CapacityBlackMetalRows; internal static ConfigEntry<int> CapacityBlackMetalCols; internal static ConfigEntry<int> CapacityMagicRows; internal static ConfigEntry<int> CapacityMagicCols; internal static ConfigEntry<int> CapacityFlameRows; internal static ConfigEntry<int> CapacityFlameCols; internal static ConfigEntry<int> CapacityCrystalRows; internal static ConfigEntry<int> CapacityCrystalCols; internal static ConfigEntry<float> WeightBlackMetal; internal static ConfigEntry<float> WeightMagic; internal static ConfigEntry<float> WeightFlame; internal static ConfigEntry<float> WeightCrystal; internal static ConfigEntry<bool> EnableNearbyStore; internal static ConfigEntry<float> NearbyStoreRange; internal static ConfigEntry<bool> NearbyStoreCheckWard; internal static ConfigEntry<bool> NearbyStoreIgnoreHotbar; internal static ConfigEntry<bool> NearbyStoreIgnoreFood; internal static ConfigEntry<bool> NearbyStoreIgnoreAmmo; internal static ConfigEntry<bool> NearbyStoreIgnoreMead; internal static ConfigEntry<float> NearbyStoreTimeout; internal static ConfigEntry<bool> EnableRemoteStore; internal static ConfigEntry<bool> RemoteStoreAlwaysAvailable; internal static ConfigEntry<float> RemoteStoreCacheSeconds; private Harmony _harmony; private void Awake() { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Expected O, but got Unknown //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Expected O, but got Unknown //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Expected O, but got Unknown //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Expected O, but got Unknown //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Expected O, but got Unknown //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Expected O, but got Unknown //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Expected O, but got Unknown //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Expected O, but got Unknown //IL_026f: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Expected O, but got Unknown //IL_02ec: Unknown result type (might be due to invalid IL or missing references) //IL_02f6: Expected O, but got Unknown //IL_0349: Unknown result type (might be due to invalid IL or missing references) //IL_0353: Expected O, but got Unknown //IL_045b: Unknown result type (might be due to invalid IL or missing references) //IL_0465: Expected O, but got Unknown //IL_0510: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; OpenHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "OpenHotkey", (KeyCode)98, "打开/关闭虚空宝箱容器界面的热键。"); DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLog", true, "输出详细调试日志(诊断用,默认开启)。"); AcceptableValueRange<int> val = new AcceptableValueRange<int>(1, 8); CapacityBlackMetalRows = ((BaseUnityPlugin)this).Config.Bind<int>("Capacity", "BlackMetal.Rows", 2, new ConfigDescription("黑金属虚空宝箱:行数。", (AcceptableValueBase)(object)val, Array.Empty<object>())); CapacityBlackMetalCols = ((BaseUnityPlugin)this).Config.Bind<int>("Capacity", "BlackMetal.Cols", 6, new ConfigDescription("黑金属虚空宝箱:列数。", (AcceptableValueBase)(object)val, Array.Empty<object>())); CapacityMagicRows = ((BaseUnityPlugin)this).Config.Bind<int>("Capacity", "Magic.Rows", 3, new ConfigDescription("魔能虚空宝箱:行数。", (AcceptableValueBase)(object)val, Array.Empty<object>())); CapacityMagicCols = ((BaseUnityPlugin)this).Config.Bind<int>("Capacity", "Magic.Cols", 6, new ConfigDescription("魔能虚空宝箱:列数。", (AcceptableValueBase)(object)val, Array.Empty<object>())); CapacityFlameRows = ((BaseUnityPlugin)this).Config.Bind<int>("Capacity", "Flame.Rows", 3, new ConfigDescription("烈焰虚空宝箱:行数。", (AcceptableValueBase)(object)val, Array.Empty<object>())); CapacityFlameCols = ((BaseUnityPlugin)this).Config.Bind<int>("Capacity", "Flame.Cols", 8, new ConfigDescription("烈焰虚空宝箱:列数。", (AcceptableValueBase)(object)val, Array.Empty<object>())); CapacityCrystalRows = ((BaseUnityPlugin)this).Config.Bind<int>("Capacity", "Crystal.Rows", 4, new ConfigDescription("水晶虚空宝箱:行数。", (AcceptableValueBase)(object)val, Array.Empty<object>())); CapacityCrystalCols = ((BaseUnityPlugin)this).Config.Bind<int>("Capacity", "Crystal.Cols", 8, new ConfigDescription("水晶虚空宝箱:列数。", (AcceptableValueBase)(object)val, Array.Empty<object>())); AcceptableValueRange<float> val2 = new AcceptableValueRange<float>(0f, 9999f); WeightBlackMetal = ((BaseUnityPlugin)this).Config.Bind<float>("Weight", "BlackMetal.Max", 100f, new ConfigDescription("黑金属虚空宝箱:重量上限(0 = 无限制)。", (AcceptableValueBase)(object)val2, Array.Empty<object>())); WeightMagic = ((BaseUnityPlugin)this).Config.Bind<float>("Weight", "Magic.Max", 150f, new ConfigDescription("魔能虚空宝箱:重量上限(0 = 无限制)。", (AcceptableValueBase)(object)val2, Array.Empty<object>())); WeightFlame = ((BaseUnityPlugin)this).Config.Bind<float>("Weight", "Flame.Max", 300f, new ConfigDescription("烈焰虚空宝箱:重量上限(0 = 无限制)。", (AcceptableValueBase)(object)val2, Array.Empty<object>())); WeightCrystal = ((BaseUnityPlugin)this).Config.Bind<float>("Weight", "Crystal.Max", 800f, new ConfigDescription("水晶虚空宝箱:重量上限(0 = 无限制)。", (AcceptableValueBase)(object)val2, Array.Empty<object>())); EnableRemoteStore = ((BaseUnityPlugin)this).Config.Bind<bool>("RemoteStore", "Enabled", true, "启用虚空宝箱界面上的远程存入功能(守护石仓库)。仅单机/主机模式可用。"); RemoteStoreAlwaysAvailable = ((BaseUnityPlugin)this).Config.Bind<bool>("RemoteStore", "AlwaysAvailable", false, "远程存储是否一直可用。false = 需要装备魔能(L2)及以上宝箱才解锁;true = 初始即可用。"); RemoteStoreCacheSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("RemoteStore", "CacheSeconds", 300f, new ConfigDescription("远程存入的扫描结果缓存时间(秒)。0 = 每次重新扫描(慢)。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3600f), Array.Empty<object>())); EnableNearbyStore = ((BaseUnityPlugin)this).Config.Bind<bool>("NearbyStore", "Enabled", true, "启用附近存储功能(不影响原版与 V+ 的堆叠按钮行为)。"); NearbyStoreRange = ((BaseUnityPlugin)this).Config.Bind<float>("NearbyStore", "Range", 30f, new ConfigDescription("附近存储搜索半径(米)。范围 1-100,默认 30。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>())); NearbyStoreCheckWard = ((BaseUnityPlugin)this).Config.Bind<bool>("NearbyStore", "CheckWard", true, "跳过无权限的领地(守护石)内的容器。"); NearbyStoreIgnoreHotbar = ((BaseUnityPlugin)this).Config.Bind<bool>("NearbyStore", "IgnoreHotbar", true, "不存储物品栏第一排(快捷栏)的物品(通常放置装备)。"); NearbyStoreIgnoreFood = ((BaseUnityPlugin)this).Config.Bind<bool>("NearbyStore", "IgnoreFood", false, "不存储食物。"); NearbyStoreIgnoreAmmo = ((BaseUnityPlugin)this).Config.Bind<bool>("NearbyStore", "IgnoreAmmo", false, "不存储弹药。"); NearbyStoreIgnoreMead = ((BaseUnityPlugin)this).Config.Bind<bool>("NearbyStore", "IgnoreMead", false, "不存储蜜酒/药水。"); NearbyStoreTimeout = ((BaseUnityPlugin)this).Config.Bind<float>("NearbyStore", "TimeoutSeconds", 2f, "单个容器等待 RPC 响应超时(秒),超时后跳过。"); if (Mathf.Approximately(NearbyStoreRange.Value, 10f)) { NearbyStoreRange.Value = 30f; VLog.Info("附近存储范围已从旧默认 10 更新为 30(可在 ConfigurationManager 中调整)。"); } VLog.Info("Void Chest v0.5.1 初始化中..."); _harmony = new Harmony("trigger.valheim.voidchest"); _harmony.PatchAll(typeof(VoidChestPlugin).Assembly); foreach (MethodBase patchedMethod in _harmony.GetPatchedMethods()) { VLog.Info(" Harmony patch: " + patchedMethod.DeclaringType?.Name + "." + patchedMethod.Name); } VPlusGate.Install(_harmony); VoidChestLocalization.Register(); VoidChestItems.Register(); VLog.Info(string.Format("{0} v{1} 初始化完成。热键={2}, Debug={3}, 附近存储={4}, 远程存入={5}(Always={6})", "Void Chest", "0.5.1", OpenHotkey.Value, DebugLog.Value, EnableNearbyStore.Value, EnableRemoteStore.Value, RemoteStoreAlwaysAvailable.Value)); } private void Update() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) VoidChestNearbyStore.Update(); VoidChestRemoteStore.Update(); VoidChestRemoteNet.Update(); Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)InventoryGui.instance == (Object)null) && Input.GetKeyDown(OpenHotkey.Value)) { VoidChestManager.Toggle(localPlayer); } } private void OnDestroy() { if (_harmony != null) { _harmony.UnpatchSelf(); } } } public class VirtualContainer : Container { private static readonly FieldRef<Container, Inventory> InventoryRef = AccessTools.FieldRefAccess<Container, Inventory>("m_inventory"); internal bool SuppressSave; internal float MaxWeight; internal void InitVirtual() { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown SetInventory(new Inventory("Void Chest", (Sprite)null, 6, 2)); VLog.Debug("VirtualContainer.InitVirtual: 占位 Inventory 6x2 已创建。"); } internal void SetInventory(Inventory inv) { Inventory inventory = ((Container)this).GetInventory(); if (inventory != null) { inventory.m_onChanged = (Action)Delegate.Remove(inventory.m_onChanged, new Action(OnInventoryChanged)); } InventoryRef.Invoke((Container)(object)this) = inv; if (inv != null) { inv.m_onChanged = (Action)Delegate.Combine(inv.m_onChanged, new Action(OnInventoryChanged)); } VLog.Debug("VirtualContainer.SetInventory: " + ((inv != null) ? (inv.GetWidth() + "x" + inv.GetHeight()) : "null")); } private void OnInventoryChanged() { if (SuppressSave) { VLog.Debug("OnInventoryChanged: SuppressSave=true,跳过保存。"); return; } Player localPlayer = Player.m_localPlayer; Inventory inventory = ((Container)this).GetInventory(); if ((Object)(object)localPlayer != (Object)null && inventory != null) { VLog.Debug($"OnInventoryChanged: 库存变更 -> {inventory.NrOfItems()} 件,保存。"); VoidChestSave.SaveFrom(inventory, localPlayer); } } } internal static class VLog { internal static bool DebugEnabled { get { if (VoidChestPlugin.DebugLog != null) { return VoidChestPlugin.DebugLog.Value; } return true; } } internal static void Debug(string message) { if (DebugEnabled) { VoidChestPlugin.Log.LogInfo((object)("[Debug] " + message)); } } internal static void Info(string message) { VoidChestPlugin.Log.LogInfo((object)message); } internal static void Warn(string message) { VoidChestPlugin.Log.LogWarning((object)message); } internal static void Error(string message, Exception e = null) { VoidChestPlugin.Log.LogError((object)((e == null) ? message : (message + Environment.NewLine + e))); } } internal static class VoidChestDiagnostics { private static readonly FieldInfo KnownRecipesField = AccessTools.Field(typeof(Player), "m_knownRecipes"); private static readonly FieldInfo KnownStationsField = AccessTools.Field(typeof(Player), "m_knownStations"); private static readonly FieldInfo KnownMaterialField = AccessTools.Field(typeof(Player), "m_knownMaterial"); private static float _lastDump = -999f; internal static void DumpRecipesAndInventory() { if (VoidChestPlugin.DebugLog == null || !VoidChestPlugin.DebugLog.Value || Time.realtimeSinceStartup - _lastDump < 2f) { return; } _lastDump = Time.realtimeSinceStartup; try { Dump(); } catch (Exception ex) { VLog.Warn("配方诊断失败: " + ex.Message); } } private static void Dump() { Player localPlayer = Player.m_localPlayer; ObjectDB instance = ObjectDB.instance; if ((Object)(object)localPlayer == (Object)null || (Object)(object)instance == (Object)null) { return; } HashSet<string> hashSet = (KnownRecipesField?.GetValue(localPlayer) as HashSet<string>) ?? new HashSet<string>(); Dictionary<string, int> dictionary = (KnownStationsField?.GetValue(localPlayer) as Dictionary<string, int>) ?? new Dictionary<string, int>(); HashSet<string> hashSet2 = (KnownMaterialField?.GetValue(localPlayer) as HashSet<string>) ?? new HashSet<string>(); Inventory inventory = ((Humanoid)localPlayer).GetInventory(); StringBuilder stringBuilder = new StringBuilder(2048); stringBuilder.AppendLine("========== 虚空宝箱配方诊断 =========="); foreach (Recipe recipe in instance.m_recipes) { if ((Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null) { continue; } string name = ((Object)recipe.m_item).name; if (!name.StartsWith("VoidChest")) { continue; } ItemDrop component = ((Component)recipe.m_item).GetComponent<ItemDrop>(); string text = (((Object)(object)component != (Object)null) ? component.m_itemData.m_shared.m_name : "?"); string text2 = Localization.instance.Localize(text); bool flag = hashSet.Contains(text); string text3 = (((Object)(object)recipe.m_craftingStation != (Object)null) ? ((Object)recipe.m_craftingStation).name : "(无)"); string text4 = (((Object)(object)recipe.m_craftingStation != (Object)null) ? Localization.instance.Localize(recipe.m_craftingStation.m_name) : "(无)"); int value = -1; if ((Object)(object)recipe.m_craftingStation != (Object)null) { dictionary.TryGetValue(recipe.m_craftingStation.m_name, out value); } string text5 = ((value < 0) ? "未记录" : value.ToString()); stringBuilder.AppendLine("[" + name + "] 成品=" + text2 + "(" + text + ")"); stringBuilder.AppendLine($" 解锁={flag} | 工作台={text4}({text3}) | 需要等级={recipe.m_minStationLevel} | 玩家已知该工作台等级={text5} | 只需一种材料={recipe.m_requireOnlyOneIngredient}"); List<string> list = new List<string>(); if (recipe.m_resources != null) { Requirement[] resources = recipe.m_resources; foreach (Requirement val in resources) { if (val == null || (Object)(object)val.m_resItem == (Object)null) { stringBuilder.AppendLine(" 材料: <null 引用>"); continue; } string name2 = val.m_resItem.m_itemData.m_shared.m_name; string text6 = Localization.instance.Localize(name2); int num = inventory.CountItems(name2, -1, true); bool flag2 = hashSet2.Contains(name2); stringBuilder.AppendLine($" 材料: {((Object)val.m_resItem).name}({text6}) 需要={val.m_amount} 背包数量={num} 已见过={flag2}"); if (!flag2) { list.Add(text6 + "/" + ((Object)val.m_resItem).name); } } } List<string> list2 = new List<string>(); if (!flag) { if (list.Count > 0) { list2.Add("材料未见过: " + string.Join(", ", list)); } if ((Object)(object)recipe.m_craftingStation != (Object)null && value < recipe.m_minStationLevel) { list2.Add($"工作台等级不足/未知(玩家={text5},需要={recipe.m_minStationLevel})"); } } stringBuilder.AppendLine(flag ? " 状态: 已解锁" : (" 未解锁原因: " + ((list2.Count > 0) ? string.Join("; ", list2) : "未知(其他条件)"))); } List<ItemData> allItems = inventory.GetAllItems(); stringBuilder.AppendLine($"---- 玩家背包物品({allItems.Count})----"); foreach (ItemData item in allItems) { if (item != null) { string arg = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : "(无prefab)"); stringBuilder.AppendLine($" {arg} | {Localization.instance.Localize(item.m_shared.m_name)} | x{item.m_stack}"); } } stringBuilder.AppendLine($"---- 玩家已记录工作台({dictionary.Count})----"); foreach (KeyValuePair<string, int> item2 in dictionary) { stringBuilder.AppendLine($" {Localization.instance.Localize(item2.Key)} = {item2.Value}"); } stringBuilder.Append("======================================="); VoidChestPlugin.Log.LogInfo((object)stringBuilder.ToString()); } } internal static class VoidChestFilter { internal static bool ShouldSkip(ItemData item) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Invalid comparison between Unknown and I4 //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Invalid comparison between Unknown and I4 //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Invalid comparison between Unknown and I4 if (VoidChestPlugin.NearbyStoreIgnoreHotbar.Value && item.m_gridPos.y == 0) { VLog.Debug("存储过滤:跳过快捷栏物品 " + item.m_shared.m_name); return true; } if (ExtraSlotsCompat.IsInExtraSlot(item)) { VLog.Debug("存储过滤:跳过额外槽位物品 " + item.m_shared.m_name); return true; } SharedData shared = item.m_shared; ItemType itemType = shared.m_itemType; if (VoidChestPlugin.NearbyStoreIgnoreAmmo.Value && ((int)itemType == 9 || (int)itemType == 23)) { return true; } if ((int)itemType == 2) { bool flag = shared.m_food > 0f; if (flag && VoidChestPlugin.NearbyStoreIgnoreFood.Value) { return true; } if (!flag && VoidChestPlugin.NearbyStoreIgnoreMead.Value) { return true; } } return false; } internal static int StackAllRaw(Inventory container, Inventory from) { Stopwatch stopwatch = Stopwatch.StartNew(); List<ItemData> list = new List<ItemData>(from.GetAllItems()); int num = 0; foreach (ItemData item in list) { if (item != null && item.m_shared != null && container.ContainsItemByName(item.m_shared.m_name) && container.AddItem(item)) { from.RemoveItem(item); num++; } } stopwatch.Stop(); VoidChestPerf.AddStack(stopwatch.Elapsed.TotalMilliseconds); VLog.Debug($"StackAllRaw: {container.GetName()} 移动 {num} 堆叠,耗时 {stopwatch.Elapsed.TotalMilliseconds:F2}ms"); return num; } internal static int StackAllFiltered(Inventory container, Inventory from) { Stopwatch stopwatch = Stopwatch.StartNew(); List<ItemData> list = new List<ItemData>(from.GetAllItems()); int num = 0; Player localPlayer = Player.m_localPlayer; foreach (ItemData item in list) { if (item != null && container.ContainsItemByName(item.m_shared.m_name) && (!((Object)(object)localPlayer != (Object)null) || !((Humanoid)localPlayer).IsItemEquiped(item)) && !ShouldSkip(item) && container.AddItem(item)) { from.RemoveItem(item); num++; } } stopwatch.Stop(); VoidChestPerf.AddStack(stopwatch.Elapsed.TotalMilliseconds); VLog.Debug($"StackAllFiltered: {container.GetName()} 移动 {num} 堆叠,耗时 {stopwatch.Elapsed.TotalMilliseconds:F2}ms"); return num; } } internal static class VoidChestItemIdentity { internal static string Of(ItemData item) { StringBuilder stringBuilder = new StringBuilder(96); stringBuilder.Append(((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : "?").Append('|'); stringBuilder.Append(item.m_quality).Append('|'); stringBuilder.Append(item.m_variant).Append('|'); stringBuilder.Append(item.m_worldLevel).Append('|'); stringBuilder.Append(Mathf.RoundToInt(item.m_durability * 100f)).Append('|'); stringBuilder.Append(item.m_crafterID).Append('|'); stringBuilder.Append(item.m_crafterName).Append('|'); stringBuilder.Append(item.m_pickedUp ? '1' : '0').Append('|'); stringBuilder.Append(item.m_cheated ? '1' : '0'); Dictionary<string, string> customData = item.m_customData; if (customData != null && customData.Count > 0) { List<string> list = new List<string>(customData.Keys); list.Sort(StringComparer.Ordinal); foreach (string item2 in list) { string text = customData[item2] ?? string.Empty; stringBuilder.Append('|').Append(item2.Length).Append(':') .Append(item2) .Append('=') .Append(text.Length) .Append(':') .Append(text); } } return stringBuilder.ToString(); } internal static Dictionary<string, int> Totals(Inventory inv) { Dictionary<string, int> dictionary = new Dictionary<string, int>(); if (inv == null) { return dictionary; } foreach (ItemData allItem in inv.GetAllItems()) { if (allItem != null && allItem.m_shared != null) { string key = Of(allItem); dictionary.TryGetValue(key, out var value); dictionary[key] = value + allItem.m_stack; } } return dictionary; } } internal static class VoidChestItems { internal const string BlackMetal = "VoidChestBlackMetal"; internal const string Magic = "VoidChestMagic"; internal const string Flame = "VoidChestFlame"; internal const string Crystal = "VoidChestCrystal"; private const string BasePrefab = "chest_hildir2"; private static bool _registered; internal static readonly Dictionary<string, Color> Tints = new Dictionary<string, Color> { { "VoidChestBlackMetal", Color32.op_Implicit(new Color32((byte)208, (byte)120, byte.MaxValue, byte.MaxValue)) }, { "VoidChestMagic", Color32.op_Implicit(new Color32((byte)24, (byte)231, (byte)169, byte.MaxValue)) }, { "VoidChestFlame", Color32.op_Implicit(new Color32(byte.MaxValue, (byte)172, (byte)89, byte.MaxValue)) }, { "VoidChestCrystal", Color32.op_Implicit(new Color32(byte.MaxValue, (byte)69, (byte)69, byte.MaxValue)) } }; internal static void Register() { PrefabManager.OnVanillaPrefabsAvailable += RegisterItems; VLog.Info("已订阅 PrefabManager.OnVanillaPrefabsAvailable"); } private static void RegisterItems() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Expected O, but got Unknown //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Expected O, but got Unknown //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Expected O, but got Unknown //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Expected O, but got Unknown //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Expected O, but got Unknown //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Expected O, but got Unknown if (_registered) { VLog.Debug("物品已注册过,跳过。"); return; } VLog.Info("原版 prefab 可用,开始注册虚空宝箱..."); try { Add("VoidChestBlackMetal", "$item_voidchest_blackmetal", "$item_voidchest_blackmetal_desc", "forge", (RequirementConfig[])(object)new RequirementConfig[3] { new RequirementConfig("DragonTear", 5, 0, true), new RequirementConfig("FineWood", 10, 0, true), new RequirementConfig("BlackMetal", 20, 0, true) }); Add("VoidChestMagic", "$item_voidchest_magic", "$item_voidchest_magic_desc", "forge", (RequirementConfig[])(object)new RequirementConfig[3] { new RequirementConfig("VoidChestBlackMetal", 1, 0, true), new RequirementConfig("Eitr", 10, 0, true), new RequirementConfig("YagluthDrop", 2, 0, true) }); Add("VoidChestFlame", "$item_voidchest_flame", "$item_voidchest_flame_desc", "blackforge", (RequirementConfig[])(object)new RequirementConfig[3] { new RequirementConfig("VoidChestMagic", 1, 0, true), new RequirementConfig("FlametalNew", 10, 0, true), new RequirementConfig("FaderDrop", 2, 0, true) }); Add("VoidChestCrystal", "$item_voidchest_crystal", "$item_voidchest_crystal_desc", "blackforge", (RequirementConfig[])(object)new RequirementConfig[3] { new RequirementConfig("VoidChestFlame", 1, 0, true), new RequirementConfig("Gold", 10, 0, true), new RequirementConfig("FrozenFuel", 10, 0, true) }); _registered = true; VLog.Info("虚空宝箱物品与配方注册完成。"); } catch (Exception e) { VLog.Error("注册虚空宝箱失败: ", e); } } private static void Add(string prefabName, string displayName, string description, string station, RequirementConfig[] requirements) { //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Expected O, but got Unknown //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Expected O, but got Unknown List<string> list = new List<string>(); foreach (RequirementConfig val in requirements) { list.Add($"{val.Item}x{val.Amount}"); } VLog.Info("注册 [" + prefabName + "] 名称=" + displayName + " 工作台=" + station + " 材料=" + string.Join(", ", list)); ItemConfig val2 = new ItemConfig { Name = prefabName, Description = prefabName, CraftingStation = station, MinStationLevel = 1, Amount = 1, Requirements = requirements, Weight = 2f }; CustomItem val3 = new CustomItem(prefabName, "chest_hildir2", val2); if ((Object)(object)val3.ItemPrefab == (Object)null) { VLog.Error("[" + prefabName + "] CustomItem.ItemPrefab 为空(克隆 chest_hildir2 失败)"); } Sanitize(val3, prefabName, displayName, description); ItemManager.Instance.AddItem(val3); CustomRecipe recipe = val3.Recipe; if (recipe == null) { VLog.Warn("[" + prefabName + "] 配方未生成(Recipe 为空)。"); return; } object arg = (recipe.Recipe?.m_resources?.Length).GetValueOrDefault(); Recipe recipe2 = recipe.Recipe; object arg2; if (recipe2 == null) { arg2 = null; } else { CraftingStation craftingStation = recipe2.m_craftingStation; arg2 = ((craftingStation != null) ? ((Object)craftingStation).name : null); } VLog.Info($"[{prefabName}] 配方已生成: {arg} 项材料, 工作台={arg2}"); } private static void Sanitize(CustomItem item, string prefabName, string displayName, string description) { //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) GameObject itemPrefab = item.ItemPrefab; if ((Object)(object)itemPrefab == (Object)null) { VLog.Error("[" + prefabName + "] 克隆失败:prefab 为空。"); return; } Component[] componentsInChildren = itemPrefab.GetComponentsInChildren<Component>(true); List<string> list = new List<string>(); Component[] array = componentsInChildren; foreach (Component val in array) { if ((Object)(object)val != (Object)null) { list.Add(((object)val).GetType().Name); } } VLog.Info("[" + prefabName + "] 组件结构: " + string.Join(", ", list)); array = componentsInChildren; foreach (Component val2 in array) { if (!((Object)(object)val2 == (Object)null) && (val2 is Container || val2 is Piece || val2 is WearNTear || val2 is Destructible || val2 is PrivateArea)) { VLog.Info("[" + prefabName + "] 移除组件: " + ((object)val2).GetType().Name); Object.DestroyImmediate((Object)(object)val2); } } ItemDrop itemDrop = item.ItemDrop; if ((Object)(object)itemDrop == (Object)null) { VLog.Error("[" + prefabName + "] 没有 ItemDrop 组件,无法作为物品。"); return; } SharedData shared = itemDrop.m_itemData.m_shared; shared.m_itemType = (ItemType)18; shared.m_maxStackSize = 1; shared.m_weight = 2f; shared.m_teleportable = true; shared.m_questItem = false; shared.m_name = displayName; shared.m_description = description; object[] obj = new object[5] { prefabName, shared.m_name, shared.m_itemType, shared.m_weight, null }; Sprite[] icons = shared.m_icons; obj[4] = ((icons != null) ? icons.Length : 0); VLog.Info(string.Format("[{0}] ItemDrop: name={1}, type={2}, weight={3}, icons={4}", obj)); Color val3 = Tints[prefabName]; if (shared.m_icons != null && shared.m_icons.Length != 0) { for (int j = 0; j < shared.m_icons.Length; j++) { Sprite val4 = shared.m_icons[j]; Sprite val5 = RecolorSprite(val4, val3); shared.m_icons[j] = val5; VLog.Info($"[{prefabName}] 图标[{j}] {SpriteInfo(val4)} -> {SpriteInfo(val5)}"); } } else { VLog.Warn("[" + prefabName + "] 没有图标(m_icons 为空)。"); } int num = RecolorRenderers(itemPrefab, val3); VLog.Info($"[{prefabName}] 材质换色: {num} 个材质设置 tint={ColorUtility.ToHtmlStringRGB(val3)}"); } private static string SpriteInfo(Sprite sprite) { //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 ((Object)(object)sprite == (Object)null) { return "null"; } Texture2D texture = sprite.texture; Rect textureRect = sprite.textureRect; return string.Format("{0}/rect({1},{2},{3},{4})/ppu{5}", ((Object)(object)texture != (Object)null) ? (((Texture)texture).width + "x" + ((Texture)texture).height) : "no-tex", ((Rect)(ref textureRect)).x, ((Rect)(ref textureRect)).y, ((Rect)(ref textureRect)).width, ((Rect)(ref textureRect)).height, sprite.pixelsPerUnit); } private static Sprite RecolorSprite(Sprite src, Color tint) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)src == (Object)null) { return null; } Texture2D texture = src.texture; if ((Object)(object)texture == (Object)null) { return src; } Rect textureRect = src.textureRect; int num = Mathf.Max(1, Mathf.RoundToInt(((Rect)(ref textureRect)).width)); int num2 = Mathf.Max(1, Mathf.RoundToInt(((Rect)(ref textureRect)).height)); int num3 = Mathf.Max(1, ((Texture)texture).width); int num4 = Mathf.Max(1, ((Texture)texture).height); RenderTexture temporary = RenderTexture.GetTemporary(num3, num4, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)2); RenderTexture active = RenderTexture.active; try { Graphics.Blit((Texture)(object)texture, temporary); RenderTexture.active = temporary; Texture2D val = new Texture2D(num, num2, (TextureFormat)4, false); val.ReadPixels(new Rect(((Rect)(ref textureRect)).x, ((Rect)(ref textureRect)).y, (float)num, (float)num2), 0, 0); val.Apply(); Color32[] pixels = val.GetPixels32(); for (int i = 0; i < pixels.Length; i++) { Color32 val2 = pixels[i]; if (val2.a != 0) { float num5 = (float)Mathf.Max((int)val2.r, Mathf.Max((int)val2.g, (int)val2.b)) / 255f; num5 = Mathf.Clamp01(0.2f + 0.8f * num5); pixels[i] = new Color32((byte)(tint.r * 255f * num5), (byte)(tint.g * 255f * num5), (byte)(tint.b * 255f * num5), val2.a); } } val.SetPixels32(pixels); val.Apply(); return Sprite.Create(val, new Rect(0f, 0f, (float)num, (float)num2), new Vector2(0.5f, 0.5f), src.pixelsPerUnit, 0u, (SpriteMeshType)0); } finally { RenderTexture.active = active; RenderTexture.ReleaseTemporary(temporary); } } private static int RecolorRenderers(GameObject root, Color tint) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) int num = 0; Renderer[] componentsInChildren = root.GetComponentsInChildren<Renderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Material[] materials = componentsInChildren[i].materials; foreach (Material val in materials) { if (!((Object)(object)val == (Object)null)) { if (val.HasProperty("_Color")) { val.SetColor("_Color", tint); num++; } if (val.HasProperty("_EmissionColor")) { val.SetColor("_EmissionColor", tint * 0.35f); } } } } return num; } } internal static class VoidChestLocalization { internal const string ItemBlackMetal = "$item_voidchest_blackmetal"; internal const string ItemBlackMetalDesc = "$item_voidchest_blackmetal_desc"; internal const string ItemMagic = "$item_voidchest_magic"; internal const string ItemMagicDesc = "$item_voidchest_magic_desc"; internal const string ItemFlame = "$item_voidchest_flame"; internal const string ItemFlameDesc = "$item_voidchest_flame_desc"; internal const string ItemCrystal = "$item_voidchest_crystal"; internal const string ItemCrystalDesc = "$item_voidchest_crystal_desc"; internal const string StoreNearby = "$vc_store_nearby"; internal const string StoreRemote = "$vc_store_remote"; internal const string NearbyInProgress = "$vc_nearby_in_progress"; internal const string NearbyNone = "$vc_nearby_none"; internal const string NearbyStored = "$vc_nearby_stored"; internal const string NearbyNothing = "$vc_nearby_nothing"; internal const string NearbyRejected = "$vc_nearby_rejected"; internal const string NearbyTimeout = "$vc_nearby_timeout"; internal const string RemoteInProgress = "$vc_remote_in_progress"; internal const string RemoteRequested = "$vc_remote_requested"; internal const string RemoteServerMissing = "$vc_remote_server_missing"; internal const string RemoteDisabled = "$vc_remote_disabled"; internal const string RemoteNoGuardstone = "$vc_remote_no_guardstone"; internal const string RemoteNoAccess = "$vc_remote_no_access"; internal const string RemoteStored = "$vc_remote_stored"; internal const string RemoteNothing = "$vc_remote_nothing"; internal const string RemoteSkipped = "$vc_remote_skipped"; private static readonly (string, string, string, string)[] Translations = new(string, string, string, string)[25] { ("$item_voidchest_blackmetal", "Black Metal Void Chest", "黑金属虚空宝箱", "黑金屬虛空寶箱"), ("$item_voidchest_blackmetal_desc", "A personal void container bound to your character - its contents follow you across worlds and servers. Equip it and press the hotkey to open. Supports nearby storage and guard-stone remote deposit. Capacity: 2x6 (configurable).", "角色绑定的个人虚空容器——匣中之物跨世界、跨服务器随身携带。装备后按热键打开;支持附近存储与守护石远程存入。容量:2×6(可配置)。", "角色綁定的個人虛空容器——匣中之物跨世界、跨伺服器隨身攜帶。裝備後按熱鍵打開;支援附近存儲與守護石遠端存入。容量:2×6(可配置)。"), ("$item_voidchest_magic", "Eitr Void Chest", "魔能虚空宝箱", "魔能虛空寶箱"), ("$item_voidchest_magic_desc", "A void chest infused with refined eitr - same character-bound storage, larger space. Equip it and press the hotkey to open. Supports nearby storage and guard-stone remote deposit. Capacity: 3x6 (configurable).", "铸入埃达精华的虚空宝箱——同样角色绑定,容量更大。装备后按热键打开;支持附近存储与守护石远程存入。容量:3×6(可配置)。", "鑄入埃達精華的虛空寶箱——同樣角色綁定,容量更大。裝備後按熱鍵打開;支援附近存儲與守護石遠端存入。容量:3×6(可配置)。"), ("$item_voidchest_flame", "Flametal Void Chest", "烈焰虚空宝箱", "烈焰虛空寶箱"), ("$item_voidchest_flame_desc", "A void chest tempered by the Emerald Flame - same character-bound storage, even larger space. Equip it and press the hotkey to open. Supports nearby storage and guard-stone remote deposit. Capacity: 3x8 (configurable).", "经青焰淬炼的虚空宝箱——同样角色绑定,容量更大。装备后按热键打开;支持附近存储与守护石远程存入。容量:3×8(可配置)。", "經青焰淬鍊的虛空寶箱——同樣角色綁定,容量更大。裝備後按熱鍵打開;支援附近存儲與守護石遠端存入。容量:3×8(可配置)。"), ("$item_voidchest_crystal", "Crystal Void Chest", "水晶虚空宝箱", "水晶虛空寶箱"), ("$item_voidchest_crystal_desc", "The final void chest, born of blood-gold and liquid frost - the largest character-bound storage. Equip it and press the hotkey to open. Supports nearby storage and guard-stone remote deposit. Capacity: 4x8 (configurable).", "霜与血的造物、终极虚空宝箱——角色绑定的最大容量。装备后按热键打开;支持附近存储与守护石远程存入。容量:4×8(可配置)。", "霜與血的造物、終極虛空寶箱——角色綁定的最大容量。裝備後按熱鍵打開;支援附近存儲與守護石遠端存入。容量:4×8(可配置)。"), ("$vc_store_nearby", "Nearby Storage", "附近存储", "附近存儲"), ("$vc_store_remote", "Remote Deposit", "远程存入", "遠端存入"), ("$vc_nearby_in_progress", "Nearby storage is in progress, please try again later.", "附近存储正在进行中,请稍后再试。", "附近儲存正在進行中,請稍後再試。"), ("$vc_nearby_none", "No containers nearby to store into.", "附近没有可存储的箱子。", "附近沒有可儲存的箱子。"), ("$vc_nearby_stored", "Stored {0} items into {1} containers", "已存入 {0} 件物品到 {1} 个箱子", "已存入 {0} 件物品到 {1} 個箱子"), ("$vc_nearby_nothing", "Nothing to store (scanned {0} containers)", "没有可存入的物品(扫描 {0} 个箱子)", "沒有可存入的物品(掃描 {0} 個箱子)"), ("$vc_nearby_rejected", "({0} in use / rejected)", "({0} 个使用中/被拒绝)", "({0} 個使用中/被拒絕)"), ("$vc_nearby_timeout", "({0} timed out)", "({0} 个超时)", "({0} 個超時)"), ("$vc_remote_in_progress", "Remote deposit is already in progress...", "远程存入正在进行中...", "遠端存入正在進行中..."), ("$vc_remote_requested", "Remote deposit request sent, waiting for the server...", "已向服务器发送远程存入请求,正在等待处理...", "已向伺服器發送遠端存入請求,正在等待處理..."), ("$vc_remote_server_missing", "No response from the server: remote deposit requires the mod on the server.", "服务器未响应:远程存入需要服务端也安装本 mod。", "伺服器未回應:遠端存入需要伺服器也安裝本 mod。"), ("$vc_remote_disabled", "Remote deposit is disabled on the server.", "服务器已禁用远程存入。", "伺服器已停用遠端存入。"), ("$vc_remote_no_guardstone", "No guard stone found in the world.", "世界中未找到守护石。", "世界中未找到守護石。"), ("$vc_remote_no_access", "You have access to no guard stone.", "没有找到你有权限的守护石", "沒有找到你有權限的守護石"), ("$vc_remote_stored", "Deposited {0} items into your home containers ({1} containers)", "已远程存入 {0} 件物品到家的箱子({1} 个箱子)", "已遠端存入 {0} 件物品到家的箱子({1} 個箱子)"), ("$vc_remote_nothing", "Nothing to deposit (scanned {0} containers)", "没有可远程存入的物品(扫描 {0} 个箱子)", "沒有可遠端存入的物品(掃描 {0} 個箱子)"), ("$vc_remote_skipped", "({0} in use / invalid skipped)", "(跳过 {0} 个使用中/无效箱子)", "(跳過 {0} 個使用中/無效箱子)") }; internal static void Register() { try { CustomLocalization localization = LocalizationManager.Instance.GetLocalization(); (string, string, string, string)[] translations = Translations; for (int i = 0; i < translations.Length; i++) { (string, string, string, string) tuple = translations[i]; string text = "English"; localization.AddTranslation(ref text, ref tuple.Item1, tuple.Item2); text = "Chinese"; localization.AddTranslation(ref text, ref tuple.Item1, tuple.Item3); text = "Chinese_Trad"; localization.AddTranslation(ref text, ref tuple.Item1, tuple.Item4); } VLog.Info($"本地化已注册:{Translations.Length} 个条目 × 3 种语言(English/Chinese/Chinese_Trad)。"); } catch (Exception e) { VLog.Error("本地化注册失败: ", e); } } internal static string L(string token) { Localization instance = Localization.instance; if (instance == null) { return token; } return instance.Localize(token); } internal static string L(string token, params object[] args) { string text = L(token); if (args == null) { return text; } for (int i = 0; i < args.Length; i++) { text = text.Replace("{" + i + "}", (args[i] != null) ? args[i].ToString() : ""); } return text; } } internal static class VoidChestManager { internal const string PrefabPrefix = "VoidChest"; internal static readonly FieldRef<InventoryGui, Container> CurrentContainerRef = AccessTools.FieldRefAccess<InventoryGui, Container>("m_currentContainer"); private static VirtualContainer _container; private static ItemData _openItem; internal static VirtualContainer CurrentContainer => _container; internal static Container CurrentOpenedContainer(InventoryGui gui) { if (!((Object)(object)gui != (Object)null)) { return null; } return CurrentContainerRef.Invoke(gui); } internal static void Toggle(Player player) { if ((Object)(object)InventoryGui.instance == (Object)null) { return; } bool flag = InventoryGui.instance.IsContainerOpen(); bool flag2 = flag && IsVoidChestOpen(); VLog.Debug($"Toggle: containerOpen={flag}, isOurContainer={flag2}"); if (flag) { if (flag2) { InventoryGui.instance.Hide(); VLog.Info("关闭虚空宝箱界面。"); } } else { ItemData equippedChest = GetEquippedChest(player); if (equippedChest == null) { VLog.Debug("未装备虚空宝箱,无法打开。"); } else { Open(player, equippedChest); } } } internal static bool IsVoidChestOpen() { if ((Object)(object)InventoryGui.instance != (Object)null) { return CurrentContainerRef.Invoke(InventoryGui.instance) is VirtualContainer; } return false; } internal static ItemData GetEquippedChest(Player player) { Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory != null) { foreach (ItemData equippedItem in inventory.GetEquippedItems()) { if (IsVoidChestItem(equippedItem)) { VLog.Debug("原版装备槽检测到虚空宝箱: prefab=" + ((Object)equippedItem.m_dropPrefab).name + ", name=" + equippedItem.m_shared.m_name); return equippedItem; } } } List<ItemData> equippedItems = ExtraSlotsCompat.GetEquippedItems((Humanoid)(object)player); if (equippedItems != null) { foreach (ItemData item in equippedItems) { if (IsVoidChestItem(item)) { VLog.Debug("ExtraSlots 额外槽检测到虚空宝箱: prefab=" + ((Object)item.m_dropPrefab).name + ", name=" + item.m_shared.m_name); return item; } } } return null; } private static bool IsVoidChestItem(ItemData item) { if ((Object)(object)item?.m_dropPrefab != (Object)null) { return ((Object)item.m_dropPrefab).name.StartsWith("VoidChest"); } return false; } private static VirtualContainer EnsureContainer(Player player) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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) if ((Object)(object)_container != (Object)null) { return _container; } GameObject val = new GameObject("VoidChest_VirtualContainer"); val.transform.SetParent(((Component)player).transform, false); val.transform.localPosition = Vector3.zero; _container = val.AddComponent<VirtualContainer>(); VLog.Info("虚拟容器已创建。"); return _container; } internal static void Open(Player player, ItemData item) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown VirtualContainer virtualContainer = EnsureContainer(player); (int, int) size = GetSize(item); string text = (((Container)virtualContainer).m_name = item.m_shared.m_name); ((Container)virtualContainer).m_width = size.Item2; ((Container)virtualContainer).m_height = size.Item1; virtualContainer.MaxWeight = GetMaxWeight(item); Inventory val = new Inventory(text, (Sprite)null, size.Item2, size.Item1); virtualContainer.SuppressSave = true; int num; try { virtualContainer.SetInventory(val); VoidChestSave.LoadInto(val, player); num = val.NrOfItems(); } finally { virtualContainer.SuppressSave = false; } _openItem = item; InventoryGui.instance.Show((Container)(object)virtualContainer, 1); VLog.Info($"打开虚空宝箱: {text} ({size.Item1}行x{size.Item2}列),加载 {num} 件物品。"); } internal static (int rows, int cols) GetSize(ItemData item) { return (((Object)(object)item?.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : "") switch { "VoidChestMagic" => (rows: VoidChestPlugin.CapacityMagicRows.Value, cols: VoidChestPlugin.CapacityMagicCols.Value), "VoidChestFlame" => (rows: VoidChestPlugin.CapacityFlameRows.Value, cols: VoidChestPlugin.CapacityFlameCols.Value), "VoidChestCrystal" => (rows: VoidChestPlugin.CapacityCrystalRows.Value, cols: VoidChestPlugin.CapacityCrystalCols.Value), _ => (rows: VoidChestPlugin.CapacityBlackMetalRows.Value, cols: VoidChestPlugin.CapacityBlackMetalCols.Value), }; } internal static float GetMaxWeight(ItemData item) { return (((Object)(object)item?.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : "") switch { "VoidChestMagic" => VoidChestPlugin.WeightMagic.Value, "VoidChestFlame" => VoidChestPlugin.WeightFlame.Value, "VoidChestCrystal" => VoidChestPlugin.WeightCrystal.Value, _ => VoidChestPlugin.WeightBlackMetal.Value, }; } internal static int GetTier(ItemData item) { return (((Object)(object)item?.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : "") switch { "VoidChestMagic" => 2, "VoidChestFlame" => 3, "VoidChestCrystal" => 4, _ => 1, }; } internal static bool CanUseRemote(Player player) { if (VoidChestPlugin.EnableRemoteStore == null || !VoidChestPlugin.EnableRemoteStore.Value) { return false; } if (VoidChestPlugin.RemoteStoreAlwaysAvailable != null && VoidChestPlugin.RemoteStoreAlwaysAvailable.Value) { return true; } ItemData equippedChest = GetEquippedChest(player); if (equippedChest != null) { return GetTier(equippedChest) >= 2; } return false; } internal static void FlushActive(Player player) { if (!((Object)(object)player == (Object)null) && !((Object)(object)_container == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer)) { Inventory inventory = ((Container)_container).GetInventory(); if (inventory != null) { VLog.Debug($"Player.Save 触发 flush,当前库存 {inventory.NrOfItems()} 件。"); VoidChestSave.SaveFrom(inventory, player); } } } internal static void Message(Player player, string text) { if (!((Object)(object)player == (Object)null) && !((Object)(object)MessageHud.instance == (Object)null)) { ((Character)player).Message((MessageType)2, text, 0, (Sprite)null, false); } } } internal static class VoidChestNearbyStore { private static readonly List<Container> _queue = new List<Container>(); private static readonly HashSet<Container> _seen = new HashSet<Container>(); private static readonly FieldRef<Container, ZNetView> NViewRef = AccessTools.FieldRefAccess<Container, ZNetView>("m_nview"); private static bool _running; private static Container _current; private static float _waitTimer; private static int _beforeCount; private static int _processed; private static int _timedOut; private static int _rejected; private static float _currentSentAt; internal static bool FilterActive { get; private set; } internal static bool IsRunning => _running; internal static void Start(Player player) { if ((Object)(object)player == (Object)null) { return; } if (_running || VoidChestRemoteStore.IsRunning) { VoidChestManager.Message(player, VoidChestLocalization.L("$vc_nearby_in_progress")); return; } List<Container> list = FindContainers(player); if (list.Count == 0) { VoidChestManager.Message(player, VoidChestLocalization.L("$vc_nearby_none")); VLog.Info("附近存储:未找到可用容器。"); return; } _queue.Clear(); _queue.AddRange(list); _beforeCount = ((Humanoid)player).GetInventory().CountItems((string)null, -1, true); _processed = 0; _timedOut = 0; _rejected = 0; _running = true; FilterActive = true; _waitTimer = 0f; _current = null; VoidChestPerf.Reset(); VLog.Info($"附近存储开始:{list.Count} 个容器,背包物品 {_beforeCount}。"); SendNext(); } internal static void Update() { if (!_running) { return; } float num = Time.realtimeSinceStartup + 0.008f; while (true) { if ((Object)(object)_current == (Object)null) { if (_queue.Count == 0) { Finish(); break; } SendNext(); if ((Object)(object)_current != (Object)null || Time.realtimeSinceStartup >= num) { break; } } else { _waitTimer += Time.deltaTime; if (!(_waitTimer > Timeout())) { break; } VLog.Warn("附近存储:" + ((Object)((Component)_current).gameObject).name + " 响应超时,跳过。"); _timedOut++; _current = null; } } } internal static void OnStackResponse(bool granted) { if (_running && !((Object)(object)_current == (Object)null)) { VoidChestPerf.NoteStep((double)(Time.realtimeSinceStartup - _currentSentAt) * 1000.0); _processed++; if (!granted) { _rejected++; VLog.Debug("附近存储:" + ((Object)((Component)_current).gameObject).name + " 被拒绝(使用中/无权限)。"); } _current = null; } } private static void SendNext() { if (!_running) { return; } if (_queue.Count == 0) { Finish(); return; } _current = _queue[0]; _queue.RemoveAt(0); _waitTimer = 0f; _currentSentAt = Time.realtimeSinceStartup; try { VLog.Debug("附近存储:请求 " + ((Object)((Component)_current).gameObject).name + " (" + _current.m_name + ")"); _current.StackAll(); } catch (Exception ex) { VLog.Warn("附近存储:请求失败 " + (((Object)(object)_current != (Object)null) ? ((Object)((Component)_current).gameObject).name : "?") + ": " + ex.Message); _timedOut++; _current = null; } } private static void Finish() { _running = false; _current = null; FilterActive = false; Player localPlayer = Player.m_localPlayer; int num = (((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).GetInventory().CountItems((string)null, -1, true) : _beforeCount); int num2 = _beforeCount - num; string text = ((num2 > 0) ? VoidChestLocalization.L("$vc_nearby_stored", num2, _processed) : ((_processed <= 0) ? VoidChestLocalization.L("$vc_nearby_none") : VoidChestLocalization.L("$vc_nearby_nothing", _processed))); if (_rejected > 0) { text += VoidChestLocalization.L("$vc_nearby_rejected", _rejected); } if (_timedOut > 0) { text += VoidChestLocalization.L("$vc_nearby_timeout", _timedOut); } if ((Object)(object)localPlayer != (Object)null) { VoidChestManager.Message(localPlayer, text); } VLog.Info($"附近存储完成:移动 {num2} 件,容器 {_processed},拒绝 {_rejected},超时 {_timedOut}。"); VLog.Info(VoidChestPerf.Summary("附近存储", num2, _processed, _rejected, _timedOut)); } private static List<Container> FindContainers(Player player) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) Stopwatch stopwatch = Stopwatch.StartNew(); _seen.Clear(); List<Container> list = new List<Container>(); int mask = LayerMask.GetMask(new string[3] { "piece", "item", "vehicle" }); Collider[] array = Physics.OverlapSphere(((Component)player).transform.position, VoidChestPlugin.NearbyStoreRange.Value, mask); VLog.Debug($"附近存储:OverlapSphere 命中 {array.Length} 个碰撞体,半径 {VoidChestPlugin.NearbyStoreRange.Value}。"); Collider[] array2 = array; foreach (Collider val in array2) { if ((Object)(object)val == (Object)null) { continue; } Container componentInParent = ((Component)val).GetComponentInParent<Container>(); if (!((Object)(object)componentInParent == (Object)null) && !(componentInParent is VirtualContainer) && !_seen.Contains(componentInParent) && componentInParent.GetInventory() != null) { if (IsContainerBusy(componentInParent, out var reason)) { VLog.Debug("附近存储:跳过容器 " + ((Object)((Component)componentInParent).gameObject).name + "(" + reason + ")"); } else if (VoidChestPlugin.NearbyStoreCheckWard.Value && !PrivateArea.CheckAccess(((Component)val).transform.position, 0f, false, false)) { VLog.Debug("附近存储:跳过无权限容器 " + ((Object)((Component)componentInParent).gameObject).name); } else { _seen.Add(componentInParent); list.Add(componentInParent); } } } list.Sort((Container a, Container b) => Vector3.Distance(((Component)player).transform.position, ((Component)a).transform.position).CompareTo(Vector3.Distance(((Component)player).transform.position, ((Component)b).transform.position))); stopwatch.Stop(); VoidChestPerf.AddScan(stopwatch.Elapsed.TotalMilliseconds); return list; } private static bool IsContainerBusy(Container container, out string reason) { reason = null; if (container.IsInUse()) { reason = "本地使用中"; return true; } try { ZNetView val = NViewRef.Invoke(container); ZDO val2 = (((Object)(object)val != (Object)null) ? val.GetZDO() : null); if (val2 != null && val2.GetInt(ZDOVars.s_inUse, 0) == 1) { reason = "其他玩家使用中(ZDO)"; return true; } } catch (Exception ex) { VLog.Debug("ZDO 占用检查失败: " + ex.Message); } return false; } private static float Timeout() { return Mathf.Max(0.2f, VoidChestPlugin.NearbyStoreTimeout.Value); } } internal static class VoidChestPerf { internal const float FrameBudgetSeconds = 0.008f; internal static float StartRealtime; internal static float ScanMs; internal static int ScanBatches; internal static float ClassifyMs; internal static float FilterMs; internal static int StackCalls; internal static double StackMs; internal static float MaxStepMs; internal static int DataWriteCount; internal static double DataWriteMs; internal static void Reset() { StartRealtime = Time.realtimeSinceStartup; ScanMs = 0f; ScanBatches = 0; ClassifyMs = 0f; FilterMs = 0f; StackCalls = 0; StackMs = 0.0; MaxStepMs = 0f; DataWriteCount = 0; DataWriteMs = 0.0; } internal static void AddScan(double ms) { ScanMs += (float)ms; ScanBatches++; } internal static void AddClassify(double ms) { ClassifyMs += (float)ms; } internal static void AddFilter(double ms) { FilterMs += (float)ms; } internal static void AddStack(double ms) { StackCalls++; StackMs += ms; } internal static void AddDataWrite(double ms) { DataWriteCount++; DataWriteMs += ms; } internal static void NoteStep(double ms) { if (ms > (double)MaxStepMs) { MaxStepMs = (float)ms; } } internal static double TotalMs() { return (double)(Time.realtimeSinceStartup - StartRealtime) * 1000.0; } internal static string Summary(string tag, int moved, int containers, int rejected, int skipped) { return $"{tag}性能:总耗时 {TotalMs():F0}ms | 快照 {ScanMs:F1}ms({ScanBatches}批) | 分类 {ClassifyMs:F1}ms | 筛选 {FilterMs:F1}ms | 容器 {containers}(拒绝{rejected}/跳过{skipped}) | 堆叠 {StackCalls} 次 {StackMs:F1}ms | 最长步骤 {MaxStepMs:F0}ms | 数据写入 {DataWriteCount} 次 {DataWriteMs:F1}ms | 移动 {moved} 件"; } } internal static class VoidChestRemoteNet { internal enum Status { Ok, Busy, NoGuardstone, NoAccess, Disabled, Invalid } internal const string RequestRpc = "VoidChest_RemoteDepositRequest"; internal const string ResultRpc = "VoidChest_RemoteDepositResult"; private const int ProtocolVersion = 1; private const float ResponseTimeout = 30f; private static ZRoutedRpc _registeredOn; private static bool _pending; private static long _activeSeq; private static float _sentAt; private static bool _timeoutWarned; internal static void Update() { EnsureRegistered(); if (!_pending || Time.realtimeSinceStartup - _sentAt < 30f) { return; } _pending = false; if (!_timeoutWarned) { _timeoutWarned = true; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { VoidChestManager.Message(localPlayer, BuildStatusMessage(Status.Invalid, 0, 0, 0)); } VLog.Warn($"远程存入:等待服务端响应超过 {30f:F0} 秒(服务端可能未安装本 mod)。"); } } private static void EnsureRegistered() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance == null || _registeredOn == instance) { return; } try { instance.Register<ZPackage>("VoidChest_RemoteDepositRequest", (Action<long, ZPackage>)OnRequest); instance.Register<ZPackage>("VoidChest_RemoteDepositResult", (Action<long, ZPackage>)OnResult); _registeredOn = instance; _pending = false; _timeoutWarned = false; VLog.Info("远程存入 RPC 已注册。"); } catch (Exception e) { _registeredOn = instance; VLog.Error("远程存入 RPC 注册失败: ", e); } } internal static string BuildStatusMessage(Status status, int moved, int processed, int skipped) { string text; switch (status) { case Status.Ok: text = ((moved > 0) ? VoidChestLocalization.L("$vc_remote_stored", moved, processed) : VoidChestLocalization.L("$vc_remote_nothing", processed)); if (skipped > 0) { text += VoidChestLocalization.L("$vc_remote_skipped", skipped); } break; case Status.Busy: text = VoidChestLocalization.L("$vc_remote_in_progress"); break; case Status.NoGuardstone: text = VoidChestLocalization.L("$vc_remote_no_guardstone"); break; case Status.NoAccess: text = VoidChestLocalization.L("$vc_remote_no_access"); break; case Status.Disabled: text = VoidChestLocalization.L("$vc_remote_disabled"); break; default: text = VoidChestLocalization.L("$vc_remote_server_missing"); break; } return text; } internal static void RequestRemote(Player player) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown if ((Object)(object)player == (Object)null) { return; } if (_pending) { VoidChestManager.Message(player, VoidChestLocalization.L("$vc_remote_in_progress")); return; } ZRoutedRpc instance = ZRoutedRpc.instance; if (instance == null) { VoidChestManager.Message(player, BuildStatusMessage(Status.Invalid, 0, 0, 0)); return; } Inventory val; try { val = BuildFilteredSnapshot(player); } catch (Exception e) { VLog.Error("远程存入:构建背包快照失败: ", e); VoidChestManager.Message(player, BuildStatusMessage(Status.Invalid, 0, 0, 0)); return; } long num = ++_activeSeq; _pending = true; _timeoutWarned = false; _sentAt = Time.realtimeSinceStartup; try { ZPackage val2 = new ZPackage(); val.Save(val2); ZPackage val3 = new ZPackage(); val3.Write(player.GetPlayerID()); val3.Write(num); val3.Write(1); val3.Write(val.GetWidth()); val3.Write(val.GetHeight()); val3.Write(val2.GetArray()); instance.InvokeRoutedRPC("VoidChest_RemoteDepositRequest", new object[1] { val3 }); VoidChestManager.Message(player, VoidChestLocalization.L("$vc_remote_requested")); VLog.Info($"远程存入:已向服务端发送请求 #{num}(玩家 {player.GetPlayerID()},物品 {val.NrOfItems()} 件,背包数据 {val2.GetArray().Length} 字节)。"); } catch (Exception e2) { _pending = false; VLog.Error("远程存入:发送请求失败: ", e2); VoidChestManager.Message(player, BuildStatusMessage(Status.Invalid, 0, 0, 0)); } } private static Inventory BuildFilteredSnapshot(Player player) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown Inventory inventory = ((Humanoid)player).GetInventory(); int num = Mathf.Clamp(inventory.GetWidth(), 1, 64); int num2 = Mathf.Clamp(inventory.GetHeight(), 1, 64); Inventory val = new Inventory("VoidChestRemoteDeposit", (Sprite)null, num, num2); foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem != null && allItem.m_shared != null && !((Humanoid)player).IsItemEquiped(allItem) && !VoidChestFilter.ShouldSkip(allItem)) { val.AddItem(allItem.Clone()); } } return val; } private static void OnResult(long sender, ZPackage payload) { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { return; } long num; Status status; int num2; int num3; int num4; try { num = payload.ReadLong(); status = (Status)payload.ReadInt(); num2 = payload.ReadInt(); num3 = payload.ReadInt(); num4 = payload.ReadInt(); } catch (Exception e) { VLog.Error("远程存入:解析服务端结果头失败: ", e); return; } if (num != _activeSeq) { VLog.Info($"远程存入:忽略过期的服务端结果 #{num}(当前 #{_activeSeq})。"); return; } _pending = false; _timeoutWarned = false; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } if (status == Status.Ok) { try { int num5 = ApplyMovedItems(localPlayer, payload); VLog.Info($"远程存入:服务端结果 #{num} 移动 {num2} 堆叠,本地扣除 {num5} 件。"); } catch (Exception e2) { VLog.Error("远程存入:应用服务端结果失败: ", e2); } } VoidChestManager.Message(localPlayer, BuildStatusMessage(status, num2, num3, num4)); VLog.Info($"远程存入:服务端结果 #{num} status={status} 移动={num2} 箱子={num3} 跳过={num4}。"); } private static int ApplyMovedItems(Player player, ZPackage payload) { int num = payload.ReadInt(); if (num <= 0) { return 0; } Inventory inventory = ((Humanoid)player).GetInventory(); List<ItemData> list = new List<ItemData>(inventory.GetAllItems()); int num2 = 0; for (int i = 0; i < num; i++) { string b = payload.ReadString(); int num3 = payload.ReadInt(); if (num3 <= 0) { continue; } int num4 = num3; foreach (ItemData item in list) { if (num4 <= 0) { break; } if (item != null && item.m_shared != null && item.m_stack > 0 && string.Equals(VoidChestItemIdentity.Of(item), b, StringComparison.Ordinal)) { int num5 = Mathf.Min(num4, item.m_stack); inventory.RemoveItem(item, num5); num4 -= num5; num2 += num5; } } if (num4 > 0) { VLog.Warn($"远程存入:本地背包未找到可扣除的物品(还差 {num4} 件,物品可能已被移动/消耗)。"); } } return num2; } private static void OnRequest(long sender, ZPackage payload) { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Expected O, but got Unknown if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } try { long num = payload.ReadLong(); long seq = payload.ReadLong(); int num2 = payload.ReadInt(); int num3 = Mathf.Clamp(payload.ReadInt(), 1, 64); int num4 = Mathf.Clamp(payload.ReadInt(), 1, 64); byte[] array = payload.ReadByteArray(); if (num2 != 1 || num == 0L || array == null || array.Length == 0) { SendResult(sender, seq, Status.Invalid, 0, 0, 0, null); return; } if (!VoidChestPlugin.EnableRemoteStore.Value) { SendResult(sender, seq, Status.Disabled, 0, 0, 0, null); return; } Inventory val = new Inventory("VoidChestRemoteRequest", (Sprite)null, num3, num4); val.Load(new ZPackage(array)); VoidChestRemoteStore.StartRemoteJob(sender, seq, num, val); } catch (Exception ex) { VLog.Warn("远程存入:处理服务端请求失败: " + ex.Message); } } internal static void SendResult(long peer, long seq, Status status, int moved, int processed, int skipped, List<KeyValuePair<string, int>> movedEntries) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown ZRoutedRpc instance = ZRoutedRpc.instance; if (instance == null || peer == 0L) { return; } try { ZPackage val = new ZPackage(); val.Write(seq); val.Write((int)status); val.Write(moved); val.Write(processed); val.Write(skipped); int num = movedEntries?.Count ?? 0; val.Write(num); for (int i = 0; i < num; i++) { val.Write(movedEntries[i].Key); val.Write(movedEntries[i].Value); } instance.InvokeRoutedRPC(peer, "VoidChest_RemoteDepositResult", new object[1] { val }); } catch (Exception ex) { VLog.Warn($"远程存入:发送结果到 {peer} 失败: {ex.Message}"); } } } internal static class VoidChestRemoteStore { private enum Phase { Idle, FullScan, ChestFilter, Guards, Chests, Stacking } private struct GuardInfo { public Vector3 Pos; public float Radius; } private struct SnapshotEntry { public ZDOID Id; public int Prefab; } private static Phase _phase = Phase.Idle; private static Player _player; private static long _playerId; private static Inventory _targetInv; private static bool _preFiltered; private static long _responsePeer; private static long _responseSeq; private static Dictionary<string, int> _initialTotals; private static bool _objectsByIdInit; private static FieldRef<ZDOMan, Dictionary<ZDOID, ZDO>> _objectsByIdRef; private static List<SnapshotEntry> _snapshot; private static int _cursor; private static readonly List<ZDOID> _chestCandidates = new List<ZDOID>(); private static int _candidateCursor; private static readonly Dictionary<int, byte> _prefabKind = new Dictionary<int, byte>(); private static List<string> _prefabNames = new List<string>(); private static int _prefabIndex; private static readonly List<ZDO> _found = new List<ZDO>(); private static int _iter; private static readonly List<GuardInfo> _guards = new List<GuardInfo>(); private static readonly List<ZDOID> _guardIds = new List<ZDOID>(); private static readonly List<ZDOID> _chests = new List<ZDOID>(); private static readonly HashSet<ZDOID> _chestSeen = new HashSet<ZDOID>(); private static int _chestIndex; private static int _moved; private static int _processed; private static int _skipped; private static float _cacheTime = -99999f; private static long _cachePlayerId; private static readonly List<ZDOID> _cachedGuardIds = new List<ZDOID>(); private static readonly List<ZDOID> _cachedChestIds = new List<ZDOID>(); private static readonly Dictionary<int, float> _guardRadiusCache = new Dictionary<int, float>(); private static readonly Dictionary<int, Vector2i> _containerSizeCache = new Dictionary<int, Vector2i>(); internal static bool IsRunning => _phase != Phase.Idle; internal static void Start(Player player) { if (!((Object)(object)player == (Object)null)) { if (_phase != Phase.Idle) { VoidChestManager.Message(player, VoidChestLocalization.L("$vc_remote_in_progress")); VLog.Info("远程存入:已有流程进行中,忽略本次点击。"); } else if (VoidChestNearbyStore.IsRunning) { VoidChestManager.Message(player, VoidChestLocalization.L("$vc_nearby_in_progress")); } else if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) { VoidChestRemoteNet.RequestRemote(player); } else { BeginJob(0L, 0L, player.GetPlayerID(), ((Humanoid)player).GetInventory(), preFiltered: false, player); } } } internal static void StartRemoteJob(long peer, long seq, long playerId, Inventory snapshot) { if (peer != 0L && snapshot != null) { if (_phase != Phase.Idle) { VoidChestRemoteNet.SendResult(peer, seq, VoidChestRemoteNet.Status.Busy, 0, 0, 0, null); VLog.Info("远程存入:服务端已有任务在运行,拒绝客户端请求。"); } else { BeginJob(peer, seq, playerId, snapshot, preFiltered: true, null); } } } private static void BeginJob(long peer, long seq, long playerId, Inventory targetInv, bool preFiltered, Player localPlayer) { _responsePeer = peer; _responseSeq = seq; _playerId = playerId; _targetInv = targetInv; _preFiltered = preFiltered; _player = localPlayer; _initialTotals = (preFiltered ? VoidChestItemIdentity.Totals(targetInv) : null); _guards.Clear(); _guardIds.Clear(); _chests.Clear(); _chestSeen.Clear(); _chestCandidates.Clear(); _found.Clear(); _moved = 0; _processed = 0; _skipped = 0; _chestIndex = 0; _candidateCursor = 0; VoidChestPerf.Reset(); if (TryUseCache()) { _phase = Phase.Stacking; VLog.Info($"远程存入开始:缓存命中(守护石 {_guards.Count},箱子 {_chests.Count})。"); return; } if (EnsureObjectsByIdRef()) { _snapshot = SnapshotZdos(); _cursor = 0; _phase = Phase.FullScan; VLog.Info($"远程存入开始:全量快照 {((_snapshot != null) ? _snapshot.Count : 0)} 个 ZDO(玩家 ID {_playerId})。"); return; } List<string> list = CollectPrefabs((GameObject go) => (Object)(object)go.GetComponent<PrivateArea>() != (Object)null); if (list.Count == 0) { Finish(VoidChestRemoteNet.Status.NoGuardstone); VLog.Info("远程存入:未找到守护石 prefab。"); return; } _prefabNames = list; _prefabIndex = 0; _iter = 0; _phase = Phase.Guards; VLog.Info($"远程存入开始:降级 prefab 扫描模式(守护石 prefab {list.Count} 种,玩家 ID {_playerId})。"); } internal static void Update() { //IL_0084: Unknown result type (might be due to invalid IL or missing references) if (_phase == Phase.Idle) { return; } float num = Time.realtimeSinceStartup + 0.008f; if (_phase == Phase.FullScan) { UpdateFullScan(num); } else if (_phase == Phase.ChestFilter) { UpdateChestFilter(num); } else if (_phase == Phase.Guards) { UpdateGuards(num); } else if (_phase == Phase.Chests) { UpdateChests(num); } else { if (_phase != Phase.Stacking) { return; } while (Time.realtimeSinceStartup < num) { if (_chestIndex >= _chests.Count) { Finish(VoidChestRemoteNet.Status.Ok); break; } ProcessChest(_chests[_chestIndex++]); } } } private static bool EnsureObjectsByIdRef() { if (_objectsByIdInit) { return _objectsByIdRef != null; } _objectsByIdInit = true; try { _objectsByIdRef = AccessTools.FieldRefAccess<ZDOMan, Dictionary<ZDOID, ZDO>>("m_objectsByID"); } catch (Exception ex) { _objectsByIdRef = null; VLog.Warn("ZDOMan.m_objectsByID 反射失败,降级为 prefab 扫描模式: " + ex.Message); } return _objectsByIdRef != null; } private static List<SnapshotEntry> SnapshotZdos() { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) Stopwatch stopwatch = Stopwatch.StartNew(); Dictionary<ZDOID, ZDO> obj = _objectsByIdRef.Invoke(ZDOMan.instance); List<SnapshotEntry> list = new List<SnapshotEntry>(obj.Count); foreach (KeyValuePair<ZDOID, ZDO> item in obj) { ZDO value = item.Value; if (value != null) { list.Add(new SnapshotEntry { Id = item.Key, Prefab = value.GetPrefab() }); } } stopwatch.Stop(); VoidChestPerf.AddScan(stopwatch.Elapsed.TotalMilliseconds); return list; } private static void UpdateFullScan(float budgetEnd) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) if (_snapshot == null) { _phase = Phase.ChestFilter; return; } Stopwatch stopwatch = Stopwatch.StartNew(); int num = 0; while (_cursor < _snapshot.Count) { SnapshotEntry snapshotEntry = _snapshot[_cursor++]; num++; byte b; try { b = ClassifyPrefab(snapshotEntry.Prefab); } catch { b = 0; } switch (b) { case 1: { ZDO zDO = ZDOMan.instance.GetZDO(snapshotEntry.Id); if (zDO != null && HasAccess(zDO)) { _guards.Add(new GuardInfo { Pos = zDO.GetPosition(), Radius = GetGuardRadius(snapshotEntry.Prefab) }); _guardIds.Add(snapshotEntry.Id); } break; } case 2: _chestCandidates.Add(snapshotEntry.Id); break; } if ((num & 0x3FF) == 0 && Time.realtimeSinceStartup >= budgetEnd) { break; } } stopwatch.Stop(); VoidChestPerf.AddClassify(stopwatch.Elapsed.TotalMilliseconds); if (_cursor >= _snapshot.Count) { _snapshot = null; if (_guards.Count == 0) { Finish(VoidChestRemoteNet.Status.NoAccess); return; } _candidateCursor = 0; _phase = Phase.ChestFilter; VLog.Info($"远程存入:快照分类完成,守护石 {_guards.Count},容器候选 {_chestCandidates.Count}。"); } } private static void UpdateChestFilter(float budgetEnd) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) Stopwatch stopwatch = Stopwatch.StartNew(); int num = 0; while (_candidateCursor < _chestCandidates.Count) { ZDOID val = _chestCandidates[_candidateCursor++]; num++; if (!_chestSeen.Contains(val)) { ZDO zDO = ZDOMan.instance.GetZDO(val); if (zDO != null && IsInAnyGuard(zDO.GetPosition())) { if (zDO.GetInt(ZDOVars.s_inUse, 0) == 1) { _skipped++; } else { _chestSeen.Add(val); _chests.Add(val); } } } if ((num & 0x3FF) == 0 && Time.realtimeSinceStartup >= budgetEnd) { break; } } stopwatch.Stop(); VoidChestPerf.AddFilter(stopwatch.Elapsed.TotalMilliseconds); if (_candidateCursor >= _chestCandidates.Count) { _chestCandidates.Clear(); _chestIndex = 0; UpdateCache(); _phase = Phase.Stacking; VLog.Info($"远程存入:目标箱子 {_chests.Count} 个(有权限守护石 {_guards.Count} 个)。"); } } private static byte ClassifyPrefab(int prefabHash) { if (_prefabKind.TryGetValue(prefabHash, out var value)) { return value; } byte b = 0; GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefabHash) : null); if ((Object)(object)val != (Object)null) { if ((Object)(object)val.GetComponent<PrivateArea>() != (Object)null) { b = 1; } else if ((Object)(object)val.GetComponent<Container>() != (Object)null && (Object)(object)val.GetComponent<Ship>() == (Object)null && (Object)(object)val.GetComponent<Vagon>() == (Object)null && !((Object)val).name.ToLower().Contains("tombstone")) { b = 2; } } _prefabKind[prefabHash] = b; return b; } private static void UpdateGuards(float budgetEnd) { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) while (Time.realtimeSinceStartup < budgetEnd) { if (_prefabIndex >= _prefabNames.Count) { if (_guards.Count == 0) { Finish(VoidChestRemoteNet.Status.NoAccess); } else { BeginChestScan(); } break; } string text = _prefabNames[_prefabIndex]; Stopwatch stopwatch = Stopwatch.StartNew(); bool allZDOsWithPrefabIterative = ZDOMan.instance.GetAllZDOsWithPrefabIterative(text, _found, ref _iter); stopwatch.Stop(); VoidChestPerf.AddScan(stopwatch.Elapsed.TotalMilliseconds); if (!allZDOsWithPrefabIterative) { continue; } foreach (ZDO item in _found) { if (item.IsValid() && HasAccess(item)) { _guards.Add(new GuardInfo { Pos = item.GetPosition(), Radius = GetGuardRadius(item.GetPrefab()) }); _guardIds.Add(item.m_uid); } } _found.Clear(); _iter = 0; _prefabIndex++; } } private static void BeginChestScan() { _prefabNames = CollectPrefabs(delegate(GameObject go) { if ((Object)(object)go.GetComponent<Container>() == (Object)null) { return false; } if ((Object)(object)go.GetComponent<Ship>() != (Object)null || (Object)(object)go.GetComponent<Vagon>() != (Object)null) { return false; } return !((Object)go).name.ToLower().Contains("tombstone"); }); _prefabIndex = 0; _found.Clear(); _iter = 0; _phase = Phase.Chests; VLog.Info($"远程存入:开始扫描容器({_prefabNames.Count} 种 prefab),有权限守护石 {_guards.Count} 个。"); } private static void UpdateChests(float budgetEnd) { //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) while (Time.realtimeSinceStartup < budgetEnd) { if (_prefabIndex >= _prefabNames.Count) { _chestIndex = 0; UpdateCache(); _phase = Phase.Stacking; VLog.Info($"远程存入:目标箱子 {_chests.Count} 个(有权限守护石 {_guards.Count} 个)。"); break; } string text = _prefabNames[_prefabIndex]; Stopwatch stopwatch = Stopwatch.StartNew(); bool allZDOsWithPrefabIterative = ZDOMan.instance.GetAllZDOsWithPrefabIterative(text, _found, ref _iter); stopwatch.Stop(); VoidChestPerf.AddScan(stopwatch.Elapsed.TotalMilliseconds); if (!allZDOsWithPrefabIterative) { continue; } foreach (ZDO item in _found) { if (item.IsValid() && !_chestSeen.Contains(item.m_uid) && IsInAnyGuard(item.GetPosition())) { if (item.GetInt(ZDOVars.s_inUse, 0) == 1) { _skipped++; continue; } _chestSeen.Add(item.m_uid); _chests.Add(item.m_uid); } } _found.Clear(); _iter = 0; _prefabIndex++; } } private static bool TryUseCache() { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: 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_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) if (VoidChestPlugin.RemoteStoreCacheSeconds.Value <= 0f) { return false; } if (_cachePlayerId != _playerId) { return false; } if (_cachedGuardIds.Count == 0 && _cachedChestIds.Count == 0) { return false; } if (Time.realtimeSinceStartup - _cacheTime > VoidChestPlugin.RemoteStoreCacheSeconds.Value) { return false; } foreach (ZDOID cachedGuardId in _cachedGuardIds) { ZDO zDO = ZDOMan.instance.GetZDO(cachedGuardId); if (zDO != null && zDO.IsValid()) { _guards.Add(new GuardInfo { Pos = zDO.GetPosition(), Radius = GetGuardRadius(zDO.GetPrefab()) }); _guardIds.Add(cachedGuardId); } } if (_guards.Count == 0) { return false; } foreach (ZDOID cachedChestId in _cachedChestIds) { ZDO zDO2 = ZDOMan.instance.GetZDO(cachedChestId); if (zDO2 != null && zDO2.IsValid()) { if (zDO2.GetInt(ZDOVars.s_inUse, 0) == 1) { _skipped++; } else if (_chestSeen.Add(cachedChestId)) { _chests.Add(cachedChestId); } } } if (_chests.Count == 0) { return false; } VLog.Info($"远程存入:缓存命中(缓存于 {Time.realtimeSinceStartup - _cacheTime:F0}s 前,守护石 {_guards.Count},箱子 {_chests.Count})。"); return true; } private static void UpdateCache() { _cachedGuardIds.Clear(); _cachedGuardIds.AddRange(_guardIds); _cachedChestIds.Clear(); _cachedChestIds.AddRange(_chests); _cachePlayerId = _playerId; _cacheTime = Time.realtimeSinceStartup; VLog.Debug($"远程存入:缓存已更新(守护石 {_cachedGuardIds.Count},箱子 {_cachedChestIds.Count})。"); } private static void ProcessChest(ZDOID id) { //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown Inventory targetInv = _targetInv; if (targetInv == null) { return; } try { ZDO zDO = ZDOMan.instance.GetZDO(id); if (zDO == null || !zDO.IsValid()) { _skipped++; return; } if (zDO.GetInt(ZDOVars.s_inUse, 0) == 1) { _skipped++; return; } Stopwatch stopwatch = Stopwatch.StartNew(); Vector2i containerSize = GetContainerSize(zDO.GetPrefab()); Inventory val = new Inventory("RemoteContainer", (Sprite)null, containerSize.x, containerSize.y); byte[] byteArray = zDO.GetByteArray(ZDOVars.s_items, (byte[])null); if (byteArray != null) { val.Load(new ZPackage(byteArray)); } int num = (_preFiltered ? VoidChestFilter.StackAllRaw(val, targetInv) : VoidChestFilter.StackAllFiltered(val, targetInv)); if (num > 0) { Stopwatch stopwatch2 = Stopwatch.StartNew(); ZPackage val2 = new ZPackage(); val.Save(val2); zDO.Set(ZDOVars.s_items, val2.GetArray()); stopwatch2.Stop(); VoidChestPerf.AddDataWrite(stopwatch2.Elapsed.TotalMilliseconds); _moved += num; } _processed++; stopwatch.Stop(); VoidChestPerf.NoteStep(stopwatch.Elapsed.TotalMilliseconds); } catch (Exception ex) { VLog.Warn($"远程存入:处理箱子 {id} 失败: {ex.Message}"); _skipped++; } } private static void Finish(VoidChestRemoteNet.Status status) { _phase = Phase.Idle; if (_responsePeer != 0L) { List<KeyValuePair<string, int>> movedEntries = null; if (status == VoidChestRemoteNet.Status.Ok && _initialTotals != null && _targetInv != null) { movedEntries = BuildMovedEntries(); } VoidChestRemoteNet.SendResult(_responsePeer, _responseSeq, status, _moved, _processed, _skipped, movedEntries); } else if ((Object)(object)_player != (Object)null) { VoidChestManager.Message(_player, VoidChestRemoteNet.BuildStatusMessage(status, _moved, _processed, _skipped)); } VLog.Info($"远程存入完成:状态={status},移动 {_moved} 堆叠,箱子 {_processed},跳过 {_skipped},守护石 {_guards.Count}。"); VLog.Info(VoidChestPerf.Summary("远程存储", _moved, _processed, 0, _skipped)); _player = null; _targetInv = null; _initialTotals = null; _responsePeer = 0L; _responseSeq = 0L; _preFiltered = false; } private static List<KeyValuePair<string, int>> BuildMovedEntries() { List<KeyValuePair<string, int>> list = new List<KeyValuePair<string, int>>(); Dictionary<string, int> dictionary = VoidChestItemIdentity.Totals(_targetInv); foreach (KeyValuePair<string, int> initialTotal in _initialTotals) { dictionary.TryGetValue(initialTotal.Key, out var value); int num = initialTotal.Value - value; if (num > 0)