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 Workshop Auto Sort v1.0.0
plugins/CraftingChestSorter.dll
Decompiled 4 days agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [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("CraftingChestSorter")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("CraftingChestSorter")] [assembly: AssemblyTitle("CraftingChestSorter")] [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 BenjSammy.CraftingChestSorter { [BepInPlugin("benjsammy.craftingchestsorter", "Workshop Auto Sort by BenjSammy", "1.0.0")] public sealed class CraftingChestSorter : BaseUnityPlugin { private sealed class OpenRequest { public bool? Granted; public bool Manual; } private const string Id = "benjsammy.craftingchestsorter"; private const string SaveKey = "benjsammy.craftingchestsorter.protected"; private static readonly ConditionalWeakTable<Player, Inventory> Protected = new ConditionalWeakTable<Player, Inventory>(); private static readonly FieldRef<Player, Dictionary<string, string>> CustomData = AccessTools.FieldRefAccess<Player, Dictionary<string, string>>("m_customData"); private static readonly FieldRef<Container, ZNetView> View = AccessTools.FieldRefAccess<Container, ZNetView>("m_nview"); private static readonly string[] ChestPrefabs = new string[4] { "piece_chest_wood", "piece_chest", "piece_chest_blackmetal", "piece_chest_private" }; private static ConfigEntry<float> radius; private static HashSet<string> foodPrefabs; private static MethodInfo checkAccess; private static ManualLogSource log; private static CraftingChestSorter instance; private static readonly Dictionary<Container, OpenRequest> Requests = new Dictionary<Container, OpenRequest>(); private static MethodInfo reloadChest; private Coroutine sorting; private int sortGeneration; private Harmony harmony; private void Awake() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Expected O, but got Unknown radius = ((BaseUnityPlugin)this).Config.Bind<float>("Transfer", "Chest radius metres", 60f, new ConfigDescription("Maximum distance from the crafting station in metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 60f), Array.Empty<object>())); log = ((BaseUnityPlugin)this).Logger; checkAccess = AccessTools.Method(typeof(Container), "CheckAccess", new Type[1] { typeof(long) }, (Type[])null); using (StreamReader streamReader = new StreamReader(typeof(CraftingChestSorter).Assembly.GetManifestResourceStream("ExcludedFood.txt"))) { foodPrefabs = new HashSet<string>(StringComparer.Ordinal); string item; while ((item = streamReader.ReadLine()) != null) { foodPrefabs.Add(item); } } instance = this; harmony = new Harmony("benjsammy.craftingchestsorter"); reloadChest = AccessTools.Method(typeof(Container), "Load", Type.EmptyTypes, (Type[])null); MethodInfo methodInfo = AccessTools.Method(typeof(Container), "RPC_OpenRespons", new Type[2] { typeof(long), typeof(bool) }, (Type[])null) ?? AccessTools.Method(typeof(Container), "RPC_OpenResponse", new Type[2] { typeof(long), typeof(bool) }, (Type[])null); if (methodInfo != null && reloadChest != null) { harmony.Patch((MethodBase)methodInfo, Hook("HandleOpenResponse"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(Container), "Interact", new Type[3] { typeof(Humanoid), typeof(bool), typeof(bool) }, (Type[])null), Hook("AllowManualOpen"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { reloadChest = null; ((BaseUnityPlugin)this).Logger.LogWarning((object)"Multiplayer chest ownership API unavailable."); } harmony.Patch((MethodBase)AccessTools.Method(typeof(InventoryGui), "UpdateInventory", new Type[1] { typeof(Player) }, (Type[])null), (HarmonyMethod)null, Hook("RecoverLegacyItems"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(Player), "Save", new Type[1] { typeof(ZPackage) }, (Type[])null), Hook("SaveProtectedInventory"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(Player), "SetCraftingStation", new Type[1] { typeof(CraftingStation) }, (Type[])null), (HarmonyMethod)null, Hook("AfterStationOpened"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static HarmonyMethod Hook(string name) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown return new HarmonyMethod(typeof(CraftingChestSorter), name, (Type[])null); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } Requests.Clear(); instance = null; } private static bool HandleOpenResponse(Container __instance, bool __1) { if (!Requests.TryGetValue(__instance, out var value)) { return true; } Requests.Remove(__instance); value.Granted = __1; return value.Manual; } private static void AllowManualOpen(Container __instance, Humanoid __0, bool __1) { if (!__1 && (Object)(object)__0 == (Object)(object)Player.m_localPlayer && Requests.TryGetValue(__instance, out var value)) { value.Manual = true; } } private static bool IsSolo() { ZNet val = ZNet.instance; if ((Object)(object)val != (Object)null && val.IsServer() && !val.IsDedicated()) { return val.GetPeerConnections() == 0; } return false; } private static Inventory LoadProtectedInventory(Player player) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown Inventory val = new Inventory("Protected transfer", (Sprite)null, 8, 1); if (CustomData.Invoke(player).TryGetValue("benjsammy.craftingchestsorter.protected", out var value)) { val.Load(new ZPackage(value)); } return val; } private static void SaveProtectedInventory(Player __instance) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown if (!Protected.TryGetValue(__instance, out var value)) { return; } try { if (value.GetAllItems().Count == 0) { CustomData.Invoke(__instance).Remove("benjsammy.craftingchestsorter.protected"); return; } ZPackage val = new ZPackage(); value.Save(val); CustomData.Invoke(__instance)["benjsammy.craftingchestsorter.protected"] = val.GetBase64(); } catch (Exception ex) { log.LogError((object)("Protected row save failed: " + ex.GetBaseException())); throw; } } private static void RecoverLegacyItems(Player __0) { if ((Object)(object)__0 == (Object)null || (Object)(object)__0 != (Object)(object)Player.m_localPlayer || !IsSolo() || !CustomData.Invoke(__0).ContainsKey("benjsammy.craftingchestsorter.protected")) { return; } Inventory inventory = ((Humanoid)__0).GetInventory(); if (inventory == null) { return; } Inventory value = Protected.GetValue(__0, LoadProtectedInventory); ItemData[] array = value.GetAllItems().ToArray(); foreach (ItemData val in array) { if (inventory.CanAddItem(val, val.m_stack)) { inventory.MoveItemToThis(value, val); } } SaveProtectedInventory(__0); } private static void AfterStationOpened(Player __instance, CraftingStation __0) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || (Object)(object)instance == (Object)null) { return; } instance.sortGeneration++; if (instance.sorting != null) { ((MonoBehaviour)instance).StopCoroutine(instance.sorting); } if ((Object)(object)__0 == (Object)null || (Object)(object)ZNet.instance == (Object)null) { return; } if (!IsSolo()) { if (reloadChest != null) { instance.sorting = ((MonoBehaviour)instance).StartCoroutine(instance.SortOnline(__instance, __0, instance.sortGeneration)); } return; } try { TransferToChests(__instance, __0); } catch (Exception ex) { log.LogWarning((object)("Automatic transfer stopped: " + ex.GetBaseException())); } } private static void TransferToChests(Player player, CraftingStation station) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return; } Vector3 origin = ((Component)station).transform.position; float rangeSquared = radius.Value * radius.Value; Container[] array = (from chest in Object.FindObjectsOfType<Container>().Where(delegate(Container chest) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((Component)chest).transform.position - origin; return ((Vector3)(ref val)).sqrMagnitude <= rangeSquared; }) where CanUse(chest, player) && View.Invoke(chest).IsOwner() select chest).OrderBy(delegate(Container chest) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((Component)chest).transform.position - origin; return ((Vector3)(ref val)).sqrMagnitude; }).ToArray(); ItemData[] array2 = inventory.GetAllItems().ToArray(); foreach (ItemData item in array2) { if (!IsEligible(item)) { continue; } Container[] array3 = array; for (int num2 = 0; num2 < array3.Length; num2++) { Inventory inventory2 = array3[num2].GetInventory(); if (inventory2 != null && inventory2.GetAllItems().Any((ItemData existing) => SameItem(existing, item)) && inventory2.CanAddItem(item, item.m_stack)) { inventory2.MoveItemToThis(inventory, item); break; } } } } private static bool IsEligible(ItemData item) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Invalid comparison between Unknown and I4 //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Invalid comparison between Unknown and I4 //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Invalid comparison between Unknown and I4 //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Invalid comparison between Unknown and I4 if (item.m_gridPos.x >= 0 && item.m_gridPos.x < 8 && item.m_gridPos.y >= 1 && item.m_gridPos.y <= 3 && !item.m_equipped && item.m_shared != null && item.m_shared.m_teleportable && !item.IsEquipable() && (int)item.m_shared.m_itemType != 2 && (int)item.m_shared.m_itemType != 21 && (int)item.m_shared.m_itemType != 9 && (int)item.m_shared.m_itemType != 23 && (Object)(object)item.m_dropPrefab != (Object)null) { return !foodPrefabs.Contains(((Object)item.m_dropPrefab).name.Replace("(Clone)", "").TrimEnd()); } return false; } private IEnumerator SortOnline(Player player, CraftingStation station, int generation) { yield return null; Inventory source = ((Humanoid)player).GetInventory(); if (source == null) { yield break; } List<ItemData> remaining = source.GetAllItems().Where(IsEligible).ToList(); Vector3 origin = ((Component)station).transform.position; float rangeSquared = radius.Value * radius.Value; Container[] array = Object.FindObjectsOfType<Container>().Where(delegate(Container val2) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((Component)val2).transform.position - origin; return ((Vector3)(ref val)).sqrMagnitude <= rangeSquared && CanUse(val2, player); }).OrderBy(delegate(Container val2) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((Component)val2).transform.position - origin; return ((Vector3)(ref val)).sqrMagnitude; }) .ToArray(); Container[] array2 = array; foreach (Container chest in array2) { if (remaining.Count == 0 || !IsCurrent(player, station, generation)) { break; } if ((Object)(object)chest == (Object)null || !CanUse(chest, player)) { continue; } Inventory destination = chest.GetInventory(); if (destination == null || !remaining.Any((ItemData item) => destination.GetAllItems().Any((ItemData existing) => SameItem(existing, item)))) { continue; } ZNetView view = View.Invoke(chest); if (Requests.ContainsKey(chest)) { continue; } if (!view.IsOwner()) { OpenRequest request = new OpenRequest(); Requests[chest] = request; view.InvokeRPC("RPC_RequestOpen", new object[1] { player.GetPlayerID() }); float deadline = Time.realtimeSinceStartup + 2f; while (IsCurrent(player, station, generation) && (Object)(object)chest != (Object)null && !request.Manual && request.Granted != false && (request.Granted != true || !view.IsOwner()) && Time.realtimeSinceStartup < deadline) { yield return null; } if (!IsCurrent(player, station, generation)) { break; } if ((Object)(object)chest == (Object)null || request.Manual || request.Granted != true || !view.IsOwner()) { continue; } } try { if (CanUse(chest, player) && view.IsOwner()) { reloadChest.Invoke(chest, null); Deposit(player, source, chest, remaining); } } catch (Exception ex) { log.LogWarning((object)("Automatic transfer stopped: " + ex.GetBaseException())); break; } } } private bool IsCurrent(Player player, CraftingStation station, int generation) { if (generation == sortGeneration && (Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer && (Object)(object)station != (Object)null && (Object)(object)player.GetCurrentCraftingStation() == (Object)(object)station) { return InventoryGui.IsVisible(); } return false; } private static void Deposit(Player player, Inventory source, Container chest, List<ItemData> remaining) { Inventory inventory = chest.GetInventory(); if (inventory == null) { return; } ItemData[] array = remaining.ToArray(); foreach (ItemData item in array) { if (!source.GetAllItems().Contains(item) || !IsEligible(item)) { remaining.Remove(item); continue; } if (!View.Invoke(chest).IsOwner() || !CanUse(chest, player)) { break; } if (inventory.GetAllItems().Any((ItemData existing) => SameItem(existing, item)) && inventory.CanAddItem(item, item.m_stack)) { inventory.MoveItemToThis(source, item); remaining.Remove(item); } } } private static bool SameItem(ItemData existing, ItemData item) { if ((Object)(object)existing.m_dropPrefab != (Object)null && (Object)(object)item.m_dropPrefab != (Object)null && ((Object)existing.m_dropPrefab).name == ((Object)item.m_dropPrefab).name && existing.m_quality == item.m_quality) { return existing.m_variant == item.m_variant; } return false; } private static bool CanUse(Container chest, Player player) { //IL_0091: Unknown result type (might be due to invalid IL or missing references) string value = ((Object)((Component)chest).gameObject).name.Replace("(Clone)", "").TrimEnd(); ZNetView val = View.Invoke(chest); if (Array.IndexOf(ChestPrefabs, value) >= 0 && (Object)(object)val != (Object)null && val.IsValid() && !chest.IsInUse()) { object obj = checkAccess?.Invoke(chest, new object[1] { player.GetPlayerID() }); if (obj is bool && (bool)obj) { return PrivateArea.CheckAccess(((Component)chest).transform.position, 0f, false, false); } } return false; } } }