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 SwiftPickup v1.3.3
SwiftPickup.dll
Decompiled 3 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("SwiftPickup")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("SwiftPickup")] [assembly: AssemblyTitle("SwiftPickup")] [assembly: AssemblyVersion("1.0.0.0")] namespace SwiftPickup; [BepInPlugin("user.swiftpickup", "SwiftPickup", "1.3.3")] [BepInProcess("valheim.exe")] public class SwiftPickupPlugin : BaseUnityPlugin { private struct Candidate { public ItemDrop Drop; public float Distance; } [HarmonyPatch(typeof(Player), "Update")] private static class PlayerUpdatePatch { private static void Postfix(Player __instance) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { if (IsPickupEnabled() && MagnetItems.Count > 0) { MoveMagnets(__instance); } else if (MagnetItems.Count > 0 && !IsPickupEnabled()) { MagnetItems.Clear(); } KeyboardShortcut value = _toggleKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { bool flag = !_modEnabled.Value; _modEnabled.Value = flag; ((Character)__instance).Message((MessageType)1, "SwiftPickup подбор: " + (flag ? "вкл" : "выкл") + " (множитель и кучки — независимо)", 0, (Sprite)null, false); _log.LogInfo((object)$"Pickup toggled to {flag}"); } } } } [HarmonyPatch(typeof(Player), "AutoPickup")] private static class AutoPickupPatch { private static bool Prefix(Player __instance, float __0) { try { if (((Character)__instance).IsTeleporting()) { return false; } HandleScan(__instance, __0); } catch (Exception arg) { _log.LogError((object)$"AutoPickup patch error: {arg}"); } return false; } } [HarmonyPatch(typeof(Humanoid), "Pickup")] private static class HumanoidPickupPatch { private static void Prefix(Humanoid __instance, GameObject __0) { _handMultiplyPending = false; try { if ((Object)(object)__0 == (Object)null || _lootMultiplier.Value <= 1 || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || ((Character)__instance).IsTeleporting()) { return; } ItemDrop component = __0.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null || !component.m_autoPickup) { return; } object? obj = _humanoidInventory?.GetValue(__instance); Inventory val = (Inventory)((obj is Inventory) ? obj : null); if (val == null) { return; } component.Load(); ItemData itemData = component.m_itemData; if (itemData == null || itemData.m_shared == null || itemData.m_shared.m_questItem || IsIgnored(itemData) || val.ContainsItem(itemData)) { return; } int stack = itemData.m_stack; if (stack > 0) { int handMultiplyTarget = (itemData.m_stack = stack * _lootMultiplier.Value); if (!val.CanAddItem(itemData, -1) || itemData.GetWeight(-1) + val.GetTotalWeight() > Player.m_localPlayer.GetMaxCarryWeight()) { itemData.m_stack = stack; return; } _handMultiplyTarget = handMultiplyTarget; _handMultiplyPending = true; } } catch (Exception ex) { _log.LogWarning((object)("Hand pickup multiply failed: " + ex.Message)); } } private static void Postfix() { _handMultiplyPending = false; } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })] private static class InventoryAddItemPatch { private static void Prefix(Inventory __instance, ItemData __0) { if (!_handMultiplyPending) { return; } _handMultiplyPending = false; try { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && __instance == ((Humanoid)localPlayer).GetInventory() && __0 != null && __0.m_shared != null) { int stack = __0.m_stack; __0.m_stack = _handMultiplyTarget; if (!__instance.CanAddItem(__0, -1) || __0.GetWeight(-1) + __instance.GetTotalWeight() > localPlayer.GetMaxCarryWeight()) { __0.m_stack = stack; } } } catch (Exception ex) { _log.LogWarning((object)("AddItem multiply failed: " + ex.Message)); } } } [HarmonyPatch(typeof(ZNetScene), "RemoveObjects")] private static class ZNetSceneRemoveObjectsGuard { private static float _nextLog; private static readonly List<ZDO> GhostKeys = new List<ZDO>(16); private static Exception Finalizer(Exception __exception) { if (__exception == null) { return null; } try { ZNetScene instance = ZNetScene.instance; Dictionary<ZDO, ZNetView> dictionary = (((Object)(object)instance != (Object)null && _znetInstances != null) ? (_znetInstances.GetValue(instance) as Dictionary<ZDO, ZNetView>) : null); if (dictionary != null && dictionary.Count > 0) { GhostKeys.Clear(); foreach (KeyValuePair<ZDO, ZNetView> item in dictionary) { if ((Object)(object)item.Value == (Object)null || item.Value.GetZDO() == null) { GhostKeys.Add(item.Key); } } int num = 0; foreach (ZDO ghostKey in GhostKeys) { if (dictionary.TryGetValue(ghostKey, out var value) && (!((Object)(object)value != (Object)null) || value.GetZDO() == null)) { dictionary.Remove(ghostKey); if ((Object)(object)value != (Object)null && (Object)(object)((Component)value).gameObject != (Object)null) { Object.Destroy((Object)(object)((Component)value).gameObject); } num++; } } if (num > 0 && Time.time > _nextLog) { _nextLog = Time.time + 30f; _log.LogWarning((object)($"ZNetScene: fixed net race, removed {num} ghost instance(s) " + "(null ZDO). Exception suppressed, object cycle continues.")); } } } catch (Exception ex) { _log.LogWarning((object)("ZNetScene ghost cleanup failed: " + ex.Message)); } return null; } } public const string PluginGuid = "user.swiftpickup"; public const string PluginName = "SwiftPickup"; public const string PluginVersion = "1.3.3"; private static ConfigEntry<bool> _modEnabled; private static ConfigEntry<float> _pickupRange; private static ConfigEntry<float> _scanInterval; private static ConfigEntry<int> _maxPickupsPerScan; private static ConfigEntry<float> _magnetSpeed; private static ConfigEntry<bool> _skipPickupEffects; private static ConfigEntry<bool> _showPickupMessages; private static ConfigEntry<string> _ignoreList; private static ConfigEntry<KeyboardShortcut> _toggleKey; private static ConfigEntry<int> _lootMultiplier; private static ConfigEntry<bool> _mergeDrops; private static ConfigEntry<float> _mergeRadius; private static ConfigEntry<int> _mergeMinCount; private static ConfigEntry<int> _maxMergesPerScan; private static ManualLogSource _log; private static FieldInfo _maskField; private static MethodInfo _itemDropSave; private static FieldInfo _humanoidInventory; private static FieldInfo _znetInstances; private static readonly Collider[] Buffer = (Collider[])(object)new Collider[256]; private static HashSet<string> _ignore = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static bool _bufferWarned; private static readonly List<Candidate> Candidates = new List<Candidate>(64); private static float _nextScan; private static readonly List<ItemDrop> MagnetItems = new List<ItemDrop>(32); private static bool _handMultiplyPending; private static int _handMultiplyTarget; private static readonly Dictionary<string, List<ItemDrop>> Groups = new Dictionary<string, List<ItemDrop>>(32); private void Awake() { //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Unknown result type (might be due to invalid IL or missing references) _log = ((BaseUnityPlugin)this).Logger; _modEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Включён ли автоподбор при старте. Клавиша ниже переключает только подбор; множитель и кучки настраиваются отдельно."); _pickupRange = ((BaseUnityPlugin)this).Config.Bind<float>("General", "PickupRange", 3.5f, "Радиус автоподбора в метрах (игровых юнитах). Ваниль ~2."); _scanInterval = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ScanIntervalSeconds", 0.15f, "Как часто (сек) сканировать окружение на предметы. Больше значение = меньше нагрузки."); _maxPickupsPerScan = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxPickupsPerScan", 2, "Сколько предметов максимум подбирать за один скан (распределяет нагрузку по кадрам). 0 = без ограничения."); _magnetSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MagnetSpeed", 15f, "Скорость магнитного притяжения предметов к игроку (как в ванили, 15)."); _skipPickupEffects = ((BaseUnityPlugin)this).Config.Bind<bool>("Performance", "SkipPickupEffects", true, "Не создавать частицы/эффекты при подборе. Главная экономия кадров при массовом подборе."); _showPickupMessages = ((BaseUnityPlugin)this).Config.Bind<bool>("Performance", "ShowPickupMessages", true, "Показывать всплывающие сообщения '+Древесина xN'. Очень дёшево, но можно отключить."); _lootMultiplier = ((BaseUnityPlugin)this).Config.Bind<int>("Loot", "LootMultiplier", 5, "Во сколько раз умножать количество предметов при подборе (авто и ручной). На земле лежит ванильное число объектов — умножение происходит в момент подбора. 1 = без умножения."); _ignoreList = ((BaseUnityPlugin)this).Config.Bind<string>("General", "IgnoreItems", "", "Имена префабов предметов (через запятую), которые НЕ автоподбирать. Пример: Wood,Stone."); _toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "ToggleKey", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Клавиша переключения автоподбора в игре."); _mergeDrops = ((BaseUnityPlugin)this).Config.Bind<bool>("Merge", "MergeNearbyDrops", true, "Склеивать одинаковые предметы, лежащие рядом, в один стак (как при выбросе игроком). Резко снижает количество объектов на земле."); _mergeRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Merge", "MergeRadius", 3f, "Радиус (м), внутри которого одинаковые предметы считаются 'лежащими рядом' и склеиваются."); _mergeMinCount = ((BaseUnityPlugin)this).Config.Bind<int>("Merge", "MergeMinCount", 2, "Минимум одинаковых предметов рядом, чтобы запустить склейку в кучку."); _maxMergesPerScan = ((BaseUnityPlugin)this).Config.Bind<int>("Merge", "MaxMergesPerScan", 2, "Максимум операций склейки за один скан (нагрузка распределяется по кадрам)."); _maskField = AccessTools.Field(typeof(Player), "m_autoPickupMask"); _itemDropSave = AccessTools.Method(typeof(ItemDrop), "Save", (Type[])null, (Type[])null); _znetInstances = AccessTools.Field(typeof(ZNetScene), "m_instances"); _humanoidInventory = AccessTools.Field(typeof(Humanoid), "m_inventory"); ParseIgnoreList(); new Harmony("user.swiftpickup").PatchAll(typeof(SwiftPickupPlugin).Assembly); _log.LogInfo((object)string.Format("{0} v{1} загружен. Клавиша {2} переключает только автоподбор; множитель и кучки настроены в конфиге.", "SwiftPickup", "1.3.3", _toggleKey.Value)); } private static void ParseIgnoreList() { _ignore = new HashSet<string>(from s in (_ignoreList.Value ?? "").Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries) select s.Trim() into s where s.Length > 0 select s, StringComparer.OrdinalIgnoreCase); } private static bool IsPickupEnabled() { return _modEnabled.Value; } private static int GetMask(Player player) { try { if (_maskField != null && _maskField.GetValue(player) is int num && num != 0) { return num; } } catch { } return LayerMask.GetMask(new string[1] { "item" }); } private static bool IsIgnored(ItemData data) { if (_ignore.Count == 0 || data == null) { return false; } if ((Object)(object)data.m_dropPrefab != (Object)null && _ignore.Contains(((Object)data.m_dropPrefab).name)) { return true; } if (data.m_shared != null && _ignore.Contains(data.m_shared.m_name)) { return true; } return false; } private static void HandleScan(Player player, float dt) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: 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_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) bool flag = IsPickupEnabled(); float num = (flag ? _pickupRange.Value : 0f); if (_mergeDrops.Value) { num = Mathf.Max(num, _mergeRadius.Value); } if (num <= 0f) { return; } float time = Time.time; if (time - _nextScan < _scanInterval.Value) { return; } _nextScan = time + _scanInterval.Value; Vector3 val = ((Component)player).transform.position + Vector3.up; int mask = GetMask(player); int num2; try { num2 = Physics.OverlapSphereNonAlloc(val, num, Buffer, mask); } catch (Exception arg) { _log.LogError((object)$"OverlapSphere failed: {arg}"); return; } if (num2 >= Buffer.Length && !_bufferWarned) { _bufferWarned = true; _log.LogWarning((object)"Буфер переполнен (256 коллайдеров), часть предметов не сканируется."); } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return; } Candidates.Clear(); Groups.Clear(); for (int i = 0; i < num2; i++) { Collider val2 = Buffer[i]; if ((Object)(object)val2 == (Object)null) { continue; } Rigidbody attachedRigidbody = val2.attachedRigidbody; if ((Object)(object)attachedRigidbody == (Object)null) { continue; } ItemDrop component = ((Component)attachedRigidbody).GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null) { FloatingTerrainDummy component2 = ((Component)attachedRigidbody).GetComponent<FloatingTerrainDummy>(); if ((Object)(object)component2 != (Object)null && (Object)(object)component2.m_parent != (Object)null) { component = ((Component)component2.m_parent).gameObject.GetComponent<ItemDrop>(); } } if ((Object)(object)component == (Object)null || !component.m_autoPickup || component.IsPiece()) { continue; } SharedData val3 = ((component.m_itemData != null) ? component.m_itemData.m_shared : null); if (val3 == null || ((Humanoid)player).HaveUniqueKey(val3.m_name) || IsIgnored(component.m_itemData)) { continue; } ZNetView component3 = ((Component)component).GetComponent<ZNetView>(); if ((Object)(object)component3 == (Object)null || !component3.IsValid()) { continue; } if (!component.CanPickup(true)) { component.RequestOwn(); } else { if (component.InTar()) { continue; } if (_mergeDrops.Value) { string key = (((Object)(object)component.m_itemData.m_dropPrefab != (Object)null) ? ((Object)component.m_itemData.m_dropPrefab).name : component.m_itemData.m_shared.m_name); if (!Groups.TryGetValue(key, out var value)) { value = new List<ItemDrop>(); Groups[key] = value; } value.Add(component); } if (!flag) { continue; } float num3 = Vector3.Distance(((Component)component).transform.position, val); if (!(num3 > _pickupRange.Value)) { if (num3 < 0.3f) { Candidates.Add(new Candidate { Drop = component, Distance = num3 }); } else if (!MagnetItems.Contains(component)) { MagnetItems.Add(component); } } } } if (_mergeDrops.Value) { TryMergeNearby(); } if (!flag || Candidates.Count <= 0) { return; } if (Candidates.Count > 1) { Candidates.Sort((Candidate a, Candidate b) => a.Distance.CompareTo(b.Distance)); } int num4 = ((_maxPickupsPerScan.Value <= 0) ? Candidates.Count : _maxPickupsPerScan.Value); int num5 = 0; for (int num6 = 0; num6 < Candidates.Count; num6++) { if (num5 >= num4) { break; } ItemDrop drop = Candidates[num6].Drop; if (!((Object)(object)drop == (Object)null) && PickupOne(player, inventory, drop)) { num5++; } } } private static void MoveMagnets(Player player) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) Inventory inventory = ((Humanoid)player).GetInventory(); Vector3 val = ((Component)player).transform.position + Vector3.up; float deltaTime = Time.deltaTime; int num = 1; for (int num2 = MagnetItems.Count - 1; num2 >= 0; num2--) { ItemDrop val2 = MagnetItems[num2]; if ((Object)(object)val2 == (Object)null) { MagnetItems.RemoveAt(num2); } else { float num3 = Vector3.Distance(((Component)val2).transform.position, val); if (num3 > _pickupRange.Value * 1.5f) { MagnetItems.RemoveAt(num2); } else if (num3 < 0.3f) { if (num > 0 && inventory != null && PickupOne(player, inventory, val2)) { num--; MagnetItems.RemoveAt(num2); } else if (num > 0) { MagnetItems.RemoveAt(num2); } } else { Vector3 val3 = val - ((Component)val2).transform.position; Vector3 normalized = ((Vector3)(ref val3)).normalized; float num4 = Mathf.Min(_magnetSpeed.Value * deltaTime, num3 - 0.25f); Transform transform = ((Component)val2).transform; transform.position += normalized * num4; } } } } private static void TryMergeNearby() { int num = _maxMergesPerScan.Value; foreach (KeyValuePair<string, List<ItemDrop>> group in Groups) { if (num <= 0) { break; } List<ItemDrop> value = group.Value; if (value == null || value.Count < _mergeMinCount.Value) { continue; } ItemDrop target = value[0]; int num2 = value.Count - 1; while (num2 >= 1 && num > 0) { ItemDrop val = value[num2]; if (!((Object)(object)val == (Object)null)) { if (MergePair(target, val)) { value.RemoveAt(num2); num--; } else if (num2 > 1) { target = val; } } num2--; } } } private static bool MergePair(ItemDrop target, ItemDrop source) { try { ZNetView component = ((Component)target).GetComponent<ZNetView>(); ZNetView component2 = ((Component)source).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null || !component.IsValid() || !component2.IsValid()) { return false; } if (component.GetZDO() == null || component2.GetZDO() == null) { return false; } if (!component.IsOwner() || !component2.IsOwner()) { return false; } target.Load(); source.Load(); ItemData itemData = target.m_itemData; ItemData itemData2 = source.m_itemData; if (itemData == null || itemData2 == null || itemData.m_shared == null || itemData2.m_shared == null) { return false; } if (itemData.m_shared.m_name != itemData2.m_shared.m_name) { return false; } if (itemData.m_variant != itemData2.m_variant || itemData.m_quality != itemData2.m_quality) { return false; } int maxStackSize = itemData.m_shared.m_maxStackSize; if (maxStackSize <= 1) { return false; } int num = Mathf.Min(maxStackSize - itemData.m_stack, itemData2.m_stack); if (num <= 0) { return false; } itemData.m_stack += num; itemData2.m_stack -= num; _itemDropSave?.Invoke(target, null); if (itemData2.m_stack <= 0) { if ((Object)(object)ZNetScene.instance != (Object)null) { ZNetScene.instance.Destroy(((Component)source).gameObject); } return true; } _itemDropSave?.Invoke(source, null); return false; } catch (Exception ex) { _log.LogWarning((object)("Merge failed: " + ex.Message)); return false; } } private static bool PickupOne(Player player, Inventory inventory, ItemDrop drop) { //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) if (!drop.CanPickup(true)) { return false; } ZNetView component = ((Component)drop).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid() || component.GetZDO() == null) { return false; } drop.Load(); ItemData itemData = drop.m_itemData; if (itemData == null || itemData.m_shared == null) { return false; } if (itemData.m_shared.m_questItem && ((Humanoid)player).HaveUniqueKey(itemData.m_shared.m_name)) { return false; } if (inventory.ContainsItem(itemData)) { return false; } int stack = itemData.m_stack; int value = _lootMultiplier.Value; if (value > 1 && stack > 0) { itemData.m_stack = stack * value; } int stack2 = itemData.m_stack; if (!inventory.CanAddItem(itemData, -1)) { itemData.m_stack = stack; return false; } if (itemData.GetWeight(-1) + inventory.GetTotalWeight() > player.GetMaxCarryWeight()) { itemData.m_stack = stack; return false; } if (!inventory.AddItem(itemData)) { return false; } if (itemData.m_shared.m_questItem) { ((Humanoid)player).AddUniqueKey(itemData.m_shared.m_name); } if ((Object)(object)ZNetScene.instance != (Object)null && component.IsOwner() && component.GetZDO() != null) { ZNetScene.instance.Destroy(((Component)drop).gameObject); } if (!_skipPickupEffects.Value) { try { if ((Object)(object)player != (Object)null && ((Humanoid)player).m_pickupEffects != null) { ((Humanoid)player).m_pickupEffects.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, ((Character)player).GetZDOID()); } } catch (Exception ex) { _log.LogWarning((object)("Effect create failed: " + ex.Message)); } } if (_showPickupMessages.Value) { ((Character)player).ShowPickupMessage(itemData, stack2 - itemData.m_stack); } return true; } }