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 Slot Fixer v1.1.1
SlotFixer.dll
Decompiled a week agousing System; using System.Collections; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using UnityEngine; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace SlotFixer; [BepInPlugin("com.burks.slotfixer", "Slot Fixer", "1.1.1")] public class SlotFixerPlugin : BaseUnityPlugin { private const BindingFlags Any = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private const BindingFlags AnyStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private ConfigEntry<bool> _enabled; private ConfigEntry<float> _interval; private ConfigEntry<int> _mainRows; private ConfigEntry<bool> _guardPickups; private ConfigEntry<bool> _alwaysBlock; internal static SlotFixerPlugin Instance; private bool _patched; private Type _itemDataType; private MethodInfo _message; private Type _msgType; private bool _ready; private int _setupTries; private bool _failed; private float _nextCheck; private FieldInfo _localPlayer; private FieldInfo _humanoidInv; private FieldInfo _invList; private FieldInfo _invWidth; private FieldInfo _invHeight; private MethodInfo _invChanged; private FieldInfo _gridPos; private FieldInfo _equipped; private FieldInfo _shared; private FieldInfo _sharedName; private Type _vecType; private FieldInfo _vecX; private FieldInfo _vecY; private readonly HashSet<object> _warnedNoRoom = new HashSet<object>(); private void Awake() { _enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Fix items that share an inventory slot or sit outside the inventory grid."); _interval = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Check interval seconds", 1.5f, "How often the inventory is checked."); _mainRows = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Main grid rows", 4, "Rows counted as the regular inventory. Moved items only go into these rows, so equipment, quick slot and quiver rows added by other mods are never filled."); _guardPickups = ((BaseUnityPlugin)this).Config.Bind<bool>("Pickups", "Guard pickups", true, "Check every item you pick up. If it lands on a slot that is already taken, put it in a free slot instead, or leave it on the ground when there is no free slot."); _alwaysBlock = ((BaseUnityPlugin)this).Config.Bind<bool>("Pickups", "Always block conflicting pickups", false, "When an item would land on a taken slot, always leave it on the ground, even if a free slot exists."); Instance = this; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Slot Fixer 1.1.1 loaded."); } private void Update() { if (_failed || !_enabled.Value) { return; } float time = Time.time; if (time < _nextCheck) { return; } _nextCheck = time + Math.Max(0.25f, _interval.Value); try { if (_ready || Setup()) { Check(); } } catch (Exception ex) { _failed = true; ((BaseUnityPlugin)this).Logger.LogWarning((object)("Slot Fixer stopped after an error: " + ex)); } } private bool Setup() { Type type = FindType("Player"); Type type2 = FindType("Humanoid"); Type type3 = FindType("Inventory"); Type type4 = FindType("ItemDrop+ItemData"); if (type == null || type2 == null || type3 == null || type4 == null) { if (++_setupTries == 40) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Slot Fixer is still waiting for game types (Player=" + (type != null) + ", Humanoid=" + (type2 != null) + ", Inventory=" + (type3 != null) + ", ItemData=" + (type4 != null) + "). It will keep trying.")); } return false; } _localPlayer = type.GetField("m_localPlayer", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _humanoidInv = type2.GetField("m_inventory", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _invList = type3.GetField("m_inventory", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _invWidth = type3.GetField("m_width", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _invHeight = type3.GetField("m_height", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _invChanged = type3.GetMethod("Changed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); _gridPos = type4.GetField("m_gridPos", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _vecType = ((!(_gridPos != null)) ? null : _gridPos.FieldType); _equipped = type4.GetField("m_equipped", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _shared = type4.GetField("m_shared", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _sharedName = ((!(_shared != null)) ? null : _shared.FieldType.GetField("m_name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)); _vecX = ((!(_vecType != null)) ? null : _vecType.GetField("x", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)); _vecY = ((!(_vecType != null)) ? null : _vecType.GetField("y", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)); if (_localPlayer == null || _humanoidInv == null || _invList == null || _invWidth == null || _invHeight == null || _gridPos == null || _vecX == null || _vecY == null) { _failed = true; ((BaseUnityPlugin)this).Logger.LogWarning((object)("Slot Fixer could not find the inventory fields it needs; it is turned off for this session. localPlayer=" + (_localPlayer != null) + " humanoidInv=" + (_humanoidInv != null) + " list=" + (_invList != null) + " width=" + (_invWidth != null) + " height=" + (_invHeight != null) + " gridPos=" + (_gridPos != null) + " x=" + (_vecX != null) + " y=" + (_vecY != null))); return false; } _itemDataType = type4; _msgType = FindType("MessageHud+MessageType"); Type type5 = FindType("Character"); if (type5 != null && _msgType != null) { MethodInfo[] methods = type5.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == "Message" && methodInfo.GetParameters().Length >= 2 && methodInfo.GetParameters()[0].ParameterType == _msgType && methodInfo.GetParameters()[1].ParameterType == typeof(string)) { _message = methodInfo; break; } } } _ready = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Slot Fixer is watching the inventory (slot type " + _vecType.FullName + " from " + _vecType.Assembly.GetName().Name + ").")); PatchPickup(type3); return true; } private void PatchPickup(Type inventory) { if (_patched || !_guardPickups.Value) { return; } _patched = true; try { MethodInfo method = inventory.GetMethod("AddItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { _itemDataType }, null); Type type = null; Type type2 = null; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (!(assembly.GetName().Name != "0Harmony")) { type = assembly.GetType("HarmonyLib.Harmony", throwOnError: false); type2 = assembly.GetType("HarmonyLib.HarmonyMethod", throwOnError: false); break; } } if (method == null || type == null || type2 == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Slot Fixer could not hook pickups (Harmony or Inventory.AddItem not found). Overlaps are still fixed by the periodic check."); return; } object obj = Activator.CreateInstance(type, "com.burks.slotfixer"); ConstructorInfo constructor = type2.GetConstructor(new Type[1] { typeof(MethodInfo) }); object obj2 = constructor.Invoke(new object[1] { typeof(SlotFixerPlugin).GetMethod("AddItemPrefix", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) }); object obj3 = constructor.Invoke(new object[1] { typeof(SlotFixerPlugin).GetMethod("AddItemPostfix", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) }); MethodInfo methodInfo = null; MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public); foreach (MethodInfo methodInfo2 in methods) { if (methodInfo2.Name == "Patch" && methodInfo2.GetParameters().Length >= 3 && typeof(MethodBase).IsAssignableFrom(methodInfo2.GetParameters()[0].ParameterType)) { methodInfo = methodInfo2; break; } } if (methodInfo == null) { throw new MissingMethodException("Harmony.Patch"); } object[] array = new object[methodInfo.GetParameters().Length]; array[0] = method; array[1] = obj2; array[2] = obj3; methodInfo.Invoke(obj, array); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Pickup guard is on."); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Slot Fixer could not hook pickups: " + ex.Message + ". Overlaps are still fixed by the periodic check.")); } } private static void AddItemPrefix(object item, out int __state) { __state = -1; try { if (Instance != null) { __state = Instance.StackOf(item); } } catch { } } private static void AddItemPostfix(object __instance, object item, ref bool __result, int __state) { try { if (__result && Instance != null) { __result = Instance.GuardAdd(__instance, item, __state); } } catch (Exception ex) { if (Instance != null) { ((BaseUnityPlugin)Instance).Logger.LogWarning((object)("Pickup guard error: " + ex.Message)); } } } private int StackOf(object item) { FieldInfo field = _itemDataType.GetField("m_stack", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); return (!(field != null)) ? (-1) : ((int)field.GetValue(item)); } internal bool GuardAdd(object inv, object item, int stackBefore) { if (!_ready || !_guardPickups.Value || item == null) { return true; } object value = _localPlayer.GetValue(null); if (value == null || inv != _humanoidInv.GetValue(value)) { return true; } if (!(_invList.GetValue(inv) is IList list) || !list.Contains(item)) { return true; } int num = (int)_invWidth.GetValue(inv); int num2 = (int)_invHeight.GetValue(inv); GetPos(item, out var x, out var y); Dictionary<long, object> dictionary = new Dictionary<long, object>(); bool flag = x < 0 || y < 0 || x >= num || y >= num2; foreach (object item2 in list) { if (item2 != null && item2 != item) { GetPos(item2, out var x2, out var y2); dictionary[Key(x2, y2)] = item2; if (x2 == x && y2 == y) { flag = true; } } } if (!flag) { return true; } int rows = Math.Max(1, Math.Min(_mainRows.Value, num2)); if (!_alwaysBlock.Value && FindFree(dictionary, num, rows, out var fx, out var fy)) { SetPos(item, fx, fy); if (_invChanged != null) { _invChanged.Invoke(inv, null); } ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Pickup of {NameOf(item)} would have overlapped slot {x},{y}; placed it in free slot {fx},{fy}."); return true; } if (stackBefore >= 0 && StackOf(item) != stackBefore) { return true; } list.Remove(item); if (_invChanged != null) { _invChanged.Invoke(inv, null); } ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Blocked pickup of {NameOf(item)}: it would have overlapped slot {x},{y}."); ShowMessage(value, "No free slot - " + NameOf(item) + " left on the ground"); return false; } private void ShowMessage(object player, string text) { if (_message == null) { return; } try { ParameterInfo[] parameters = _message.GetParameters(); object[] array = new object[parameters.Length]; array[0] = Enum.Parse(_msgType, "Center"); array[1] = text; for (int i = 2; i < parameters.Length; i++) { array[i] = ((!parameters[i].ParameterType.IsValueType) ? null : Activator.CreateInstance(parameters[i].ParameterType)); } _message.Invoke(player, array); } catch { } } private static Type FindType(string name) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); Assembly[] array = assemblies; foreach (Assembly assembly in array) { if (assembly.GetName().Name == "assembly_valheim") { Type type = assembly.GetType(name, throwOnError: false); if (type != null) { return type; } } } Assembly[] array2 = assemblies; foreach (Assembly assembly2 in array2) { string name2 = assembly2.GetName().Name; if (!(name2 != "assembly_utils") || !(name2 != "assembly_valheim")) { Type type2 = assembly2.GetType(name, throwOnError: false); if (type2 != null) { return type2; } } } return null; } private void Check() { object value = _localPlayer.GetValue(null); if (value == null) { return; } object value2 = _humanoidInv.GetValue(value); if (value2 == null || !(_invList.GetValue(value2) is IList { Count: not 0 } list)) { return; } int num = (int)_invWidth.GetValue(value2); int num2 = (int)_invHeight.GetValue(value2); int rows = Math.Max(1, Math.Min(_mainRows.Value, num2)); Dictionary<long, object> dictionary = new Dictionary<long, object>(); List<object> list2 = new List<object>(); foreach (object item in list) { if (item == null) { continue; } GetPos(item, out var x, out var y); if (x < 0 || y < 0 || x >= num || y >= num2) { list2.Add(item); continue; } long key = Key(x, y); if (!dictionary.TryGetValue(key, out var value3)) { dictionary[key] = item; } else if (IsEquipped(item) && !IsEquipped(value3)) { dictionary[key] = item; list2.Add(value3); } else { list2.Add(item); } } if (list2.Count == 0) { _warnedNoRoom.Clear(); return; } bool flag = false; foreach (object item2 in list2) { GetPos(item2, out var x2, out var y2); if (!FindFree(dictionary, num, rows, out var fx, out var fy)) { if (_warnedNoRoom.Add(item2)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)$"{NameOf(item2)} overlaps slot {x2},{y2} but the inventory has no free slot to move it to. Free up a slot and it will be moved."); } continue; } SetPos(item2, fx, fy); dictionary[Key(fx, fy)] = item2; _warnedNoRoom.Remove(item2); flag = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Moved {NameOf(item2)} from slot {x2},{y2} to free slot {fx},{fy}."); } if (flag && _invChanged != null) { _invChanged.Invoke(value2, null); } } private static bool FindFree(Dictionary<long, object> owner, int width, int rows, out int fx, out int fy) { for (int i = 0; i < rows; i++) { for (int j = 0; j < width; j++) { if (!owner.ContainsKey(Key(j, i))) { fx = j; fy = i; return true; } } } fx = (fy = -1); return false; } private static long Key(int x, int y) { return ((long)x << 32) | (uint)y; } private void GetPos(object item, out int x, out int y) { object value = _gridPos.GetValue(item); x = (int)_vecX.GetValue(value); y = (int)_vecY.GetValue(value); } private void SetPos(object item, int x, int y) { object obj = Activator.CreateInstance(_vecType); _vecX.SetValue(obj, x); _vecY.SetValue(obj, y); _gridPos.SetValue(item, obj); } private bool IsEquipped(object item) { return _equipped != null && (bool)_equipped.GetValue(item); } private string NameOf(object item) { try { object obj = ((!(_shared != null)) ? null : _shared.GetValue(item)); object obj2 = ((obj == null || !(_sharedName != null)) ? null : _sharedName.GetValue(obj)); return (obj2 as string) ?? "item"; } catch { return "item"; } } }