using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Valheim Item Vacuum")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Valheim Item Vacuum")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("a0ea8960-687f-4860-821c-01a3e40c5dd8")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace MasterVacuumMod;
[BepInPlugin("com.peep.mastervacuum", "Item Vacuum", "1.0.1")]
public class MasterVacuumPlugin : BaseUnityPlugin
{
[HarmonyPatch(typeof(Terminal), "InitTerminal")]
private static class ConsolePatch
{
[Serializable]
[CompilerGenerated]
private sealed class <>c
{
public static readonly <>c <>9 = new <>c();
public static Func<string, bool> <>9__0_1;
public static ConsoleEvent <>9__0_0;
internal void <Postfix>b__0_0(ConsoleEventArgs args)
{
string text = ZoneSystem.instance.m_globalKeysValues.Keys.FirstOrDefault((string k) => k.StartsWith("mastervacuum_zdoid"));
if (!string.IsNullOrEmpty(text))
{
ZoneSystem.instance.RemoveGlobalKey(text);
args.Context.AddString("Master Vacuum key cleared.");
}
else
{
args.Context.AddString("No vacuum key found.");
}
}
internal bool <Postfix>b__0_1(string k)
{
return k.StartsWith("mastervacuum_zdoid");
}
}
private static void Postfix(Terminal __instance)
{
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//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_0029: Expected O, but got Unknown
object obj = <>c.<>9__0_0;
if (obj == null)
{
ConsoleEvent val = delegate(ConsoleEventArgs args)
{
string text = ZoneSystem.instance.m_globalKeysValues.Keys.FirstOrDefault((string k) => k.StartsWith("mastervacuum_zdoid"));
if (!string.IsNullOrEmpty(text))
{
ZoneSystem.instance.RemoveGlobalKey(text);
args.Context.AddString("Master Vacuum key cleared.");
}
else
{
args.Context.AddString("No vacuum key found.");
}
};
<>c.<>9__0_0 = val;
obj = (object)val;
}
new ConsoleCommand("resetvacuum", "Manual wipe of the vacuum global key.", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
}
}
[HarmonyPatch(typeof(Container), "Awake")]
private static class Container_Track_Patch
{
private static void Postfix(Container __instance)
{
//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
ZNetView value = Traverse.Create((object)__instance).Field("m_nview").GetValue<ZNetView>();
if (!((Object)(object)value == (Object)null) && value.IsValid())
{
value.Register("OnInventoryChanged", (Action<long>)delegate
{
});
if (!TrackedContainers.Contains(__instance))
{
TrackedContainers.Add(__instance);
}
Inventory inventory = __instance.GetInventory();
value.GetZDO().Set(HashWidth, inventory.GetWidth(), false);
value.GetZDO().Set(HashHeight, inventory.GetHeight(), false);
if (((Object)__instance).name.Contains("Item_Vacuum") && ZNet.instance.IsServer())
{
ZDOID uid2 = value.GetZDO().m_uid;
ZoneSystem.instance.SetGlobalKey(string.Format("{0}={1}:{2}", "mastervacuum_zdoid", ((ZDOID)(ref uid2)).UserID, ((ZDOID)(ref uid2)).ID));
}
}
}
}
[HarmonyPatch(typeof(Container), "OnDestroyed")]
private static class Container_Cleanup_Patch
{
private static void Prefix(Container __instance)
{
TrackedContainers.Remove(__instance);
if (((Object)__instance).name.Contains("Item_Vacuum") && ZNet.instance.IsServer())
{
string text = ZoneSystem.instance.m_globalKeysValues.Keys.FirstOrDefault((string k) => k.StartsWith("mastervacuum_zdoid"));
if (!string.IsNullOrEmpty(text))
{
ZoneSystem.instance.RemoveGlobalKey(text);
}
}
}
}
[HarmonyPatch(typeof(ItemDrop), "SlowUpdate")]
private static class ItemDrop_SlowUpdate_Patch
{
private static void Postfix(ItemDrop __instance, ZNetView ___m_nview)
{
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_008d: Unknown result type (might be due to invalid IL or missing references)
//IL_0092: Unknown result type (might be due to invalid IL or missing references)
//IL_0098: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)___m_nview == (Object)null || !___m_nview.IsValid() || !___m_nview.IsOwner() || !IsItemTypeAllowed(__instance.m_itemData.m_shared.m_itemType))
{
return;
}
string text = ZoneSystem.instance.m_globalKeysValues.Keys.FirstOrDefault((string k) => k.StartsWith("mastervacuum_zdoid"));
if (string.IsNullOrEmpty(text))
{
return;
}
string[] array = text.Split(new char[1] { '=' });
if (array.Length < 2)
{
return;
}
ZDOID val = ParseZDOID(array[1]);
ZDO zDO = ZDOMan.instance.GetZDO(val);
if (zDO != null)
{
MasterVacuumPlugin masterVacuumPlugin = (MasterVacuumPlugin)(object)Chainloader.PluginInfos["com.peep.mastervacuum"].Instance;
Inventory inventoryFromZDO = masterVacuumPlugin.GetInventoryFromZDO(zDO, 6, 3);
if (inventoryFromZDO.CanAddItem(__instance.m_itemData, -1))
{
inventoryFromZDO.AddItem(__instance.m_itemData);
masterVacuumPlugin.SaveInventoryToZDO(inventoryFromZDO, zDO);
___m_nview.Destroy();
}
}
}
private static bool IsItemTypeAllowed(ItemType type)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_0048: Expected I4, but got Unknown
switch (type - 1)
{
case 0:
return PickUpMaterials.Value;
case 1:
return PickUpConsumables.Value;
case 12:
return PickUpTrophies.Value;
case 2:
case 3:
case 4:
case 5:
case 6:
case 10:
case 13:
return PickUpEquipment.Value;
case 15:
return PickUpMisc.Value;
default:
return true;
}
}
}
[HarmonyPatch(typeof(Player), "UpdatePlacement")]
private static class Placement_Blocker_Patch
{
private static bool Prefix(Player __instance)
{
//IL_008a: Unknown result type (might be due to invalid IL or missing references)
//IL_008f: 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)
GameObject value = Traverse.Create((object)__instance).Field("m_placementGhost").GetValue<GameObject>();
if ((Object)(object)value == (Object)null || !((Object)value).name.Contains("Item_Vacuum"))
{
return true;
}
string text = ZoneSystem.instance.m_globalKeysValues.Keys.FirstOrDefault((string k) => k.StartsWith("mastervacuum_zdoid"));
if (!string.IsNullOrEmpty(text))
{
string[] array = text.Split(new char[1] { '=' });
if (array.Length >= 2)
{
ZDOID val = ParseZDOID(array[1]);
if (ZDOMan.instance.GetZDO(val) == null)
{
ZoneSystem.instance.RemoveGlobalKey(text);
return true;
}
}
SetPlacementGhostValidDelegate?.Invoke(__instance, arg2: false);
if (ZInput.GetButtonDown("Attack") || ZInput.GetButton("Attack"))
{
((Character)__instance).Message((MessageType)2, "A Master Vacuum already exists!", 0, (Sprite)null);
return false;
}
}
return true;
}
}
[HarmonyPatch(typeof(ZNetScene), "Awake")]
public static class ZNetSceneAwakePatch
{
public static void Prefix(ZNetScene __instance)
{
if ((Object)(object)vacuumPrefab != (Object)null)
{
__instance.m_prefabs.Add(vacuumPrefab);
}
}
}
[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
private static class RegisterAndSyncRPC
{
private static void Postfix(ZNet __instance, ZNetPeer peer)
{
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Expected O, but got Unknown
peer.m_rpc.Register<ZPackage>("MasterVacuum_RPC_SyncConfig", (Action<ZRpc, ZPackage>)RPC_MasterVacuum_SyncConfig);
if (ZNet.instance.IsServer())
{
logger.LogInfo((object)("Sending config to peer: " + peer.m_playerName));
ZPackage val = new ZPackage();
val.Write(PickUpMaterials.Value);
val.Write(PickUpConsumables.Value);
val.Write(PickUpTrophies.Value);
val.Write(PickUpEquipment.Value);
val.Write(PickUpMisc.Value);
peer.m_rpc.Invoke("MasterVacuum_RPC_SyncConfig", new object[1] { val });
}
}
private static void RPC_MasterVacuum_SyncConfig(ZRpc rpc, ZPackage pkg)
{
if (!ZNet.instance.IsServer())
{
logger.LogInfo((object)"Received config from server. Applying settings...");
PickUpMaterials.Value = pkg.ReadBool();
PickUpConsumables.Value = pkg.ReadBool();
PickUpTrophies.Value = pkg.ReadBool();
PickUpEquipment.Value = pkg.ReadBool();
PickUpMisc.Value = pkg.ReadBool();
}
}
}
[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
public static class ObjectDB_Patch
{
public static void Postfix(ObjectDB __instance)
{
GameObject itemPrefab = __instance.GetItemPrefab("Hammer");
if ((Object)(object)itemPrefab != (Object)null)
{
PieceTable buildPieces = itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_buildPieces;
if ((Object)(object)vacuumPrefab != (Object)null && !buildPieces.m_pieces.Contains(vacuumPrefab))
{
buildPieces.m_pieces.Add(vacuumPrefab);
}
}
}
}
[HarmonyPatch(typeof(ZNetScene), "Awake")]
public static class ZNetScene_Finalize_Patch
{
public static void Postfix(ZNetScene __instance)
{
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_005d: Unknown result type (might be due to invalid IL or missing references)
//IL_0065: Expected O, but got Unknown
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_006c: Unknown result type (might be due to invalid IL or missing references)
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
//IL_007b: Expected O, but got Unknown
if (!((Object)(object)vacuumPrefab == (Object)null))
{
Piece component = vacuumPrefab.GetComponent<Piece>();
GameObject prefab = __instance.GetPrefab("vfx_Place_chest");
GameObject prefab2 = __instance.GetPrefab("sfx_build_hammer_wood");
if ((Object)(object)component != (Object)null && (Object)(object)prefab != (Object)null)
{
component.m_placeEffect.m_effectPrefabs = (EffectData[])(object)new EffectData[2]
{
new EffectData
{
m_prefab = prefab,
m_enabled = true
},
new EffectData
{
m_prefab = prefab2,
m_enabled = true
}
};
}
}
}
}
private const string ModGUID = "com.peep.mastervacuum";
private const string ModName = "Item Vacuum";
private const string ModVersion = "1.0.1";
public static ManualLogSource logger;
private readonly Harmony harmony = new Harmony("com.peep.mastervacuum");
private static GameObject vacuumPrefab;
private static MethodInfo _findObjectsMethod;
private static float m_distributeTimer = 0f;
private const string GlobalKeyName = "mastervacuum_zdoid";
private static readonly int HashWidth = StringExtensionMethods.GetStableHashCode("mastervac_width");
private static readonly int HashHeight = StringExtensionMethods.GetStableHashCode("mastervac_height");
public static readonly List<Container> TrackedContainers = new List<Container>();
private static Action<Player, bool> SetPlacementGhostValidDelegate;
public static ConfigEntry<bool> PickUpMaterials;
public static ConfigEntry<bool> PickUpConsumables;
public static ConfigEntry<bool> PickUpTrophies;
public static ConfigEntry<bool> PickUpEquipment;
public static ConfigEntry<bool> PickUpMisc;
public void Awake()
{
logger = ((BaseUnityPlugin)this).Logger;
PickUpMaterials = ((BaseUnityPlugin)this).Config.Bind<bool>("Filters", "PickUpMaterials", true, "Pick up items like wood, stone, and ores.");
PickUpConsumables = ((BaseUnityPlugin)this).Config.Bind<bool>("Filters", "PickUpConsumables", true, "Pick up food and potions.");
PickUpTrophies = ((BaseUnityPlugin)this).Config.Bind<bool>("Filters", "PickUpTrophies", true, "Pick up monster trophies.");
PickUpEquipment = ((BaseUnityPlugin)this).Config.Bind<bool>("Filters", "PickUpEquipment", false, "Pick up weapons and armor.");
PickUpMisc = ((BaseUnityPlugin)this).Config.Bind<bool>("Filters", "PickUpMisc", false, "Pick up miscellaneous items.");
LoadAssets();
_findObjectsMethod = typeof(ZDOMan).GetMethod("FindObjects", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
{
typeof(Vector2i),
typeof(List<ZDO>)
}, null);
SetPlacementGhostValidDelegate = AccessTools.MethodDelegate<Action<Player, bool>>(AccessTools.Method(typeof(Player), "SetPlacementGhostValid", (Type[])null, (Type[])null), (object)null, true);
harmony.PatchAll();
logger.LogInfo((object)"Master Vacuum: Full System Initialized (Sorting + Dimensions + Failsafes).");
}
private void Update()
{
if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
{
m_distributeTimer += Time.deltaTime;
if (m_distributeTimer >= 5f)
{
m_distributeTimer = 0f;
ProcessVacuumCycle();
}
}
}
private void LoadAssets()
{
Assembly executingAssembly = Assembly.GetExecutingAssembly();
string text = executingAssembly.GetManifestResourceNames().FirstOrDefault((string str) => str.EndsWith("item_vacuum"));
if (text != null)
{
using (Stream stream = executingAssembly.GetManifestResourceStream(text))
{
AssetBundle obj = AssetBundle.LoadFromStream(stream);
vacuumPrefab = obj.LoadAsset<GameObject>("Item_Vacuum");
obj.Unload(false);
}
}
}
private void ProcessVacuumCycle()
{
//IL_0059: 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_0064: Unknown result type (might be due to invalid IL or missing references)
//IL_0098: Unknown result type (might be due to invalid IL or missing references)
//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
string text = ZoneSystem.instance.m_globalKeysValues.Keys.FirstOrDefault((string k) => k.StartsWith("mastervacuum_zdoid"));
if (string.IsNullOrEmpty(text))
{
return;
}
string[] array = text.Split(new char[1] { '=' });
if (array.Length < 2)
{
return;
}
ZDOID val = ParseZDOID(array[1]);
ZDO zDO = ZDOMan.instance.GetZDO(val);
if (zDO == null)
{
ZoneSystem.instance.RemoveGlobalKey(array[0]);
return;
}
Inventory inventoryFromZDO = GetInventoryFromZDO(zDO, 6, 3);
if (inventoryFromZDO.GetAllItems().Count == 0)
{
return;
}
List<ZDO> list = FindAllZDOsInRange(zDO.GetPosition(), 30f, val);
bool flag = false;
foreach (ZDO containerZDO in list)
{
if (string.IsNullOrEmpty(containerZDO.GetString(ZDOVars.s_items, "")))
{
continue;
}
Container val2 = TrackedContainers.Find(delegate(Container c)
{
//IL_0030: 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)
if ((Object)(object)c == (Object)null)
{
return false;
}
ZNetView value2 = Traverse.Create((object)c).Field("m_nview").GetValue<ZNetView>();
return (Object)(object)value2 != (Object)null && value2.GetZDO().m_uid == containerZDO.m_uid;
});
Inventory val3 = ((!((Object)(object)val2 != (Object)null)) ? GetInventoryFromZDO(containerZDO, 4, 2) : val2.GetInventory());
bool flag2 = false;
List<ItemData> allItems = inventoryFromZDO.GetAllItems();
for (int num = allItems.Count - 1; num >= 0; num--)
{
ItemData val4 = allItems[num];
if (val3.HaveItem(val4.m_shared.m_name, true) && val3.CanAddItem(val4, -1))
{
val3.AddItem(val4);
inventoryFromZDO.RemoveItem(val4);
flag2 = true;
flag = true;
}
}
if (flag2)
{
if ((Object)(object)val2 != (Object)null)
{
ZNetView value = Traverse.Create((object)val2).Field("m_nview").GetValue<ZNetView>();
if (value != null)
{
value.InvokeRPC(ZNetView.Everybody, "OnInventoryChanged", Array.Empty<object>());
}
}
else
{
SaveInventoryToZDO(val3, containerZDO);
}
}
if (inventoryFromZDO.GetAllItems().Count == 0)
{
break;
}
}
if (flag)
{
SaveInventoryToZDO(inventoryFromZDO, zDO);
}
}
public Inventory GetInventoryFromZDO(ZDO zdo, int fallbackW, int fallbackH)
{
//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
//IL_00b0: Expected O, but got Unknown
//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
//IL_00d1: Expected O, but got Unknown
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
foreach (Container trackedContainer in TrackedContainers)
{
if (!((Object)(object)trackedContainer == (Object)null))
{
ZNetView value = Traverse.Create((object)trackedContainer).Field("m_nview").GetValue<ZNetView>();
if (Object.op_Implicit((Object)(object)value) && value.IsValid() && value.GetZDO().m_uid == zdo.m_uid)
{
return trackedContainer.GetInventory();
}
}
}
int @int = zdo.GetInt(HashWidth, fallbackW);
int int2 = zdo.GetInt(HashHeight, fallbackH);
Inventory val = new Inventory("temp", (Sprite)null, @int, int2);
string @string = zdo.GetString(ZDOVars.s_items, "");
if (!string.IsNullOrEmpty(@string))
{
ZPackage val2 = new ZPackage(@string);
val.Load(val2);
}
return val;
}
public void SaveInventoryToZDO(Inventory inv, ZDO zdo)
{
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Expected O, but got Unknown
//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
if (zdo == null || !zdo.IsValid())
{
return;
}
if (zdo.GetOwner() == 0L)
{
zdo.SetOwner(ZNet.GetUID());
}
zdo.Set(HashWidth, inv.GetWidth(), false);
zdo.Set(HashHeight, inv.GetHeight(), false);
ZPackage val = new ZPackage();
inv.Save(val);
zdo.Set(ZDOVars.s_items, val.GetBase64());
uint dataRevision = zdo.DataRevision;
zdo.DataRevision = dataRevision + 1;
foreach (Container trackedContainer in TrackedContainers)
{
if (!((Object)(object)trackedContainer == (Object)null))
{
ZNetView value = Traverse.Create((object)trackedContainer).Field("m_nview").GetValue<ZNetView>();
if (Object.op_Implicit((Object)(object)value) && value.IsValid() && value.GetZDO().m_uid == zdo.m_uid)
{
value.InvokeRPC(ZNetView.Everybody, "OnInventoryChanged", Array.Empty<object>());
break;
}
}
}
}
private List<ZDO> FindAllZDOsInRange(Vector3 center, float radius, ZDOID ignore)
{
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
//IL_0088: Unknown result type (might be due to invalid IL or missing references)
//IL_008d: Unknown result type (might be due to invalid IL or missing references)
//IL_0097: Unknown result type (might be due to invalid IL or missing references)
//IL_009c: 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)
List<ZDO> list = new List<ZDO>();
float num = radius * radius;
Vector2i zone = ZoneSystem.GetZone(center);
Vector2i val = default(Vector2i);
for (int i = -1; i <= 1; i++)
{
for (int j = -1; j <= 1; j++)
{
((Vector2i)(ref val))..ctor(zone.x + i, zone.y + j);
List<ZDO> list2 = new List<ZDO>();
_findObjectsMethod?.Invoke(ZDOMan.instance, new object[2] { val, list2 });
if (list2 == null)
{
continue;
}
foreach (ZDO item in list2)
{
if (item != null && !(item.m_uid == ignore) && Vector3.SqrMagnitude(item.GetPosition() - center) <= num)
{
list.Add(item);
}
}
}
}
return list;
}
private static ZDOID ParseZDOID(string id)
{
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
string[] array = id.Split(new char[1] { ':' });
if (array.Length == 2)
{
return new ZDOID(long.Parse(array[0]), uint.Parse(array[1]));
}
return ZDOID.None;
}
}