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 BetterOrganisationBetrValheimCompat v1.0.2
plugins/BetterOrganisationBetrValheimCompat.dll
Decompiled 10 hours agousing System; using System.Collections; 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.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("BetterOrganisationBetrValheimCompat")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("BetterOrganisationBetrValheimCompat")] [assembly: AssemblyTitle("BetterOrganisationBetrValheimCompat")] [assembly: AssemblyVersion("1.0.0.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 BetterOrganisationBetrValheimCompat { [BepInPlugin("com.phoenixf.betterorganisation.betrvalheimcompat", "Better Organisation - BetrValheim Drawers Compat", "0.8.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string ModGuid = "com.phoenixf.betterorganisation.betrvalheimcompat"; public const string ModName = "Better Organisation - BetrValheim Drawers Compat"; public const string ModVersion = "0.8.3"; internal static ManualLogSource Log; private Harmony _harmony; private void Awake() { Log = ((BaseUnityPlugin)this).Logger; } private void Start() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_0069: Expected O, but got Unknown //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Expected O, but got Unknown try { if (!Compat.Bind()) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Compatibility bridge not enabled; Better Organisation is untouched."); return; } _harmony = new Harmony("com.phoenixf.betterorganisation.betrvalheimcompat"); _harmony.Patch((MethodBase)Compat.StashAllMethod, new HarmonyMethod(typeof(Compat), "StashAllPrefix", (Type[])null), new HarmonyMethod(typeof(Compat), "StashAllPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); foreach (MethodInfo item in from m in typeof(MessageHud).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where m.Name == "ShowMessage" && m.GetParameters().Any((ParameterInfo p) => p.ParameterType == typeof(string)) select m) { _harmony.Patch((MethodBase)item, new HarmonyMethod(typeof(Compat), "MessageHudShowMessagePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"Better Organisation - BetrValheim Drawers Compat 0.8.3 active: matching BetrValheim drawers first, then Better Organisation chests."); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Failed to initialise compatibility bridge: " + ex)); } } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal static class Compat { internal static MethodInfo StashAllMethod; private static Assembly _boAssembly; private static Assembly _betrAssembly; private static Type _drawerType; private static MethodInfo _getStashableItems; private static MethodInfo _isFavorite; private static FieldInfo _boRangeField; private static PropertyInfo _configEntryValue; private static PropertyInfo _drawerItemName; private static PropertyInfo _drawerFreeSpace; private static MethodInfo _drawerDeposit; private static bool _inside; private static bool _warnedDeposit; private static int _inventoryCountBefore; private static int _drawerMoved; private static bool _stashCallActive; private static readonly HashSet<string> _drawerDestinations = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private const BindingFlags AllStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private const BindingFlags AllInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; internal static bool Bind() { _boAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "ChestCategories", StringComparison.OrdinalIgnoreCase)); _betrAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "BetrValheim", StringComparison.OrdinalIgnoreCase)); if (_boAssembly == null) { Plugin.Log.LogError((object)"ChestCategories.dll (Better Organisation) is not loaded."); return false; } if (_betrAssembly == null) { Plugin.Log.LogError((object)"BetrValheim.dll is not loaded."); return false; } StashAllMethod = SafeTypes(_boAssembly).FirstOrDefault((Type t) => t.Name == "StashService")?.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "StashAll" && m.GetParameters().Length == 0); _getStashableItems = SafeTypes(_boAssembly).FirstOrDefault((Type t) => t.Name == "InventoryRules")?.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "GetStashableItems" && m.GetParameters().Length == 1 && typeof(Player).IsAssignableFrom(m.GetParameters()[0].ParameterType)); _isFavorite = SafeTypes(_boAssembly).FirstOrDefault((Type t) => t.Name == "ItemFavorites")?.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "IsFavorite" && m.ReturnType == typeof(bool) && m.GetParameters().Length >= 1 && typeof(ItemData).IsAssignableFrom(m.GetParameters()[0].ParameterType)); _boRangeField = SafeTypes(_boAssembly).FirstOrDefault((Type t) => t.Name == "ModConfig")?.GetField("Range", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (_boRangeField != null) { _configEntryValue = _boRangeField.GetValue(null)?.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public); } _drawerType = _betrAssembly.GetType("BetrValheim.Drawers.Drawer", throwOnError: false); if (_drawerType != null) { _drawerItemName = _drawerType.GetProperty("ItemName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _drawerFreeSpace = _drawerType.GetProperty("FreeSpace", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _drawerDeposit = _drawerType.GetMethod("Deposit", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2] { typeof(string), typeof(int) }, null); } if (StashAllMethod == null) { Plugin.Log.LogError((object)"Could not find Better Organisation StashService.StashAll()."); return false; } if (_getStashableItems == null) { Plugin.Log.LogError((object)"Could not find Better Organisation InventoryRules.GetStashableItems(Player)."); return false; } if (_drawerType == null || _drawerItemName == null || _drawerDeposit == null) { Plugin.Log.LogError((object)"Could not bind BetrValheim Drawer.ItemName / Drawer.Deposit(string,int). Better Organisation is untouched."); return false; } Plugin.Log.LogInfo((object)("Bound Better Organisation: " + StashAllMethod.DeclaringType.FullName + ".StashAll()")); Plugin.Log.LogInfo((object)("Bound BO stash filter: " + Describe(_getStashableItems))); Plugin.Log.LogInfo((object)("Bound BO favourite check: " + Describe(_isFavorite))); Plugin.Log.LogInfo((object)("Bound BetrValheim drawer: ItemName=" + _drawerItemName.Name + ", FreeSpace=" + (_drawerFreeSpace?.Name ?? "<not needed>") + ", Deposit=" + Describe(_drawerDeposit))); return true; } internal static bool StashAllPrefix() { //IL_006c: Unknown result type (might be due to invalid IL or missing references) if (_inside) { return true; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return true; } _inventoryCountBefore = CountInventoryItems(((Humanoid)localPlayer).GetInventory()); _drawerMoved = 0; _drawerDestinations.Clear(); _stashCallActive = true; _inside = true; try { List<ItemData> stashableItems = GetStashableItems(localPlayer); if (stashableItems == null || stashableItems.Count == 0) { return true; } float boRange = GetBoRange(); List<object> list = FindNearbyDrawers(((Component)localPlayer).transform.position, boRange); if (list.Count == 0) { return true; } Inventory inventory = ((Humanoid)localPlayer).GetInventory(); int num = 0; int num2 = 0; ItemData[] array = stashableItems.ToArray(); foreach (ItemData val in array) { if (val == null || val.m_stack <= 0 || (Object)(object)val.m_dropPrefab == (Object)null || IsFavorite(val)) { continue; } string prefabName = GetPrefabName(val); if (string.IsNullOrEmpty(prefabName)) { continue; } string sharedName = val.m_shared?.m_name; foreach (object item in list) { if (val.m_stack <= 0) { break; } string assigned = ReadString(_drawerItemName, item); if (!MatchesAssignedDrawer(assigned, prefabName, sharedName)) { continue; } int num3 = val.m_stack; if (_drawerFreeSpace != null) { int num4 = ReadInt(_drawerFreeSpace, item); if (num4 <= 0) { continue; } num3 = Math.Min(num3, num4); } int num5 = Deposit(item, prefabName, num3); if (num5 > 0) { num5 = Math.Min(num5, val.m_stack); inventory.RemoveItem(val, num5); num += num5; num2++; _drawerMoved += num5; _drawerDestinations.Add(FriendlyDrawerName(assigned, prefabName, sharedName)); } } } if (num > 0) { Plugin.Log.LogInfo((object)$"Drawer-first stash moved {num} item(s) in {num2} transfer(s); Better Organisation will handle the remainder."); List<ItemData> stashableItems2 = GetStashableItems(localPlayer); if (stashableItems2 == null || stashableItems2.Count == 0) { Plugin.Log.LogInfo((object)"All stashable items were handled by matching drawers; skipping empty Better Organisation chest pass."); return false; } } return true; } catch (Exception e) { Plugin.Log.LogError((object)("Drawer-first stash failed; falling through to unmodified Better Organisation StashAll: " + RootMessage(e))); return true; } finally { _inside = false; } } internal static bool MessageHudShowMessagePrefix(object[] __args) { if (!_stashCallActive || _drawerMoved <= 0 || __args == null) { return true; } for (int i = 0; i < __args.Length; i++) { if (__args[i] is string text && !string.IsNullOrEmpty(text) && text.IndexOf("No chest nearby accepts these items", StringComparison.OrdinalIgnoreCase) >= 0) { Plugin.Log.LogInfo((object)"Suppressed Better Organisation chest-only failure message because drawers accepted items."); return false; } } return true; } internal static void StashAllPostfix() { try { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && _drawerMoved > 0) { int num = CountInventoryItems(((Humanoid)localPlayer).GetInventory()); int num2 = Math.Max(0, _inventoryCountBefore - num); int num3 = Math.Max(0, num2 - _drawerMoved); if (num2 > 0 && !((Object)(object)MessageHud.instance == (Object)null)) { string text = ((num3 > 0) ? "Drawers/Chests" : ((_drawerDestinations.Count != 1) ? (_drawerDestinations.Count + " drawers") : _drawerDestinations.First())); string text2 = string.Format("Stashed {0} item{1} into {2}", num2, (num2 == 1) ? "" : "s", text); ((Character)localPlayer).Message((MessageType)2, text2, 0, (Sprite)null, false); Plugin.Log.LogInfo((object)$"Combined stash summary: total={num2}, drawers={_drawerMoved}, chests={num3}, destination={text}"); } } } catch (Exception e) { Plugin.Log.LogWarning((object)("Could not show combined BO/drawer stash summary: " + RootMessage(e))); } finally { _stashCallActive = false; _inventoryCountBefore = 0; _drawerMoved = 0; _drawerDestinations.Clear(); } } private static int CountInventoryItems(Inventory inventory) { if (inventory == null) { return 0; } int num = 0; try { foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem != null && allItem.m_stack > 0) { num += allItem.m_stack; } } } catch { } return num; } private static string FriendlyDrawerName(string assigned, string prefabName, string sharedName) { if (!string.IsNullOrEmpty(sharedName) && !sharedName.StartsWith("$", StringComparison.Ordinal)) { return sharedName; } if (!string.IsNullOrEmpty(assigned)) { return CleanPrefabName(assigned); } return CleanPrefabName(prefabName); } private static string CleanPrefabName(string name) { if (string.IsNullOrEmpty(name)) { return "Drawer"; } if (name.EndsWith("(Clone)", StringComparison.Ordinal)) { name = name.Substring(0, name.Length - "(Clone)".Length); } return name.Replace("_", " "); } private static bool IsFavorite(ItemData item) { if (item == null) { return false; } if (_isFavorite == null) { return false; } try { ParameterInfo[] parameters = _isFavorite.GetParameters(); object[] array = new object[parameters.Length]; array[0] = item; for (int i = 1; i < parameters.Length; i++) { if (parameters[i].HasDefaultValue) { array[i] = parameters[i].DefaultValue; } else if (parameters[i].ParameterType.IsValueType) { array[i] = Activator.CreateInstance(parameters[i].ParameterType); } else { array[i] = null; } } object obj = _isFavorite.Invoke(null, array); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } catch (Exception e) { Plugin.Log.LogWarning((object)("Could not query Better Organisation favourite state; skipping this item for drawer stash: " + RootMessage(e))); return true; } } private static List<ItemData> GetStashableItems(Player player) { try { if (!(_getStashableItems.Invoke(null, new object[1] { player }) is IEnumerable enumerable)) { return null; } List<ItemData> list = new List<ItemData>(); foreach (object item in enumerable) { ItemData val = (ItemData)((item is ItemData) ? item : null); if (val != null && val.m_stack > 0) { list.Add(val); } } return list; } catch (Exception e) { Plugin.Log.LogWarning((object)("Could not obtain Better Organisation stashable items: " + RootMessage(e))); return null; } } private static float GetBoRange() { try { object obj = _boRangeField?.GetValue(null); object obj2 = _configEntryValue?.GetValue(obj, null); if (obj2 is float result) { return result; } if (obj2 is double num) { return (float)num; } if (obj2 is int num2) { return num2; } } catch { } return 15f; } private static List<object> FindNearbyDrawers(Vector3 origin, float range) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: 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_0068: 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_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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) float num = range * range; List<object> list = new List<object>(); Object[] array = Resources.FindObjectsOfTypeAll(_drawerType); foreach (Object val in array) { if (val == (Object)null) { continue; } Component val2 = (Component)(object)((val is Component) ? val : null); if ((Object)(object)val2 == (Object)null || (Object)(object)val2.gameObject == (Object)null) { continue; } Scene scene = val2.gameObject.scene; if (((Scene)(ref scene)).IsValid() && val2.gameObject.activeInHierarchy) { Vector3 val3 = val2.transform.position - origin; if (((Vector3)(ref val3)).sqrMagnitude <= num) { list.Add(val); } } } list.Sort(delegate(object a, object b) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown //IL_0012: Unknown result type (might be due to invalid IL or missing references) //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_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) Component val4 = (Component)a; Component val5 = (Component)b; Vector3 val6 = val4.transform.position - origin; float sqrMagnitude = ((Vector3)(ref val6)).sqrMagnitude; val6 = val5.transform.position - origin; float sqrMagnitude2 = ((Vector3)(ref val6)).sqrMagnitude; return sqrMagnitude.CompareTo(sqrMagnitude2); }); return list; } private static int Deposit(object drawer, string prefabName, int amount) { if (drawer == null || amount <= 0) { return 0; } try { if (_drawerDeposit.Invoke(drawer, new object[2] { prefabName, amount }) is int val) { return Math.Max(0, Math.Min(amount, val)); } return 0; } catch (Exception e) { if (!_warnedDeposit) { _warnedDeposit = true; Plugin.Log.LogWarning((object)("Drawer.Deposit failed: " + RootMessage(e))); } return 0; } } private static bool MatchesAssignedDrawer(string assigned, string prefabName, string sharedName) { if (string.IsNullOrEmpty(assigned)) { return false; } if (string.Equals(assigned, prefabName, StringComparison.OrdinalIgnoreCase)) { return true; } if (!string.IsNullOrEmpty(sharedName) && string.Equals(assigned, sharedName, StringComparison.OrdinalIgnoreCase)) { return true; } return false; } private static string GetPrefabName(ItemData item) { try { string text = ((Object)item.m_dropPrefab).name; if (text.EndsWith("(Clone)", StringComparison.Ordinal)) { text = text.Substring(0, text.Length - "(Clone)".Length); } return text; } catch { return null; } } private static string ReadString(PropertyInfo property, object target) { try { return property?.GetValue(target, null) as string; } catch { return null; } } private static int ReadInt(PropertyInfo property, object target) { try { return (property?.GetValue(target, null) is int num) ? num : 0; } catch { return 0; } } private static string RootMessage(Exception e) { while (e is TargetInvocationException { InnerException: not null } ex) { e = ex.InnerException; } return e?.Message ?? "<unknown>"; } private static string Describe(MethodInfo m) { if (m == null) { return "<not found>"; } string text = string.Join(", ", from p in m.GetParameters() select p.ParameterType.Name + " " + p.Name); return m.DeclaringType.FullName + "." + m.Name + "(" + text + ") -> " + m.ReturnType.Name; } private static Type[] SafeTypes(Assembly assembly) { try { return assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { return ex.Types.Where((Type t) => t != null).ToArray(); } } } }