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 InnangardBetrValheimDrawerBridge v1.0.1
plugins/InnangardBetrValheimDrawerBridge.dll
Decompiled 5 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 API; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using InnangardBetrValheimDrawerBridge; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.SceneManagement; using kg_ItemDrawers; [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("kg_ItemDrawers")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("kg_ItemDrawers")] [assembly: AssemblyTitle("kg_ItemDrawers")] [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 API { public static class ClientSideV2 { public static List<ZNetView> AllDrawers() { return Bridge.AllViews(); } } public static class ClientSide { public static List<ZNetView> AllDrawers() { return Bridge.AllViews(); } } public static class ItemDrawers_API { public sealed class Drawer { private readonly ZNetView _znv; public readonly string Prefab; public readonly Vector3 Position; public readonly int Amount; public readonly int Quality; public Drawer(ZNetView znv) { //IL_0032: 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_006f: 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) _znv = znv; if ((Object)(object)znv == (Object)null || !znv.IsValid() || znv.GetZDO() == null) { Prefab = string.Empty; Position = Vector3.zero; Amount = 0; Quality = 1; } else { ZDO zDO = znv.GetZDO(); Prefab = zDO.GetString("Prefab", string.Empty); Position = ((Component)znv).transform.position; Amount = zDO.GetInt("Amount", 0); Quality = Math.Max(1, zDO.GetInt("Quality", 1)); } } public void Add(int amount, int quality) { Bridge.ApiAdd(_znv, Prefab, amount, quality); } public void Remove(int amount) { Bridge.ApiRemove(_znv, amount); } } public static List<Drawer> AllDrawers => (from v in Bridge.AllViews() select new Drawer(v)).ToList(); public static List<Drawer> AllDrawersInRange(Vector3 position, float range) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: 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) float num = range * range; List<ZNetView> list = Bridge.AllViews(); List<Drawer> list2 = new List<Drawer>(list.Count); foreach (ZNetView item in list) { if (!((Object)(object)item == (Object)null) && item.IsValid()) { Vector3 val = ((Component)item).transform.position - position; if (!(((Vector3)(ref val)).sqrMagnitude > num)) { list2.Add(new Drawer(item)); } } } if (Plugin.DetailedLogging.Value) { Plugin.Log.LogInfo((object)$"[RANGE] pos={position} range={range:0.0} returned={list2.Count}/{list.Count}"); } return list2; } } } namespace kg_ItemDrawers { public sealed class DrawerComponent : MonoBehaviour { public static readonly List<DrawerComponent> AllDrawers = new List<DrawerComponent>(); internal object RealDrawer; internal ZNetView View; public string CurrentPrefab => Bridge.ResolvePrefabName(Bridge.GetAssigned(RealDrawer)); public int CurrentAmount => Bridge.GetCount(RealDrawer); internal void Bind(object real, ZNetView view) { RealDrawer = real; View = view; } internal void Sync() { if (RealDrawer == null || (Object)(object)View == (Object)null || !View.IsValid()) { return; } ZDO zDO = View.GetZDO(); if (zDO != null && View.IsOwner()) { string text = CurrentPrefab ?? string.Empty; int num = ((!string.IsNullOrEmpty(text)) ? Math.Max(0, CurrentAmount) : 0); if (zDO.GetString("Prefab", string.Empty) != text) { zDO.Set("Prefab", text); } if (zDO.GetInt("Amount", 0) != num) { zDO.Set("Amount", num); } if (zDO.GetInt("Quality", 1) != 1) { zDO.Set("Quality", 1); } } } private void OnDestroy() { AllDrawers.Remove(this); RealDrawer = null; View = null; } } } namespace InnangardBetrValheimDrawerBridge { [BepInPlugin("com.phoenixf.innangard.betrvalheimdrawerbridge", "Innangard BetrValheim Drawer Bridge", "1.0.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string ModGuid = "com.phoenixf.innangard.betrvalheimdrawerbridge"; public const string ModName = "Innangard BetrValheim Drawer Bridge"; public const string ModVersion = "1.0.1"; internal static ManualLogSource Log; internal static ConfigEntry<bool> DetailedLogging; internal static ConfigEntry<bool> DeliverBeforeCollectingMore; internal static ConfigEntry<bool> SortAfterStall; internal static ConfigEntry<bool> ReduceClaimRenewals; private float _nextRefresh; private Harmony _harmony; private bool _caretakerPatchInstalled; private bool _sortRangePatchInstalled; private bool _boRulesPatchInstalled; private bool _claimRenewalPatchInstalled; private bool _boUiPatchInstalled; private bool _betterOrganisationInstalled; private void Awake() { //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; DetailedLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Detailed logging", false, "Log individual bridged drawers and API range queries. Add/remove failures are always logged."); DeliverBeforeCollectingMore = ((BaseUnityPlugin)this).Config.Bind<bool>("Caretaker", "Deliver before collecting more", true, "When a caretaker has already collected sorting cargo, deliver it physically before visiting another chest. Prevents CollectMore from trapping an undelivered load."); SortAfterStall = ((BaseUnityPlugin)this).Config.Bind<bool>("Caretaker", "Deposit when stuck near storage", true, "After a caretaker gets stuck near its sorting destination, allow a deposit within Innangard's usual interaction distance."); ReduceClaimRenewals = ((BaseUnityPlugin)this).Config.Bind<bool>("Networking", "Reduce station claim renewals", true, "Renew Innangard work claims only after one third of their lease has elapsed, reducing redundant ZDO updates."); try { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Innangard BetrValheim Drawer Bridge 1.0.1 starting."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Assembly identity is kg_ItemDrawers. Drawer storage uses the ItemDrawers API; caretaker sorting delivers first and deposits nearby only after getting stuck."); if (!Bridge.BindBetrValheim()) { ((BaseUnityPlugin)this).Logger.LogError((object)"BetrValheim drawer binding failed; bridge is inactive."); return; } Bridge.LogApiSurface(); _betterOrganisationInstalled = AppDomain.CurrentDomain.GetAssemblies().Any((Assembly a) => string.Equals(a.GetName().Name, "ChestCategories", StringComparison.OrdinalIgnoreCase)); if (_betterOrganisationInstalled) { BetterOrganisationRules.Bind(); } else { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[BO] Better Organisation not installed; all BO-specific integration is disabled. Drawer/caretaker/network patches remain active."); } try { _harmony = new Harmony("com.phoenixf.innangard.betrvalheimdrawerbridge"); _caretakerPatchInstalled = CaretakerDeliverFirst.Install(_harmony); _sortRangePatchInstalled = CaretakerSortRange.Install(_harmony); if (_betterOrganisationInstalled) { _boRulesPatchInstalled = BetterOrganisationRules.Install(_harmony); } _claimRenewalPatchInstalled = StationClaimRenewal.Install(_harmony); if (_betterOrganisationInstalled) { _boUiPatchInstalled = BetterOrganisationChestPanelCompat.Install(_harmony); } } catch (Exception e) { ((BaseUnityPlugin)this).Logger.LogError((object)("[Patch] Innangard patch failed: " + Bridge.Root(e))); } Bridge.Refresh(forceSummary: true); if (_betterOrganisationInstalled) { BetterOrganisationRules.Refresh(forceSummary: true); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"Innangard BetrValheim Drawer Bridge 1.0.1 loaded."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Expected Innangard log: compatible ItemDrawers API detected; sorting and supply access enabled."); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Bridge initialisation failed: " + ex)); } } private void Update() { if (Time.unscaledTime < _nextRefresh) { return; } _nextRefresh = Time.unscaledTime + 0.5f; try { if (!_caretakerPatchInstalled && _harmony != null) { _caretakerPatchInstalled = CaretakerDeliverFirst.Install(_harmony); } if (!_sortRangePatchInstalled && _harmony != null) { _sortRangePatchInstalled = CaretakerSortRange.Install(_harmony); } if (_betterOrganisationInstalled && !_boRulesPatchInstalled && _harmony != null) { _boRulesPatchInstalled = BetterOrganisationRules.Install(_harmony); } if (!_claimRenewalPatchInstalled && _harmony != null) { _claimRenewalPatchInstalled = StationClaimRenewal.Install(_harmony); } if (_betterOrganisationInstalled && !_boUiPatchInstalled && _harmony != null) { _boUiPatchInstalled = BetterOrganisationChestPanelCompat.Install(_harmony); } Bridge.Refresh(forceSummary: false); if (_betterOrganisationInstalled) { BetterOrganisationRules.Refresh(forceSummary: false); } } catch (Exception e) { ((BaseUnityPlugin)this).Logger.LogError((object)("[Refresh] " + Bridge.Root(e))); } } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } if (_betterOrganisationInstalled) { BetterOrganisationRules.RestoreAll(); } Bridge.Clear(); } } internal static class StationClaimRenewal { private static bool _installed; private static bool _reportedMissing; internal static bool Install(Harmony harmony) { //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Expected O, but got Unknown if (_installed) { return true; } Type type = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "Innangard", StringComparison.OrdinalIgnoreCase))?.GetType("Innangard.StationClaim", throwOnError: false); if (type == null) { return false; } MethodInfo method = type.GetMethod("Renew", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[3] { typeof(ZNetView), typeof(ZDOID), typeof(float) }, null); if (method == null || method.ReturnType != typeof(void)) { if (!_reportedMissing) { _reportedMissing = true; Plugin.Log.LogWarning((object)"[Network] StationClaim.Renew signature changed; claim throttling unavailable."); } return false; } harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(StationClaimRenewal), "RenewPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _installed = true; Plugin.Log.LogInfo((object)"[Network] Innangard station claim renewals reduced while the lease is healthy."); return true; } private static bool RenewPrefix(ZNetView __0, ZDOID __1, float __2) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: 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) if (Plugin.ReduceClaimRenewals == null || !Plugin.ReduceClaimRenewals.Value || (Object)(object)__0 == (Object)null || !__0.IsValid() || !__0.IsOwner() || __1 == ZDOID.None || __2 <= 0f) { return true; } ZDO zDO = __0.GetZDO(); if (zDO == null || zDO.GetZDOID("inn_station_claim") != __1) { return true; } long num = zDO.GetLong("inn_station_claim_until", 0L); long ticks = DateTime.UtcNow.Ticks; long num2 = (long)((double)(__2 * 10000000f) * (2.0 / 3.0)); if (num > ticks) { return num - ticks <= num2; } return true; } } internal static class CaretakerDeliverFirst { private static FieldInfo _load; private static FieldInfo _remaining; private static bool _reportedDeferred; private static bool _reportedMissingMember; internal static bool Install(Harmony harmony) { //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Expected O, but got Unknown Type type = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "Innangard", StringComparison.OrdinalIgnoreCase))?.GetType("Innangard.Jobs.CaretakerJob", throwOnError: false); if (type == null) { if (!_reportedDeferred) { _reportedDeferred = true; Plugin.Log.LogInfo((object)"[Caretaker] Innangard is not loaded yet; will bind the deliver-first patch when it loads."); } return false; } MethodInfo method = type.GetMethod("TryCollectFromAnother", BindingFlags.Instance | BindingFlags.NonPublic); _load = type.GetField("_load", BindingFlags.Instance | BindingFlags.NonPublic); _remaining = type.GetField("_remaining", BindingFlags.Instance | BindingFlags.NonPublic); if (method == null || method.ReturnType != typeof(void) || _load == null || _remaining == null || _remaining.FieldType != typeof(int)) { if (!_reportedMissingMember) { _reportedMissingMember = true; Plugin.Log.LogError((object)"[Caretaker] Innangard caretaker method or queued-delivery field is missing; deliver-first patch could not bind."); } return false; } harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(CaretakerDeliverFirst), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[Caretaker] Deliver-first patch active: carry one chest's sorting cargo to its destination before collecting from another chest."); return true; } private static bool Prefix(object __instance) { if (!Plugin.DeliverBeforeCollectingMore.Value || __instance == null) { return true; } try { ICollection collection = _load.GetValue(__instance) as ICollection; int num = (int)_remaining.GetValue(__instance); if (num <= 0 && (collection == null || collection.Count == 0)) { return true; } if (Plugin.DetailedLogging.Value) { Plugin.Log.LogInfo((object)$"[Caretaker] Skipped CollectMore with remaining={num}, queued={collection?.Count ?? 0}; proceeding to physical delivery."); } return false; } catch (Exception e) { Plugin.Log.LogWarning((object)("[Caretaker] Could not read pending deliveries: " + Bridge.Root(e))); return true; } } } internal static class CaretakerSortRange { private sealed class StallState { internal GameObject Goal; internal Vector3 Position; internal float Since; } private sealed class Context { internal Component Settler; } private static FieldInfo _chore; private static FieldInfo _remaining; private static FieldInfo _target; private static MethodInfo _putAway; private static PropertyInfo _ai; private static PropertyInfo _pathBlocked; private static readonly ConditionalWeakTable<object, StallState> Stalls = new ConditionalWeakTable<object, StallState>(); [ThreadStatic] private static Context _active; internal static bool Install(Harmony harmony) { //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Expected O, but got Unknown //IL_01b0: Expected O, but got Unknown //IL_01b0: Expected O, but got Unknown Assembly? assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "Innangard", StringComparison.OrdinalIgnoreCase)); Type type = assembly?.GetType("Innangard.Jobs.CaretakerJob", throwOnError: false); Type type2 = assembly?.GetType("Innangard.SettlerController", throwOnError: false); Type type3 = assembly?.GetType("Innangard.SettlerAI", throwOnError: false); if (type == null || type2 == null || type3 == null) { return false; } _chore = type.GetField("_chore", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _remaining = type.GetField("_remaining", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _target = type.GetField("_target", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _putAway = type.GetMethod("PutAway", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _ai = type2.GetProperty("Ai", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _pathBlocked = type3.GetProperty("PathBlocked", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo method = type.GetMethod("Pursue", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (_chore == null || _remaining == null || _target == null || _putAway == null || _ai == null || _pathBlocked == null || method == null) { Plugin.Log.LogWarning((object)"[Caretaker] Stalled-deposit patch could not bind to this Innangard version."); return false; } harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(CaretakerSortRange), "Begin", (Type[])null), new HarmonyMethod(typeof(CaretakerSortRange), "End", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(CaretakerSortRange), "Finish", (Type[])null), (HarmonyMethod)null); Plugin.Log.LogInfo((object)"[Caretaker] Sorting deposits after a blocked approach or 3 seconds stalled nearby."); return true; } private static void Begin(object __instance, object __0, out Context __state) { __state = _active; _active = null; if (!Plugin.SortAfterStall.Value || __instance == null) { return; } try { int num = Convert.ToInt32(_chore.GetValue(__instance)); if ((num == 1 || num == 2) && (int)_remaining.GetValue(__instance) > 0) { _active = new Context { Settler = (Component)((__0 is Component) ? __0 : null) }; } } catch (Exception e) { Plugin.Log.LogWarning((object)("[Caretaker] Stalled-deposit context failed: " + Bridge.Root(e))); } } private static void End(object __instance, ref bool __result, Context __state) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: 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_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)_active?.Settler == (Object)null || (int)_remaining.GetValue(__instance) <= 0) { Stalls.Remove(__instance); return; } object? value = _target.GetValue(__instance); GameObject val = (GameObject)((value is GameObject) ? value : null); if (!((Object)(object)val == (Object)null) && val.activeInHierarchy) { Vector3 position = _active.Settler.transform.position; StallState value2 = Stalls.GetValue(__instance, (object _) => new StallState()); if ((Object)(object)value2.Goal != (Object)(object)val || Vector3.Distance(value2.Position, position) > 0.4f) { value2.Goal = val; value2.Position = position; value2.Since = Time.time; } float range = ((Convert.ToInt32(_chore.GetValue(__instance)) == 2) ? 3.55f : 2.8f); bool flag = Time.time - value2.Since >= 3f; bool flag2 = Blocked(); if ((__result || flag2) && (flag || flag2) && InRange(_active.Settler, val, range)) { _putAway.Invoke(__instance, new object[1] { _active.Settler }); __result = true; Stalls.Remove(__instance); Plugin.Log.LogInfo((object)("[Caretaker] Deposited sorting cargo after " + (flag ? "standing still" : "blocked route") + " near " + ((Object)val).name)); } } } catch (Exception e) { Plugin.Log.LogWarning((object)("[Caretaker] Stalled deposit failed: " + Bridge.Root(e))); } finally { _active = __state; } } private static Exception Finish(Exception __exception, Context __state) { _active = __state; return __exception; } private static bool Blocked() { object value = _ai.GetValue(_active.Settler, null); if (value != null) { return (bool)_pathBlocked.GetValue(value, null); } return false; } private static bool InRange(Component settler, GameObject target, float range) { //IL_0006: 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_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_007a: 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_0054: 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_0058: Unknown result type (might be due to invalid IL or missing references) Vector3 val = settler.transform.position + Vector3.up * 1.05f; float num = float.MaxValue; Collider[] componentsInChildren = target.GetComponentsInChildren<Collider>(true); foreach (Collider val2 in componentsInChildren) { if (!((Object)(object)val2 == (Object)null) && val2.enabled && !val2.isTrigger) { num = Mathf.Min(num, Vector3.Distance(val, val2.ClosestPoint(val))); } } if (num == float.MaxValue) { num = Vector3.Distance(val, target.transform.position); } return num <= range; } } internal static class Bridge { private sealed class ReferenceComparer : IEqualityComparer<object> { internal static readonly ReferenceComparer Instance = new ReferenceComparer(); public new bool Equals(object x, object y) { return x == y; } public int GetHashCode(object obj) { return RuntimeHelpers.GetHashCode(obj); } } private const BindingFlags Any = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; internal static Type DrawerType; internal static PropertyInfo ItemNameProp; internal static PropertyInfo CountProp; internal static PropertyInfo FreeSpaceProp; internal static MethodInfo DepositMethod; internal static MethodInfo WithdrawMethod; internal static MemberInfo NviewMember; private static readonly Dictionary<object, DrawerComponent> ByReal = new Dictionary<object, DrawerComponent>(ReferenceComparer.Instance); private static readonly Dictionary<int, object> ByViewId = new Dictionary<int, object>(); private static int _lastFound = -1; private static int _lastActive = -1; internal static bool BindBetrValheim() { Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "BetrValheim", StringComparison.OrdinalIgnoreCase)); if (assembly == null) { Plugin.Log.LogError((object)"[Bind] BetrValheim assembly not found."); return false; } DrawerType = assembly.GetType("BetrValheim.Drawers.Drawer", throwOnError: false); if (DrawerType == null) { Plugin.Log.LogError((object)"[Bind] BetrValheim.Drawers.Drawer not found."); return false; } ItemNameProp = DrawerType.GetProperty("ItemName", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); CountProp = DrawerType.GetProperty("Count", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); FreeSpaceProp = DrawerType.GetProperty("FreeSpace", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); DepositMethod = DrawerType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Deposit" && m.ReturnType == typeof(int) && ParametersAre(m, typeof(string), typeof(int))); WithdrawMethod = DrawerType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Withdraw" && m.ReturnType == typeof(int) && ParametersAre(m, typeof(int))); NviewMember = (MemberInfo)(((object)DrawerType.GetField("_nview", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) ?? ((object)DrawerType.GetProperty("_nview", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))); if (NviewMember == null) { NviewMember = (MemberInfo)(((object)DrawerType.GetField("m_nview", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) ?? ((object)DrawerType.GetProperty("m_nview", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))); } Plugin.Log.LogInfo((object)("[Bind] BetrValheim drawer type: " + DrawerType.FullName)); Plugin.Log.LogInfo((object)("[Bind] ItemName: " + Describe(ItemNameProp))); Plugin.Log.LogInfo((object)("[Bind] Count: " + Describe(CountProp))); Plugin.Log.LogInfo((object)("[Bind] FreeSpace: " + Describe(FreeSpaceProp))); Plugin.Log.LogInfo((object)("[Bind] Deposit: " + Describe(DepositMethod))); Plugin.Log.LogInfo((object)("[Bind] Withdraw: " + Describe(WithdrawMethod))); Plugin.Log.LogInfo((object)("[Bind] ZNetView member: " + (NviewMember?.Name ?? "<missing>"))); int num; if (ItemNameProp != null && CountProp != null && FreeSpaceProp != null && DepositMethod != null && WithdrawMethod != null) { num = ((NviewMember != null) ? 1 : 0); if (num != 0) { goto IL_02ca; } } else { num = 0; } Plugin.Log.LogError((object)"[Bind] One or more required BetrValheim members are missing."); goto IL_02ca; IL_02ca: return (byte)num != 0; } internal static void LogApiSurface() { try { Type? typeFromHandle = typeof(ItemDrawers_API); Type typeFromHandle2 = typeof(ItemDrawers_API.Drawer); MethodInfo method = typeFromHandle.GetMethod("AllDrawersInRange", BindingFlags.Static | BindingFlags.Public); MethodInfo method2 = typeFromHandle2.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); MethodInfo method3 = typeFromHandle2.GetMethod("Remove", BindingFlags.Instance | BindingFlags.Public); FieldInfo field = typeFromHandle2.GetField("Prefab", BindingFlags.Instance | BindingFlags.Public); FieldInfo field2 = typeFromHandle2.GetField("Amount", BindingFlags.Instance | BindingFlags.Public); FieldInfo field3 = typeFromHandle2.GetField("Quality", BindingFlags.Instance | BindingFlags.Public); FieldInfo fieldInfo = typeFromHandle2.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((FieldInfo f) => typeof(ZNetView).IsAssignableFrom(f.FieldType)); Plugin.Log.LogInfo((object)"[API] API.ItemDrawers_API, kg_ItemDrawers"); Plugin.Log.LogInfo((object)("[API] AllDrawersInRange(Vector3, Single): " + ((method != null && ParametersAre(method, typeof(Vector3), typeof(float))) ? "OK" : "MISSING/WRONG"))); Plugin.Log.LogInfo((object)("[API] Drawer.Add(Int32, Int32): " + ((method2 != null && ParametersAre(method2, typeof(int), typeof(int))) ? "OK" : "MISSING/WRONG"))); Plugin.Log.LogInfo((object)("[API] Drawer.Remove(Int32): " + ((method3 != null && ParametersAre(method3, typeof(int))) ? "OK" : "MISSING/WRONG"))); Plugin.Log.LogInfo((object)("[API] Drawer fields: Prefab=" + (field != null) + " Amount=" + (field2 != null) + " Quality=" + (field3 != null) + " ZNetView=" + (fieldInfo?.Name ?? "<missing>"))); Plugin.Log.LogInfo((object)"[API] No Innangard methods are patched by this build."); } catch (Exception e) { Plugin.Log.LogWarning((object)("[API] self-check failed: " + Root(e))); } } private static bool ParametersAre(MethodInfo method, params Type[] types) { if (method == null) { return false; } ParameterInfo[] parameters = method.GetParameters(); if (parameters.Length != types.Length) { return false; } for (int i = 0; i < parameters.Length; i++) { if (parameters[i].ParameterType != types[i]) { return false; } } return true; } internal static void Refresh(bool forceSummary) { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Unknown result type (might be due to invalid IL or missing references) if (DrawerType == null) { return; } Object[] array; try { array = Resources.FindObjectsOfTypeAll(DrawerType); } catch (Exception e) { Plugin.Log.LogWarning((object)("[Scan] FindObjectsOfTypeAll failed: " + Root(e))); return; } HashSet<object> hashSet = new HashSet<object>(ReferenceComparer.Instance); HashSet<int> hashSet2 = new HashSet<int>(); int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = 0; Object[] array2 = array; foreach (Object val in array2) { if (val == (Object)null) { num++; continue; } Component val2 = (Component)(object)((val is Component) ? val : null); if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.gameObject == (Object)null)) { Scene scene = val2.gameObject.scene; if (((Scene)(ref scene)).IsValid() && val2.gameObject.activeInHierarchy) { object obj = val; ZNetView nview = GetNview(obj); if ((Object)(object)nview == (Object)null || !nview.IsValid() || nview.GetZDO() == null) { num3++; continue; } GameObject val3 = PhysicalRoot(nview); if ((Object)(object)val3 == (Object)null || !val3.activeInHierarchy) { num2++; continue; } hashSet.Add(obj); int instanceID = ((Object)nview).GetInstanceID(); hashSet2.Add(instanceID); ByViewId[instanceID] = obj; if (!ByReal.TryGetValue(obj, out var value) || (Object)(object)value == (Object)null || (Object)(object)((Component)value).gameObject != (Object)(object)val3) { if ((Object)(object)value != (Object)null) { DrawerComponent.AllDrawers.Remove(value); } value = val3.GetComponent<DrawerComponent>(); if ((Object)(object)value == (Object)null) { value = val3.AddComponent<DrawerComponent>(); } ByReal[obj] = value; } value.Bind(obj, nview); value.Sync(); if (!DrawerComponent.AllDrawers.Contains(value)) { DrawerComponent.AllDrawers.Add(value); } string assigned = GetAssigned(obj); if (string.IsNullOrEmpty(assigned)) { num5++; } else { num4++; } if (Plugin.DetailedLogging.Value) { Plugin.Log.LogInfo((object)("[Drawer] root='" + ((Object)val3).name + "' real='" + ((Object)val2.gameObject).name + "' " + $"pos={val3.transform.position} assigned='{assigned}' prefab='{value.CurrentPrefab}' " + $"count={GetCount(obj)} free={GetFreeSpace(obj)} zdo={nview.GetZDO()?.m_uid}")); } continue; } } num2++; } KeyValuePair<object, DrawerComponent>[] array3 = ByReal.ToArray(); for (int i = 0; i < array3.Length; i++) { KeyValuePair<object, DrawerComponent> keyValuePair = array3[i]; if (!hashSet.Contains(keyValuePair.Key) || !((Object)(object)keyValuePair.Value != (Object)null)) { if ((Object)(object)keyValuePair.Value != (Object)null) { DrawerComponent.AllDrawers.Remove(keyValuePair.Value); } ByReal.Remove(keyValuePair.Key); } } int[] array4 = ByViewId.Keys.ToArray(); foreach (int num6 in array4) { if (!hashSet2.Contains(num6)) { ByViewId.Remove(num6); } } DrawerComponent.AllDrawers.RemoveAll((DrawerComponent d) => (Object)(object)d == (Object)null || (Object)(object)d.View == (Object)null || !d.View.IsValid() || (Object)(object)((Component)d).gameObject == (Object)null || !((Component)d).gameObject.activeInHierarchy); int count = DrawerComponent.AllDrawers.Count; if (forceSummary || array.Length != _lastFound || count != _lastActive) { _lastFound = array.Length; _lastActive = count; Plugin.Log.LogInfo((object)($"[Scan] found={array.Length} active={count} assigned={num4} unassigned={num5} " + $"invalidNview={num3} inactive={num2} null={num}")); } } internal static void Clear() { DrawerComponent[] array = DrawerComponent.AllDrawers.ToArray(); foreach (DrawerComponent drawerComponent in array) { if ((Object)(object)drawerComponent != (Object)null) { try { Object.Destroy((Object)(object)drawerComponent); } catch { } } } DrawerComponent.AllDrawers.Clear(); ByReal.Clear(); ByViewId.Clear(); _lastFound = -1; _lastActive = -1; } internal static GameObject PhysicalRoot(ZNetView view) { if ((Object)(object)view == (Object)null) { return null; } Piece val = ((Component)view).GetComponent<Piece>() ?? ((Component)view).GetComponentInParent<Piece>(); if (!((Object)(object)val != (Object)null)) { return ((Component)view).gameObject; } return ((Component)val).gameObject; } internal static ZNetView GetNview(object real) { try { if (real == null || NviewMember == null) { return null; } if (NviewMember is FieldInfo fieldInfo) { object? value = fieldInfo.GetValue(real); return (ZNetView)((value is ZNetView) ? value : null); } if (NviewMember is PropertyInfo propertyInfo) { object? value2 = propertyInfo.GetValue(real, null); return (ZNetView)((value2 is ZNetView) ? value2 : null); } } catch (Exception e) { Plugin.Log.LogWarning((object)("[ZNetView] " + Root(e))); } return null; } internal static bool TryGetReal(ZNetView view, out object real) { real = null; if ((Object)(object)view == (Object)null) { return false; } if (!ByViewId.TryGetValue(((Object)view).GetInstanceID(), out real) || real == null) { return false; } ZNetView nview = GetNview(real); if ((Object)(object)nview == (Object)null || (Object)(object)nview != (Object)(object)view || !nview.IsValid()) { real = null; return false; } return true; } internal static List<ZNetView> AllViews() { List<ZNetView> list = new List<ZNetView>(); DrawerComponent[] array = DrawerComponent.AllDrawers.ToArray(); foreach (DrawerComponent drawerComponent in array) { if (!((Object)(object)drawerComponent == (Object)null) && !((Object)(object)drawerComponent.View == (Object)null) && drawerComponent.View.IsValid() && !((Object)(object)((Component)drawerComponent).gameObject == (Object)null) && ((Component)drawerComponent).gameObject.activeInHierarchy) { drawerComponent.Sync(); list.Add(drawerComponent.View); } } return list; } internal static void SyncOne(object real) { if (real == null) { return; } if (ByReal.TryGetValue(real, out var value) && (Object)(object)value != (Object)null) { value.Sync(); return; } ZNetView nview = GetNview(real); if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return; } GameObject val = PhysicalRoot(nview); if (!((Object)(object)val == (Object)null)) { value = val.GetComponent<DrawerComponent>(); if ((Object)(object)value == (Object)null) { value = val.AddComponent<DrawerComponent>(); } value.Bind(real, nview); value.Sync(); ByReal[real] = value; ByViewId[((Object)nview).GetInstanceID()] = real; if (!DrawerComponent.AllDrawers.Contains(value)) { DrawerComponent.AllDrawers.Add(value); } } } internal static string GetAssigned(object real) { try { return (ItemNameProp?.GetValue(real, null) as string) ?? string.Empty; } catch { return string.Empty; } } internal static int GetCount(object real) { try { object obj = CountProp?.GetValue(real, null); return (obj != null) ? Convert.ToInt32(obj) : 0; } catch { return 0; } } internal static int GetFreeSpace(object real) { try { object obj = FreeSpaceProp?.GetValue(real, null); return (obj != null) ? Convert.ToInt32(obj) : 0; } catch { return 0; } } internal static string ResolvePrefabName(string assigned) { if (string.IsNullOrEmpty(assigned)) { return string.Empty; } try { if ((Object)(object)ObjectDB.instance != (Object)null) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(assigned); if ((Object)(object)itemPrefab != (Object)null) { return ((Object)itemPrefab).name; } foreach (GameObject item in ObjectDB.instance.m_items) { if ((Object)(object)item == (Object)null) { continue; } ItemDrop component = item.GetComponent<ItemDrop>(); if (!((Object)(object)component == (Object)null) && component.m_itemData?.m_shared != null) { string name = component.m_itemData.m_shared.m_name; if (string.Equals(name, assigned, StringComparison.Ordinal) || string.Equals(name?.TrimStart('$'), assigned?.TrimStart('$'), StringComparison.OrdinalIgnoreCase)) { return ((Object)item).name; } } } } } catch (Exception e) { Plugin.Log.LogWarning((object)("[Prefab] could not resolve '" + assigned + "': " + Root(e))); } return assigned; } internal static void ApiAdd(ZNetView view, string snapshotPrefab, int amount, int quality) { if ((Object)(object)view == (Object)null || !view.IsValid()) { throw new InvalidOperationException("ItemDrawers API Add: drawer ZNetView is invalid."); } if (amount <= 0) { throw new ArgumentOutOfRangeException("amount"); } if (quality <= 0) { quality = 1; } if (!TryGetReal(view, out var real)) { throw new InvalidOperationException("ItemDrawers API Add: backing BetrValheim drawer was not found."); } string text = ResolvePrefabName(GetAssigned(real)); if (string.IsNullOrEmpty(text)) { throw new InvalidOperationException("ItemDrawers API Add: backing drawer is unassigned."); } if (!string.IsNullOrEmpty(snapshotPrefab) && !Matches(text, snapshotPrefab)) { throw new InvalidOperationException("ItemDrawers API Add: drawer changed from '" + snapshotPrefab + "' to '" + text + "'."); } if (quality != 1) { throw new InvalidOperationException($"ItemDrawers API Add: BetrValheim drawer cannot represent quality {quality}."); } int num = Math.Max(0, GetFreeSpace(real)); if (num < amount) { throw new InvalidOperationException($"ItemDrawers API Add: requested {amount}, free space is {num}."); } if (Plugin.DetailedLogging.Value) { Plugin.Log.LogInfo((object)$"[ADD->] prefab='{text}' amount={amount} quality={quality} count={GetCount(real)} free={num}"); } object obj = DepositMethod.Invoke(real, new object[2] { text, amount }); int num2 = ((obj != null) ? Convert.ToInt32(obj) : 0); if (num2 != amount) { if (num2 > 0) { try { WithdrawMethod.Invoke(real, new object[1] { num2 }); } catch (Exception e) { Plugin.Log.LogError((object)("[ADD] partial-deposit rollback failed: " + Root(e))); } } SyncOne(real); throw new InvalidOperationException($"ItemDrawers API Add: BetrValheim moved {num2}, expected {amount}."); } SyncOne(real); if (Plugin.DetailedLogging.Value) { Plugin.Log.LogInfo((object)$"[ADD<-] prefab='{text}' moved={num2} count={GetCount(real)} free={GetFreeSpace(real)}"); } } internal static void ApiRemove(ZNetView view, int amount) { if ((Object)(object)view == (Object)null || !view.IsValid()) { throw new InvalidOperationException("ItemDrawers API Remove: drawer ZNetView is invalid."); } if (amount <= 0) { throw new ArgumentOutOfRangeException("amount"); } if (!TryGetReal(view, out var real)) { throw new InvalidOperationException("ItemDrawers API Remove: backing BetrValheim drawer was not found."); } int num = Math.Max(0, GetCount(real)); if (num < amount) { throw new InvalidOperationException($"ItemDrawers API Remove: requested {amount}, only {num} available."); } string text = ResolvePrefabName(GetAssigned(real)); if (Plugin.DetailedLogging.Value) { Plugin.Log.LogInfo((object)$"[REMOVE->] prefab='{text}' amount={amount} count={num}"); } object obj = WithdrawMethod.Invoke(real, new object[1] { amount }); int num2 = ((obj != null) ? Convert.ToInt32(obj) : 0); if (num2 != amount) { if (num2 > 0 && !string.IsNullOrEmpty(text)) { try { DepositMethod.Invoke(real, new object[2] { text, num2 }); } catch (Exception e) { Plugin.Log.LogError((object)("[REMOVE] partial-withdraw rollback failed: " + Root(e))); } } SyncOne(real); throw new InvalidOperationException($"ItemDrawers API Remove: BetrValheim moved {num2}, expected {amount}."); } SyncOne(real); if (Plugin.DetailedLogging.Value) { Plugin.Log.LogInfo((object)$"[REMOVE<-] prefab='{text}' moved={num2} count={GetCount(real)}"); } } internal static bool Matches(string left, string right) { if (string.IsNullOrEmpty(left) || string.IsNullOrEmpty(right)) { return false; } if (string.Equals(left, right, StringComparison.OrdinalIgnoreCase)) { return true; } return string.Equals(left.TrimStart('$').Replace("_", string.Empty), right.TrimStart('$').Replace("_", string.Empty), StringComparison.OrdinalIgnoreCase); } internal static string Describe(PropertyInfo p) { if (!(p == null)) { return p.DeclaringType?.FullName + "." + p.Name + " : " + p.PropertyType.FullName; } return "<missing>"; } internal static string Describe(MethodInfo m) { if (m == null) { return "<missing>"; } return m.DeclaringType?.FullName + "." + m.Name + "(" + string.Join(", ", from p in m.GetParameters() select p.ParameterType.Name) + ") -> " + m.ReturnType.Name; } internal static string Root(Exception e) { while (e is TargetInvocationException { InnerException: not null } ex) { e = ex.InnerException; } return e?.Message ?? "<unknown>"; } } internal static class BetterOrganisationChestPanelCompat { private const BindingFlags InstanceAny = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private const BindingFlags StaticAny = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private const string CompatObjectName = "pho_bo_compat_note"; private const string CompatButtonObjectName = "pho_bo_compat_button"; private const string CompatText = "Override: Storage Rules managed by Better Organisation"; private const string CompatButtonText = "Open Better Organisation"; [ThreadStatic] private static bool _inPanelRead; private static bool _installed; private static bool _reportedMissing; private static bool _reportedSuppression; private static FieldInfo _container; private static FieldInfo _picking; private static FieldInfo _column; private static FieldInfo _grid; private static FieldInfo _choices; private static FieldInfo _mineHelp; private static FieldInfo _origin; private static FieldInfo _byHand; private static FieldInfo _slack; private static FieldInfo _remeasureFlag; private static MethodInfo _remeasure; private static MethodInfo _resize; private static PropertyInfo _boUiInstance; private static PropertyInfo _boGui; private static MethodInfo _boToggleEditor; internal static bool InPanelRead => _inPanelRead; internal static bool Install(Harmony harmony) { //IL_03cb: Unknown result type (might be due to invalid IL or missing references) //IL_03d2: Expected O, but got Unknown //IL_03e2: Unknown result type (might be due to invalid IL or missing references) //IL_03e9: Expected O, but got Unknown //IL_03f9: Unknown result type (might be due to invalid IL or missing references) //IL_0400: Expected O, but got Unknown //IL_0433: Unknown result type (might be due to invalid IL or missing references) //IL_0441: Expected O, but got Unknown //IL_0455: Unknown result type (might be due to invalid IL or missing references) //IL_0463: Expected O, but got Unknown if (_installed) { return true; } if (!BetterOrganisationRules.IsAvailable) { return false; } Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "Innangard", StringComparison.OrdinalIgnoreCase)); Type type = assembly?.GetType("Innangard.ChestSortPanel", throwOnError: false); if (type == null) { if (!_reportedMissing && assembly != null) { _reportedMissing = true; Plugin.Log.LogWarning((object)"[BO UI] Innangard.ChestSortPanel was not found; Shift+E picker suppression is unavailable."); } return false; } MethodInfo method = type.GetMethod("Open", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Container) }, null); MethodInfo method2 = type.GetMethod("Refresh", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); MethodInfo methodInfo = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo m) { if (m.Name != "BuildChoices") { return false; } ParameterInfo[] parameters = m.GetParameters(); return parameters.Length == 2 && typeof(Container).IsAssignableFrom(parameters[0].ParameterType); }); MethodInfo methodInfo2 = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Populate" && m.GetParameters().Length == 1 && m.ReturnType == typeof(void)); _container = type.GetField("_container", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _picking = type.GetField("_picking", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _column = type.GetField("_column", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _grid = type.GetField("_grid", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _choices = type.GetField("_choices", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _mineHelp = type.GetField("_mineHelp", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _origin = type.GetField("_origin", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _byHand = type.GetField("_byHand", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _slack = type.GetField("_slack", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _remeasureFlag = type.GetField("_remeasure", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _remeasure = type.GetMethod("Remeasure", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); _resize = type.GetMethod("Resize", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); Type? obj = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "ChestCategories", StringComparison.OrdinalIgnoreCase))?.GetType("ChestCategories.UI.ModUi", throwOnError: false); _boUiInstance = obj?.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _boGui = obj?.GetProperty("Gui", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _boToggleEditor = obj?.GetMethod("ToggleEditor", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Container) }, null); if (method == null || method2 == null || methodInfo == null || methodInfo2 == null || _container == null || _picking == null || _column == null || _mineHelp == null) { if (!_reportedMissing) { _reportedMissing = true; Plugin.Log.LogWarning((object)("[BO UI] Innangard 0.1.9 Shift+E panel binding incomplete: " + $"Open={method != null} Refresh={method2 != null} BuildChoices={methodInfo != null} " + $"Populate={methodInfo2 != null} container={_container != null} picking={_picking != null} column={_column != null} mineHelp={_mineHelp != null}.")); } return false; } HarmonyMethod val = new HarmonyMethod(typeof(BetterOrganisationChestPanelCompat), "PanelScopePrefix", (Type[])null); HarmonyMethod val2 = new HarmonyMethod(typeof(BetterOrganisationChestPanelCompat), "PanelScopePostfix", (Type[])null); HarmonyMethod val3 = new HarmonyMethod(typeof(BetterOrganisationChestPanelCompat), "PanelScopeFinalizer", (Type[])null); harmony.Patch((MethodBase)method, val, val2, (HarmonyMethod)null, val3, (HarmonyMethod)null); harmony.Patch((MethodBase)method2, val, val2, (HarmonyMethod)null, val3, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(BetterOrganisationChestPanelCompat), "BuildChoicesPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(BetterOrganisationChestPanelCompat), "PopulatePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _installed = true; Plugin.Log.LogInfo((object)"[BO UI] Bound Innangard.ChestSortPanel Open/Refresh/BuildChoices/Populate."); Plugin.Log.LogInfo((object)"[BO UI] BO-managed Shift+E panels keep intake/private controls and replace only the material picker with a Better Organisation storage-rules shortcut."); if (_boUiInstance != null && _boToggleEditor != null) { Plugin.Log.LogInfo((object)"[BO UI] Better Organisation category editor shortcut bound."); } else { Plugin.Log.LogWarning((object)"[BO UI] Better Organisation category editor shortcut was not found; the compatibility heading will still be shown."); } return true; } private static void PanelScopePrefix() { _inPanelRead = true; } private static void PanelScopePostfix(object __instance) { _inPanelRead = false; Apply(__instance); } private static Exception PanelScopeFinalizer(Exception __exception) { _inPanelRead = false; return __exception; } private static bool BuildChoicesPrefix(Container __0) { return !BetterOrganisationRules.IsManaged(__0); } private static bool PopulatePrefix(object __instance) { if (!BetterOrganisationRules.IsManaged(GetContainer(__instance))) { return true; } ClearPickerRows(__instance); return false; } private static Container GetContainer(object panel) { try { return (Container)((panel != null) ? /*isinst with value type is only supported in some contexts*/: null); } catch { return null; } } private static void ClearPickerRows(object panel) { try { object? obj = _grid?.GetValue(panel); RectTransform val = (RectTransform)((obj is RectTransform) ? obj : null); if ((Object)(object)val != (Object)null) { for (int num = ((Transform)val).childCount - 1; num >= 0; num--) { Object.Destroy((Object)(object)((Component)((Transform)val).GetChild(num)).gameObject); } } (_choices?.GetValue(panel) as IList)?.Clear(); } catch (Exception e) { Plugin.Log.LogWarning((object)("[BO UI] Could not clear Innangard's old material rows: " + Bridge.Root(e))); } } private static void Apply(object panel) { if (panel == null) { return; } try { Container container = GetContainer(panel); bool flag = BetterOrganisationRules.IsManaged(container); object? value = _picking.GetValue(panel); RectTransform val = (RectTransform)((value is RectTransform) ? value : null); object? value2 = _column.GetValue(panel); RectTransform val2 = (RectTransform)((value2 is RectTransform) ? value2 : null); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { return; } GameObject val3 = FindCompatObject(val2, "pho_bo_compat_note"); GameObject val4 = FindCompatObject(val2, "pho_bo_compat_button"); bool flag2 = false; if (flag) { if (((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(false); flag2 = true; } if ((Object)(object)val3 == (Object)null) { val3 = CreateCompatNote(panel, val2, val); flag2 = (Object)(object)val3 != (Object)null; } if ((Object)(object)val4 == (Object)null && _boUiInstance != null && _boToggleEditor != null) { val4 = CreateCompatButton(panel, val2, val, container); flag2 = (Object)(object)val4 != (Object)null; } if ((Object)(object)val3 != (Object)null) { if (!val3.activeSelf) { val3.SetActive(true); flag2 = true; } int siblingIndex = ((Transform)val).GetSiblingIndex(); if (val3.transform.GetSiblingIndex() != siblingIndex) { val3.transform.SetSiblingIndex(siblingIndex); flag2 = true; } } if ((Object)(object)val4 != (Object)null) { BetterOrganisationEditorButtonAction component = val4.GetComponent<BetterOrganisationEditorButtonAction>(); if ((Object)(object)component != (Object)null) { component.Target = container; component.InnangardPanel = panel; } if (!val4.activeSelf) { val4.SetActive(true); flag2 = true; } int num = (((Object)(object)val3 != (Object)null) ? Math.Min(val3.transform.GetSiblingIndex() + 1, Math.Max(0, ((Transform)val2).childCount - 1)) : ((Transform)val).GetSiblingIndex()); if (val4.transform.GetSiblingIndex() != num) { val4.transform.SetSiblingIndex(num); flag2 = true; } } ClearPickerRows(panel); SetSlackFlexible(panel, 1f); if (!_reportedSuppression) { _reportedSuppression = true; Plugin.Log.LogInfo((object)"[BO UI] Suppressed the Shift+E Search/material list for a Better Organisation-managed chest."); } } else { if ((Object)(object)val3 != (Object)null && val3.activeSelf) { val3.SetActive(false); flag2 = true; } if ((Object)(object)val4 != (Object)null && val4.activeSelf) { val4.SetActive(false); flag2 = true; } } if (flag2) { if (_remeasureFlag != null) { _remeasureFlag.SetValue(panel, true); } _remeasure?.Invoke(panel, null); _resize?.Invoke(panel, null); } } catch (Exception e) { Plugin.Log.LogWarning((object)("[BO UI] Could not apply Shift+E material-picker compatibility layout: " + Bridge.Root(e))); } } private static GameObject FindCompatObject(RectTransform column, string name) { for (int i = 0; i < ((Transform)column).childCount; i++) { Transform child = ((Transform)column).GetChild(i); if ((Object)(object)child != (Object)null && (Object)(object)((Component)child).gameObject != (Object)null && ((Object)((Component)child).gameObject).name == name) { return ((Component)child).gameObject; } } return null; } private static GameObject CreateCompatNote(object panel, RectTransform column, RectTransform picking) { object? obj = _origin?.GetValue(panel); Component val = (Component)((obj is Component) ? obj : null); if ((Object)(object)val == (Object)null || (Object)(object)val.gameObject == (Object)null) { object? obj2 = _mineHelp?.GetValue(panel); val = (Component)((obj2 is Component) ? obj2 : null); } if ((Object)(object)val == (Object)null || (Object)(object)val.gameObject == (Object)null) { return null; } GameObject obj3 = Object.Instantiate<GameObject>(val.gameObject); ((Object)obj3).name = "pho_bo_compat_note"; obj3.transform.SetParent((Transform)(object)column, false); obj3.transform.SetSiblingIndex(((Transform)picking).GetSiblingIndex()); Component component = obj3.GetComponent(((object)val).GetType()); SetText(component, "Override: Storage Rules managed by Better Organisation"); SetNumericProperty(component, "fontSize", 20); obj3.SetActive(true); return obj3; } private static GameObject CreateCompatButton(object panel, RectTransform column, RectTransform picking, Container container) { object? obj = _byHand?.GetValue(panel); Component val = (Component)((obj is Component) ? obj : null); if ((Object)(object)val == (Object)null || (Object)(object)val.gameObject == (Object)null) { return null; } GameObject val2 = Object.Instantiate<GameObject>(val.gameObject); ((Object)val2).name = "pho_bo_compat_button"; val2.transform.SetParent((Transform)(object)column, false); val2.transform.SetSiblingIndex(((Transform)picking).GetSiblingIndex()); SetFirstChildText(val2, "Open Better Organisation"); Component component = val2.GetComponent(((object)val).GetType()); object obj2 = ((object)component)?.GetType().GetProperty("onClick", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(component, null); if (obj2 == null) { Object.Destroy((Object)(object)val2); return null; } obj2.GetType().GetMethod("RemoveAllListeners", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null)?.Invoke(obj2, null); BetterOrganisationEditorButtonAction betterOrganisationEditorButtonAction = val2.AddComponent<BetterOrganisationEditorButtonAction>(); betterOrganisationEditorButtonAction.Target = container; betterOrganisationEditorButtonAction.InnangardPanel = panel; MethodInfo method = typeof(BetterOrganisationEditorButtonAction).GetMethod("Click", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo methodInfo = obj2.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "AddListener" && m.GetParameters().Length == 1); if (method == null || methodInfo == null) { Object.Destroy((Object)(object)val2); return null; } Delegate obj3 = Delegate.CreateDelegate(methodInfo.GetParameters()[0].ParameterType, betterOrganisationEditorButtonAction, method); methodInfo.Invoke(obj2, new object[1] { obj3 }); val2.SetActive(true); return val2; } private static void SetFirstChildText(GameObject root, string value) { if (!((Object)(object)root == (Object)null)) { Component[] componentsInChildren = root.GetComponentsInChildren<Component>(true); for (int i = 0; i < componentsInChildren.Length && !SetText(componentsInChildren[i], value); i++) { } } } private static bool SetText(Component component, string value) { if ((Object)(object)component == (Object)null) { return false; } try { PropertyInfo property = ((object)component).GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property == null || !property.CanWrite || property.PropertyType != typeof(string)) { return false; } property.SetValue(component, value, null); return true; } catch { return false; } } private static void SetNumericProperty(Component component, string name, int value) { if ((Object)(object)component == (Object)null) { return; } try { PropertyInfo property = ((object)component).GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(property == null) && property.CanWrite) { if (property.PropertyType == typeof(int)) { property.SetValue(component, value, null); } else if (property.PropertyType == typeof(float)) { property.SetValue(component, (float)value, null); } } } catch { } } internal static void CloseInnangardPanel(object panel) { if (panel == null) { return; } try { Type type = panel.GetType(); MethodInfo methodInfo = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => (m.Name == "Close" || m.Name == "Hide" || m.Name == "Dismiss") && m.GetParameters().Length == 0 && m.ReturnType == typeof(void)); if (methodInfo != null) { methodInfo.Invoke(panel, null); Plugin.Log.LogInfo((object)"[BO UI] Closed Innangard Shift+E panel before opening Better Organisation."); return; } string[] array = new string[4] { "m_panel", "_panel", "_root", "m_root" }; foreach (string name in array) { object obj = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(panel); GameObject val = (GameObject)((obj is GameObject) ? obj : null); if (val != null) { val.SetActive(false); Plugin.Log.LogInfo((object)"[BO UI] Closed Innangard Shift+E panel before opening Better Organisation."); return; } Component val2 = (Component)((obj is Component) ? obj : null); if (val2 != null && (Object)(object)val2.gameObject != (Object)null) { val2.gameObject.SetActive(false); Plugin.Log.LogInfo((object)"[BO UI] Closed Innangard Shift+E panel before opening Better Organisation."); return; } } Component val3 = (Component)((panel is Component) ? panel : null); if (val3 != null && (Object)(object)val3.gameObject != (Object)null) { val3.gameObject.SetActive(false); Plugin.Log.LogInfo((object)"[BO UI] Closed Innangard Shift+E panel before opening Better Organisation."); } else { Plugin.Log.LogWarning((object)"[BO UI] Could not find Innangard's panel root to close it."); } } catch (Exception e) { Plugin.Log.LogWarning((object)("[BO UI] Could not close Innangard Shift+E panel: " + Bridge.Root(e))); } } internal static void OpenBetterOrganisationEditor(Container container) { if ((Object)(object)container == (Object)null || _boUiInstance == null || _boToggleEditor == null) { return; } try { object value = _boUiInstance.GetValue(null, null); if (value == null) { Plugin.Log.LogWarning((object)"[BO UI] Better Organisation UI is not ready yet."); return; } bool flag = false; MethodInfo method = typeof(InventoryGui).GetMethod("IsVisible", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method != null) { flag = (bool)method.Invoke(null, null); } if (!flag) { object obj = _boGui?.GetValue(value, null); (obj?.GetType().GetMethod("Show", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2] { typeof(Container), typeof(int) }, null))?.Invoke(obj, new object[2] { null, 1 }); } _boToggleEditor.Invoke(value, new object[1] { container }); Plugin.Log.LogInfo((object)"[BO UI] Opened Better Organisation storage rules for the current chest."); } catch (Exception e) { Plugin.Log.LogWarning((object)("[BO UI] Could not open Better Organisation storage rules: " + Bridge.Root(e))); } } private static void SetSlackFlexible(object panel, float value) { try { object obj = _slack?.GetValue(panel); (obj?.GetType().GetProperty("flexibleHeight", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.SetValue(obj, value, null); } catch { } } } internal sealed class BetterOrganisationEditorButtonAction : MonoBehaviour { internal Container Target; internal object InnangardPanel; public void Click() { BetterOrganisationChestPanelCompat.CloseInnangardPanel(InnangardPanel); BetterOrganisationChestPanelCompat.OpenBetterOrganisationEditor(Target); } } internal static class BetterOrganisationRules { private sealed class RuleCache { internal string Signature; internal bool HasAnyRule; internal HashSet<string> Accepted; } private sealed class RuleSnapshot { internal bool HasLinks; internal bool AcceptsEverything; internal bool AcceptsStash = true; internal readonly HashSet<string> Include = new HashSet<string>(StringComparer.OrdinalIgnoreCase); internal readonly HashSet<string> Exclude = new HashSet<string>(StringComparer.OrdinalIgnoreCase); internal bool HasAnyRule { get { if (!HasLinks && !AcceptsEverything && AcceptsStash && Include.Count <= 0) { return Exclude.Count > 0; } return true; } } } private const BindingFlags Any = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private const string MirrorKey = "pho_bo_inn_mirror"; private const string BackupConfiguredKey = "pho_bo_inn_b_cfg"; private const string BackupKindsKey = "pho_bo_inn_b_kinds"; private const string BackupItemsKey = "pho_bo_inn_b_items"; private const string BackupLegacyKindKey = "pho_bo_inn_b_legacy"; private static Assembly _assembly; private static Type _storeType; private static Type _configType; private static Type _categoryServiceType; private static Type _categoryType; private static Type _groupType; private static MethodInfo _getConfig; private static MethodInfo _getCategory; private static PropertyInfo _hasLinks; private static PropertyInfo _links; private static PropertyInfo _includeItems; private static PropertyInfo _excludeItems; private static PropertyInfo _acceptsEverything; private static PropertyInfo _acceptsStash; private static MethodInfo _isLinkedCategory; private static MethodInfo _isLinkedGroup; private static PropertyInfo _categoryGroup; private static PropertyInfo _categoryId; private static PropertyInfo _groupId; private static MethodInfo _serializeConfig; private static readonly Dictionary<int, RuleCache> RuleCaches = new Dictionary<int, RuleCache>(); private static bool _bound; private static bool _reportedUnavailable; private static bool _installed; private static bool _reportedMissing; private static int _lastActive = -1; internal static bool IsAvailable => _bound; internal static bool Install(Harmony harmony) { //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Expected O, but got Unknown //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Expected O, but got Unknown //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Expected O, but got Unknown //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Expected O, but got Unknown //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Expected O, but got Unknown if (!_bound) { return false; } if (_installed) { return true; } Type type = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "Innangard", StringComparison.OrdinalIgnoreCase))?.GetType("Innangard.ChestSortRules", throwOnError: false); if (type == null) { return false; } BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; Type[] types = new Type[1] { typeof(Container) }; MethodInfo method = type.GetMethod("IsConfigured", bindingAttr, null, types, null); MethodInfo method2 = type.GetMethod("KindsOf", bindingAttr, null, types, null); MethodInfo method3 = type.GetMethod("ReadItems", bindingAttr, null, types, null); Type[] types2 = new Type[2] { typeof(Container), typeof(string) }; MethodInfo method4 = type.GetMethod("Accepts", bindingAttr, null, types2, null); MethodInfo method5 = type.GetMethod("Precision", bindingAttr, null, types2, null); if (method == null || method.ReturnType != typeof(bool) || method2 == null || method2.ReturnType != typeof(int) || method3 == null || method3.ReturnType != typeof(HashSet<string>) || method4 == null || method4.ReturnType != typeof(bool) || method5 == null || method5.ReturnType != typeof(int)) { if (!_reportedMissing) { _reportedMissing = true; Plugin.Log.LogError((object)"[BO] Innangard chest rule methods changed; local BO matching is unavailable."); } return false; } harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(BetterOrganisationRules), "ConfiguredPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)method2, new HarmonyMethod(typeof(BetterOrganisationRules), "KindsPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)method3, new HarmonyMethod(typeof(BetterOrganisationRules), "ItemsPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)method4, new HarmonyMethod(typeof(BetterOrganisationRules), "AcceptsPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)method5, new HarmonyMethod(typeof(BetterOrganisationRules), "PrecisionPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _installed = true; Plugin.Log.LogInfo((object)"[BO] Innangard now reads BO category/item rules locally; no extra rule list is written to chests."); return true; } private static bool ConfiguredPrefix(Container __0, ref bool __result) { if (!TryGetAccepted(__0, out var _)) { return true; } __result = true; return false; } private static bool KindsPrefix(Container __0, ref int __result) { if (!TryGetAccepted(__0, out var _)) { return true; } __result = 0; return false; } private static bool ItemsPrefix(Container __0, ref HashSet<string> __result) { if (!TryGetAccepted(__0, out var accepted)) { return true; } if (BetterOrganisationChestPanelCompat.InPanelRead) { __result = new HashSet<string>(StringComparer.Ordinal) { "__pho_bo_managed__" }; return false; } __result = new HashSet<string>(accepted, StringComparer.Ordinal); return false; } private static bool AcceptsPrefix(Container __0, string __1, ref bool __result) { if (!TryGetAccepted(__0, out var accepted)) { return true; } __result = !string.IsNullOrEmpty(__1) && accepted.Contains(__1); return false; } private static bool PrecisionPrefix(Container __0, string __1, ref int __result) { if (!TryGetAccepted(__0, out var accepted)) { return true; } __result = ((string.IsNullOrEmpty(__1) || !accepted.Contains(__1)) ? int.MaxValue : 0); return false; } internal static bool IsManaged(Container container) { HashSet<string> accepted; return TryGetAccepted(container, out accepted); } private static bool TryGetAccepted(Container container, out HashSet<string> accepted) { accepted = null; if (!_installed || !_bound || (Object)(object)ObjectDB.instance == (Object)null || (Object)(object)container == (Object)null) { return false; } ZNetView containerView = GetContainerView(container); if ((Object)(object)containerView == (Object)null || !containerView.IsValid() || containerView.GetZDO() == null) { return false; } int instanceID = ((Object)containerView).GetInstanceID(); if (!RuleCaches.TryGetValue(instanceID, out var value)) { value = RefreshCache(container, containerView); } if (value == null || !value.HasAnyRule) { return false; } accepted = value.Accepted; return true; } internal static void Bind() { _assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "ChestCategories", StringComparison.OrdinalIgnoreCase)); if (_assembly == null) { Plugin.Log.LogInfo((object)"[BO] Better Organisation not loaded; Innangard chest rules are untouched."); return; } _storeType = _assembly.GetType("ChestCategories.Chests.ChestConfigStore", throwOnError: false) ?? SafeTypes(_assembly).FirstOrDefault((Type t) => t.Name == "ChestConfigStore"); _configType = _assembly.GetType("ChestCategories.Chests.ChestConfig", throwOnError: false) ?? SafeTypes(_assembly).FirstOrDefault((Type t) => t.Name == "ChestConfig"); _categoryServiceType = _assembly.GetType("ChestCategories.Categories.CategoryService", throwOnError: false) ?? SafeTypes(_assembly).FirstOrDefault((Type t) => t.Name == "CategoryService"); _categoryType = _assembly.GetType("ChestCategories.Categories.Category", throwOnError: false) ?? SafeTypes(_assembly).FirstOrDefault((Type t) => t.Name == "Category"); _groupType = _assembly.GetType("ChestCategories.Categories.CategoryGroup", throwOnError: false) ?? SafeTypes(_assembly).FirstOrDefault((Type t) => t.Name == "CategoryGroup"); _getConfig = _storeType?.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Get" && m.IsStatic && m.GetParameters().Length == 1 && typeof(Container).IsAssignableFrom(m.GetParameters()[0].ParameterType)); _getCategory = _categoryServiceType?.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "GetCategory" && m.IsStatic && m.GetParameters().Length == 1 && typeof(ItemData).IsAssignableFrom(m.GetParameters()[0].ParameterType)); if (_configType != null) { _hasLinks = _configType.GetProperty("HasLinks", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _links = _configType.GetProperty("Links", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _includeItems = _configType.GetProperty("IncludeItems", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _excludeItems = _configType.GetProperty("ExcludeItems", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _acceptsEverything = _configType.GetProperty("AcceptsEverything", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _acceptsStash = _configType.GetProperty("AcceptsStash", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _serializeConfig = _configType.GetMethod("Serialize", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (_categoryType != null) { _isLinkedCategory = _configType.GetMethod("IsLinked", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { _categoryType }, null); } if (_groupType != null) { _isLinkedGroup = _configType.GetMethod("IsLinked", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { _groupType }, null); } } _categoryGroup = _categoryType?.GetProperty("Group", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _categoryId = _categoryType?.GetProperty("Id", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _groupId = _groupType?.GetProperty("Id", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _bound = _getConfig != null && _getCategory != null && _configType != null && _includeItems != null && _excludeItems != null; if (!_bound) { Plugin.Log.LogWarning((object)"[BO] Better Organisation 1.0.0 was found but its chest-rule API could not be bound; Innangard chest rules are untouched."); return; } Plugin.Log.LogInfo((object)"[BO] Bound Better Organisation ChestConfigStore.Get(Container) and CategoryService.GetCategory(ItemData)."); Plugin.Log.LogInfo((object)"[BO] BO categories and per-item include/exclude rules override Innangard chest destinations whenever that BO chest has rules."); } internal static void Refresh(bool forceSummary) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) if (!_installed || !_bound || (Object)(object)ObjectDB.instance == (Object)null) { return; } Container[] array; try { array = Resources.FindObjectsOfTypeAll<Container>(); } catch (Exception e) { if (!_reportedUnavailable) { _reportedUnavailable = true; Plugin.Log.LogWarning((object)("[BO] container scan failed: " + Bridge.Root(e))); } return; } int num = 0; int num2 = 0; Container[] array2 = array; foreach (Container val in array2) { if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null) { continue; } Scene scene = ((Component)val).gameObject.scene; if (!((Scene)(ref scene)).IsValid() || !((Component)val).gameObject.activeInHierarchy) { continue; } ZNetView containerView = GetContainerView(val); if (!((Object)(object)containerView == (Object)null) && containerView.IsValid() && containerView.GetZDO() != null) { RuleCache ruleCache = RefreshCache(val, containerView); if (ruleCache != null && ruleCache.HasAnyRule) { num2++; } if (containerView.IsOwner() && Restore(containerView.GetZDO())) { num++; } } } HashSet<int> liveIds = new HashSet<int>(from view in array.Select(GetContainerView) where (Object)(object)view != (Object)null select ((Object)view).GetInstanceID()); int[] array3 = RuleCaches.Keys.Where((int id) => !liveIds.Contains(id)).ToArray(); foreach (int key in array3) { RuleCaches.Remove(key); } if (forceSummary || num2 != _lastActive || num > 0) { _lastActive = num2; Plugin.Log.LogInfo((object)$"[BO] localRuleChests={num2} oldMirrorsRestored={num}"); } } private static RuleCache RefreshCache(Container container, ZNetView view) { object obj; try { obj = _getConfig.Invoke(null, new object[1] { container }); } catch { return null; } if (obj == null) { return null; } RuleSnapshot ruleSnapshot = ReadRules(obj); string text = ConfigSignature(obj, ruleSnapshot); int instanceID = ((Object)view).GetInstanceID(); if (RuleCaches.TryGetValue(instanceID, out var value) && value.Signature == text && value.HasAnyRule == ruleSnapshot.HasAnyRule) { return value; } value = new RuleCache { Signature = text, HasAnyRule = ruleSnapshot.HasAnyRule, Accepted = (ruleSnapshot.HasAnyRule ? BuildAcceptedSharedNames(obj, ruleSnapshot) : new HashSet<string>(StringComparer.Ordinal)) }; RuleCaches[instanceID] = value; return value; } internal static void RestoreAll() { if (!_bound) { return; } RuleCaches.Clear(); try { Container[] array = Resources.FindObjectsOfTypeAll<Container>(); for (int i = 0; i < array.Length; i++) { ZNetView containerView = GetContainerView(array[i]); if (!((Object)(object)containerView == (Object)null) && containerView.IsValid()) { ZDO zDO = containerView.GetZDO(); if (zDO != null && containerView.IsOwner()) { Restore(zDO); } } } } catch { } } private static ZNetView GetContainerView(Container container) { if (!((Object)(object)container != (Object)null)) { return null; } return ((Component)container).GetComponent<ZNetView>(); } private static RuleSnapshot ReadRules(object config) { RuleSnapshot ruleSnapshot = new RuleSnapshot(); try { ruleSnapshot.HasLinks = ReadBool(_hasLinks, config, fallback: false) || CollectionHasAny(_links?.GetValue(config, null)); ruleSnapshot.AcceptsEverything = ReadBool(_acceptsEverything, config, fallback: false); ruleSnapshot.AcceptsStash = ReadBool(_acceptsStash, config, fallback: true); CopyStrings(_includeItems?.GetValue(config, null), ruleSnapshot.Include); CopyStrings(_excludeItems?.GetValue(config, null), ruleSnapshot.Exclude); } catch (Exception e) { Plugin.Log.LogWarning((object)("[BO] rule read failed: " + Bridge.Root(e))); } return ruleSnapshot; } private static string ConfigSignature(object config, RuleSnapshot rules) { try { if (_serializeConfig != null) { object obj = _serializeConfig.Invoke(config, null); if (obj != null) { return obj.ToString() + "#odb=" + (ObjectDB.instance?.m_items?.Count).GetValueOrDefault(); } } } catch { } return string.Join(";", rules.HasLinks ? "L1" : "L0", rules.AcceptsEverything ? "A1" : "A0", rules.AcceptsStash ? "S1" : "S0", "I=" + string.Join(",", rules.Include.OrderBy<string, string>((string x) => x, StringComparer.OrdinalIgnoreCase)), "E=" + string.Join(",", rules.Exclude.OrderBy<string, string>((string x) => x, StringComparer.OrdinalIgnoreCase)), "ODB=" + (ObjectDB.instance?.m_items?.Count).GetValueOrDefault()); } private static HashSet<string> BuildAcceptedSharedNames(object config, RuleSnapshot rules) { HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); if (!rules.AcceptsStash) { return hashSet; } foreach (GameObject item in ObjectDB.instance.m_items) { if ((Object)(object)item == (Object)null) { continue; } ItemData val = item.GetComponent<ItemDrop>()?.m_itemData; if (val?.m_shared == null) { continue; } string name = ((Object)item).name; string name2 = val.m_shared.m_name; if (!string.IsNullOrEmpty(name2) && !rules.Exclude.Contains(name)) { if (rules.Include.Contains(name)) { hashSet.Add(name2); } else if (rules.AcceptsEverything || MatchesLinkedCategory(config, val)) { hashSet.Add(name2); } } } return hashSet; } private static bool MatchesLinkedCategory(object config, ItemData item) { if (config == null || item == null || _getCategory == null) { return false; } object obj; try { obj = _getCategory.Invoke(null, new object[1] { item }); } catch { return false; } if (obj == null) { return false; } try { if (_isLinkedCategory != null && Convert.ToBoolean(_isLinkedCategory.Invoke(config, new object[1] { obj }))) { return true; } object obj3 = _categoryGroup?.GetValue(obj, null); if (obj3 != null && _isLinkedGroup != null && Convert.ToBoolean(_isLinkedGroup.Invoke(config, new object[1] { obj3 }))) { return true; } object obj4 = _links?.GetValue(config, null); if (obj4 != null) { string wanted = _categoryId?.GetValue(obj, null) as string; string wanted2 = ((obj3 != null) ? (_groupId?.GetValue(obj3, null) as string) : null); if (ContainsString(obj4, "*") || ContainsString(obj4, wanted) || ContainsString(obj4, wanted2)) { return true; } } } catch { } return false; } private static bool Restore(ZDO zdo) { if (zdo == null || !zdo.GetBool("pho_bo_inn_mirror", false)) { return false; } SetBoolIfDifferent(zdo, "inn_sort_configured", zdo.GetBool("pho_bo_inn_b_cfg", false)); SetIntIfDifferent(zdo, "inn_sort_kinds", zdo.GetInt("pho_bo_inn_b_kinds", 0)); SetStringIfDifferent(zdo, "inn_sort_items", zdo.GetString("pho_bo_inn_b_items", string.Empty)); SetIntIfDifferent(zdo, "inn_chest_sort_kind", zdo.GetInt("pho_bo_inn_b_legacy", 0)); SetBoolIfDifferent(zdo, "pho_bo_inn_mirror", value: false); return true; } private static void SetBoolIfDifferent(ZDO zdo, string key, bool value) { if (zdo.GetBool(key, !value) != value) { zdo.Set(key, value); } } private static void SetIntIfDifferent(ZDO zdo, string key, int value) { if (zdo.GetInt(key, int.MinValue) != value) { zdo.Set(key, value); } } private static void SetStringIfDifferent(ZDO zdo, string key, string value) { if (zdo.GetString(key, (string)null) != value) { zdo.Set(key, value); } } private static bool ReadBool(PropertyInfo p, object instance, bool fallback) { try { object obj = p?.GetValue(instance, null); return (obj == null) ? fallback : Convert.ToBoolean(obj); } catch { return fallback; } } private static void CopyStrings(object collection, HashSet<string> into) { if (collection == null || !(collection is IEnumerable enumerable)) { return; } foreach (object item in enumerable) { string text = item as string; if (!string.IsNullOrEmpty(text)) { into.Add(text); } } } private static bool CollectionHasAny(object collection) { if (collection == null) { return false; } if (!(collection is IEnumerable enumerable)) { return false; } IEnumerator enumerator = enumerable.GetEnumerator(); try { return enumerator.MoveNext(); } finally { IDisposable disposable = enumerator as IDisposable; if (disposable != null) { disposable.Dispose(); } } } private static bool ContainsString(object collection, string wanted) { if (collection == null || string.IsNullOrEmpty(wanted)) { return false; } if (!(collection is IEnumerable enumerable)) { return false; } foreach (object item in enumerable) { if (string.Equals(item as string, wanted, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private static Type[] SafeTypes(Assembly a) { try { return a.GetTypes(); } catch (ReflectionTypeLoadException ex) { return ex.Types.Where((Type t) => t != null).ToArray(); } catch { return Array.Empty<Type>(); } } } }