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 HexAutoStorage v1.1.0
Plugins/HexAutoStorage.dll
Decompiled 12 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using HexAutoStorage.Configuration; using HexAutoStorage.Core; using HexAutoStorage.Features; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("HexAutoStorage")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("HexAutoStorage")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("536277cd-6e46-4758-853f-ae19c2ded793")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace HexAutoStorage { [BepInPlugin("com.hex.autostorage", "HexAutoStorage", "1.1.0")] public class Plugin : BaseUnityPlugin { private const string PluginGuid = "com.hex.autostorage"; private const string PluginName = "HexAutoStorage"; private const string PluginVersion = "1.1.0"; private Harmony _harmonyInstance; internal static ManualLogSource Log; internal static Plugin Instance; private void Awake() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; StorageConfig.Bind(((BaseUnityPlugin)this).Config); Assembly executingAssembly = Assembly.GetExecutingAssembly(); _harmonyInstance = new Harmony("com.hex.autostorage"); _harmonyInstance.PatchAll(executingAssembly); Log.LogInfo((object)"HexAutoStorage v1.1.0 loaded."); } private void OnDestroy() { Log.LogInfo((object)"HexAutoStorage v1.1.0 unloaded."); Harmony harmonyInstance = _harmonyInstance; if (harmonyInstance != null) { harmonyInstance.UnpatchSelf(); } _harmonyInstance = null; Instance = null; Log = null; } } internal static class AutoStorageService { private const long InvalidCreator = 0L; internal static bool TryStore(Smelter smelter, ItemDrop producedItem, int stack) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)smelter == (Object)null || (Object)(object)producedItem == (Object)null || stack <= 0) { return false; } Piece component = ((Component)smelter).GetComponent<Piece>(); if ((Object)(object)component == (Object)null) { return false; } long creator = component.GetCreator(); if (creator == 0L) { return false; } List<Container> list = FindNearbyContainers(((Component)smelter).transform.position, StorageConfig.StorageRadius.Value, creator); if (list.Count == 0) { return false; } string prefabName = ((Object)((Component)producedItem).gameObject).name; foreach (Container item in (from container in list where ContainerTagEditor.HasTag(container, prefabName) orderby Vector3.SqrMagnitude(((Component)container).transform.position - ((Component)smelter).transform.position) select container).ToList()) { if (TryAddToContainer(item, producedItem, stack)) { return true; } } foreach (Container item2 in (from container in list where !HasTags(container) && ContainsProducedItem(container, prefabName) orderby Vector3.SqrMagnitude(((Component)container).transform.position - ((Component)smelter).transform.position) select container).ToList()) { if (TryAddToContainer(item2, producedItem, stack)) { return true; } } foreach (Container item3 in (from container in list where !HasTags(container) && !ContainsProducedItem(container, prefabName) orderby Vector3.SqrMagnitude(((Component)container).transform.position - ((Component)smelter).transform.position) select container).ToList()) { if (TryAddToContainer(item3, producedItem, stack)) { return true; } } return false; } private static List<Container> FindNearbyContainers(Vector3 position, float radius, long creator) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Collider[] array = Physics.OverlapSphere(position, radius); HashSet<Container> hashSet = new HashSet<Container>(); Collider[] array2 = array; for (int i = 0; i < array2.Length; i++) { Piece componentInParent = ((Component)array2[i]).GetComponentInParent<Piece>(); if (!((Object)(object)componentInParent == (Object)null) && componentInParent.GetCreator() == creator && StoragePrefabRules.TryGetStorageType(((Object)((Component)componentInParent).gameObject).name.Replace("(Clone)", ""), out var _)) { Container componentInChildren = ((Component)componentInParent).GetComponentInChildren<Container>(true); if (!((Object)(object)componentInChildren == (Object)null)) { hashSet.Add(componentInChildren); } } } return hashSet.ToList(); } private static bool HasTags(Container container) { return !string.IsNullOrWhiteSpace(ContainerTagEditor.GetTags(container)); } private static bool ContainsProducedItem(Container container, string prefabName) { if ((Object)(object)container == (Object)null) { return false; } Inventory inventory = container.GetInventory(); if (inventory == null) { return false; } return inventory.GetItem(prefabName, -1, true) != null; } private static bool TryAddToContainer(Container container, ItemDrop producedItem, int stack) { if ((Object)(object)container == (Object)null || (Object)(object)producedItem == (Object)null) { return false; } Inventory inventory = container.GetInventory(); if (inventory == null) { return false; } ItemData val = producedItem.m_itemData.Clone(); val.m_stack = stack; if (!inventory.CanAddItem(val, stack)) { return false; } return inventory.AddItem(val); } } } namespace HexAutoStorage.Patches { [HarmonyPatch(typeof(Container), "GetHoverText")] [HarmonyAfter(new string[] { "shudnal.MyLittleUI" })] internal static class ContainerHoverTextPatch { [HarmonyPostfix] internal static void Postfix(Container __instance, ref string __result) { if ((Object)(object)Plugin.Instance == (Object)null || !StorageConfig.ModEnabled.Value || (Object)(object)__instance == (Object)null) { return; } Piece componentInParent = ((Component)__instance).GetComponentInParent<Piece>(); if (!((Object)(object)componentInParent == (Object)null) && StoragePrefabRules.TryGetStorageType(((Object)((Component)componentInParent).gameObject).name.Replace("(Clone)", ""), out var _)) { string editTagsShortcutDisplay = ContainerTagEditor.GetEditTagsShortcutDisplay(); string text = ContainerTagEditor.GetDisplayTags(__instance); if (string.IsNullOrWhiteSpace(text)) { text = "None"; } __result = __result + "\n[<color=#ffff00ff><b>" + editTagsShortcutDisplay + "</b></color>] Edit Auto Storage Tags"; __result = __result + "\n[<color=#ffff00ff><b>Tags:</b></color> " + text + "]"; } } } [HarmonyPatch(typeof(Player), "FindHoverObject")] internal static class PlayerFindHoverObjectPatch { [HarmonyPostfix] internal static void Postfix(Player __instance, GameObject hover) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } ContainerTagEditor.HoveredObject = hover; if ((Object)(object)hover == (Object)null) { StorageRadiusVisualizer.HoveredSmelter = null; return; } Smelter componentInParent = hover.GetComponentInParent<Smelter>(); if ((Object)(object)componentInParent == (Object)null) { StorageRadiusVisualizer.HoveredSmelter = null; return; } string text = ((Object)((Component)componentInParent).gameObject).name.Replace("(Clone)", ""); Plugin.Log.LogDebug((object)("FindHoverObject pathc Prefab name: " + text)); if (!StoragePrefabRules.IsSupportedSmelter(text)) { StorageRadiusVisualizer.HoveredSmelter = null; } else { StorageRadiusVisualizer.HoveredSmelter = componentInParent; } } } [HarmonyPatch(typeof(Player), "Start")] internal static class PlayerStartPatch { [HarmonyPostfix] internal static void Postfix(Player __instance) { if (!((Object)(object)Plugin.Instance == (Object)null) && !((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && (Object)(object)((Component)__instance).GetComponent<ContainerTagEditor>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<ContainerTagEditor>(); } } } [HarmonyPatch(typeof(Smelter), "Awake")] internal static class SmelterAwakePatch { [HarmonyPostfix] internal static void Postfix(Smelter __instance) { if (!((Object)(object)Plugin.Instance == (Object)null) && !((Object)(object)__instance == (Object)null) && StoragePrefabRules.IsSupportedSmelter(((Object)((Component)__instance).gameObject).name.Replace("(Clone)", "")) && (Object)(object)((Component)__instance).GetComponent<StorageRadiusVisualizer>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<StorageRadiusVisualizer>(); } } } [HarmonyPatch(typeof(Smelter), "Spawn")] internal static class SmelterSpawnPatch { [HarmonyPrefix] internal static bool Prefix(Smelter __instance, string ore, int stack) { //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Plugin.Instance == (Object)null || !StorageConfig.ModEnabled.Value) { return true; } if (!StoragePrefabRules.IsSupportedSmelter(((Object)((Component)__instance).gameObject).name.Replace("(Clone)", ""))) { return true; } ItemConversion val = null; foreach (ItemConversion item in __instance.m_conversion) { if ((Object)(object)item.m_from != (Object)null && ((Object)((Component)item.m_from).gameObject).name == ore) { val = item; break; } } if (val == null || (Object)(object)val.m_to == (Object)null) { return true; } if (!AutoStorageService.TryStore(__instance, val.m_to, stack)) { return true; } __instance.m_produceEffects.Create(((Component)__instance).transform.position, ((Component)__instance).transform.rotation, (Transform)null, 1f, -1); return false; } } [HarmonyPatch(typeof(TextInput), "setText")] internal static class TextInputSetTextPatch { [HarmonyPostfix] internal static void Postfix(string text) { if (ContainerTagEditor.IsEditing) { ContainerTagEditor.SaveTags(text); } } } [HarmonyPatch(typeof(TextInput), "Hide")] internal static class TextInputHidePatch { [HarmonyPostfix] internal static void Postfix() { if (ContainerTagEditor.IsEditing) { ContainerTagEditor.CancelEditing(); } } } } namespace HexAutoStorage.Features { internal class ContainerTagEditor : MonoBehaviour { private const string TagsKey = "HexAutoStorage_Tags"; private const int MaxTagLength = 200; private const string TagEditorTitle = "Auto Storage Tags - Example: Copper,Tin,Bronze,Iron,Silver,Blackmetal,Flametal,Coal,Flour,Eitr,Linen"; private static readonly Dictionary<string, string> ValidTags = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "Copper", "Copper" }, { "Tin", "Tin" }, { "Bronze", "Bronze" }, { "Iron", "Iron" }, { "Silver", "Silver" }, { "Blackmetal", "BlackMetal" }, { "Flametal", "FlametalNew" }, { "Coal", "Coal" }, { "Flour", "BarleyFlour" }, { "Eitr", "Eitr" }, { "Linen", "LinenThread" } }; private static readonly Dictionary<string, string> DisplayTags = ValidTags.ToDictionary<KeyValuePair<string, string>, string, string>((KeyValuePair<string, string> entry) => entry.Value, (KeyValuePair<string, string> entry) => entry.Key, StringComparer.OrdinalIgnoreCase); private static readonly MethodInfo TextInputShowMethod = AccessTools.Method(typeof(TextInput), "Show", new Type[3] { typeof(string), typeof(string), typeof(int) }, (Type[])null); private static Container _editingContainer; internal static GameObject HoveredObject; internal static bool IsEditing => (Object)(object)_editingContainer != (Object)null; internal unsafe static string GetEditTagsShortcutDisplay() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) KeyboardShortcut value = StorageConfig.EditTagsShortcut.Value; List<string> list = ((KeyboardShortcut)(ref value)).Modifiers.Select((KeyCode modifier) => ((object)(*(KeyCode*)(&modifier))/*cast due to .constrained prefix*/).ToString()).ToList(); list.Add(((object)((KeyboardShortcut)(ref value)).MainKey/*cast due to .constrained prefix*/).ToString()); return string.Join(" + ", list); } private void Update() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Plugin.Instance == (Object)null) && StorageConfig.ModEnabled.Value && !((Object)(object)Player.m_localPlayer == (Object)null) && !IsEditing) { KeyboardShortcut value = StorageConfig.EditTagsShortcut.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { TryOpenHoveredContainer(); } } } private void TryOpenHoveredContainer() { if ((Object)(object)HoveredObject == (Object)null) { return; } Piece componentInParent = HoveredObject.GetComponentInParent<Piece>(); if ((Object)(object)componentInParent == (Object)null) { return; } string text = ((Object)((Component)componentInParent).gameObject).name.Replace("(Clone)", ""); if (!StoragePrefabRules.TryGetStorageType(text, out var _)) { return; } Container componentInChildren = ((Component)componentInParent).GetComponentInChildren<Container>(true); ZNetView component = ((Component)componentInParent).GetComponent<ZNetView>(); if (!((Object)(object)componentInChildren == (Object)null) && !((Object)(object)component == (Object)null) && component.IsValid()) { if ((Object)(object)TextInput.instance == (Object)null) { Plugin.Log.LogDebug((object)"Unable to open Auto Storage tag editor because TextInput.instance is null."); return; } _editingContainer = componentInChildren; TextInputShowMethod.Invoke(TextInput.instance, new object[3] { "Auto Storage Tags - Example: Copper,Tin,Bronze,Iron,Silver,Blackmetal,Flametal,Coal,Flour,Eitr,Linen", GetDisplayTags(componentInChildren), 200 }); Plugin.Log.LogDebug((object)("Opened Auto Storage tag editor for " + text + ".")); } } private static bool TryNormalizeTags(string value, out string normalizedTags, out string invalidTag) { normalizedTags = ""; invalidTag = ""; if (string.IsNullOrWhiteSpace(value)) { return true; } List<string> list = new List<string>(); string[] array = value.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (!string.IsNullOrWhiteSpace(text)) { if (!ValidTags.TryGetValue(text, out var value2)) { invalidTag = text; return false; } if (!list.Contains<string>(value2, StringComparer.OrdinalIgnoreCase)) { list.Add(value2); } } } normalizedTags = string.Join(",", list); return true; } internal static string GetTags(Container container) { if (!TryGetContainerZNetView(container, out var nview)) { return ""; } return nview.GetZDO().GetString("HexAutoStorage_Tags", ""); } internal static bool HasTag(Container container, string prefabName) { if (string.IsNullOrWhiteSpace(prefabName)) { return false; } string tags = GetTags(container); if (string.IsNullOrWhiteSpace(tags)) { return false; } return tags.Split(new char[1] { ',' }).Any((string tag) => tag.Trim().Equals(prefabName, StringComparison.OrdinalIgnoreCase)); } internal static void SaveTags(string text) { if ((Object)(object)_editingContainer == (Object)null) { return; } string normalizedTags; string invalidTag; if (!TryGetContainerZNetView(_editingContainer, out var nview)) { _editingContainer = null; } else if (!TryNormalizeTags(text, out normalizedTags, out invalidTag)) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, "Invalid Auto Storage tag: " + invalidTag, 0, (Sprite)null); } Plugin.Log.LogDebug((object)("Invalid Auto Storage tag '" + invalidTag + "' rejected.")); _editingContainer = null; } else { Piece componentInParent = ((Component)_editingContainer).GetComponentInParent<Piece>(); nview.GetZDO().Set("HexAutoStorage_Tags", normalizedTags); Plugin.Log.LogDebug((object)("Saved Auto Storage tags '" + normalizedTags + "' to " + (((componentInParent != null) ? ((Object)((Component)componentInParent).gameObject).name : null) ?? "container") + ".")); _editingContainer = null; } } internal static void CancelEditing() { _editingContainer = null; } internal static string GetDisplayTags(Container container) { string tags = GetTags(container); if (string.IsNullOrWhiteSpace(tags)) { return ""; } List<string> list = new List<string>(); string[] array = tags.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (DisplayTags.TryGetValue(text, out var value)) { list.Add(value); } else { list.Add(text); } } return string.Join(",", list); } private static bool TryGetContainerZNetView(Container container, out ZNetView nview) { nview = null; if ((Object)(object)container == (Object)null) { return false; } Piece componentInParent = ((Component)container).GetComponentInParent<Piece>(); if ((Object)(object)componentInParent == (Object)null) { return false; } nview = ((Component)componentInParent).GetComponent<ZNetView>(); if ((Object)(object)nview != (Object)null) { return nview.IsValid(); } return false; } } internal class StorageRadiusVisualizer : MonoBehaviour { private const int SegmentCount = 96; private const float LineWidth = 0.4f; private const float HeightOffset = 0.05f; private const float RaycastHeight = 20f; private const float RaycastDistance = 50f; private LineRenderer _lineRenderer; private Smelter _smelter; private int _terrainMask; internal static Smelter HoveredSmelter; private void Awake() { //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown _smelter = ((Component)this).GetComponent<Smelter>(); if ((Object)(object)_smelter == (Object)null) { Plugin.Log.LogDebug((object)("StorageRadiusVisualizer attached to non-Smelter object: " + ((Object)((Component)this).gameObject).name)); Object.Destroy((Object)(object)this); return; } _terrainMask = LayerMask.GetMask(new string[1] { "terrain" }); _lineRenderer = ((Component)this).gameObject.AddComponent<LineRenderer>(); _lineRenderer.loop = true; _lineRenderer.useWorldSpace = true; _lineRenderer.positionCount = 96; _lineRenderer.startWidth = 0.4f; _lineRenderer.endWidth = 0.4f; ((Renderer)_lineRenderer).material = new Material(Shader.Find("Sprites/Default")); ((Renderer)_lineRenderer).enabled = false; } private void Update() { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //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_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_lineRenderer == (Object)null) { return; } if ((Object)(object)Plugin.Instance == (Object)null || !StorageConfig.ModEnabled.Value || !StorageConfig.ShowStorageRadius.Value || (Object)(object)HoveredSmelter != (Object)(object)_smelter) { ((Renderer)_lineRenderer).enabled = false; return; } ((Renderer)_lineRenderer).enabled = true; float value = StorageConfig.StorageRadius.Value; RaycastHit val2 = default(RaycastHit); for (int i = 0; i < 96; i++) { float num = (float)i * (float)Math.PI * 2f / 96f; Vector3 val = ((Component)this).transform.position + new Vector3(Mathf.Cos(num) * value, 0f, Mathf.Sin(num) * value); if (Physics.Raycast(val + Vector3.up * 20f, Vector3.down, ref val2, 50f, _terrainMask)) { val.y = ((RaycastHit)(ref val2)).point.y + 0.05f; } else { val.y = ((Component)this).transform.position.y + 0.05f; } _lineRenderer.SetPosition(i, val); } } private void OnDestroy() { if (!((Object)(object)_lineRenderer == (Object)null)) { if ((Object)(object)((Renderer)_lineRenderer).material != (Object)null) { Object.Destroy((Object)(object)((Renderer)_lineRenderer).material); } Object.Destroy((Object)(object)_lineRenderer); _lineRenderer = null; } } } } namespace HexAutoStorage.Core { internal enum StorageType { Chest, Barrel, Cart } internal static class StoragePrefabRules { private static readonly HashSet<string> SupportedSmelterPrefabs = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "smelter", "charcoal_kiln", "blastfurnace", "windmill", "eitrrefinery", "piece_spinningwheel" }; private static readonly Dictionary<string, StorageType> SupportedPrefabs = new Dictionary<string, StorageType>(StringComparer.OrdinalIgnoreCase) { { "piece_chest_wood", StorageType.Chest }, { "piece_chest_barrel", StorageType.Barrel }, { "piece_chest", StorageType.Chest }, { "piece_chest_blackmetal", StorageType.Chest }, { "piece_chest_private", StorageType.Chest }, { "Cart", StorageType.Cart } }; internal static bool TryGetStorageType(string prefabName, out StorageType storageType) { return SupportedPrefabs.TryGetValue(prefabName, out storageType); } internal static bool IsSupportedSmelter(string prefabName) { return SupportedSmelterPrefabs.Contains(prefabName); } } } namespace HexAutoStorage.Configuration { internal static class StorageConfig { internal static ConfigEntry<bool> ModEnabled; internal static ConfigEntry<float> StorageRadius; internal static ConfigEntry<KeyboardShortcut> EditTagsShortcut; internal static ConfigEntry<bool> ShowStorageRadius; internal static void Bind(ConfigFile config) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_0089: Unknown result type (might be due to invalid IL or missing references) ModEnabled = config.Bind<bool>("General", "Enabled", true, "Enable or disable HexAutoStorage."); StorageRadius = config.Bind<float>("General", "StorageRadius", 5f, new ConfigDescription("Radius in meters that production stations search for nearby containers.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), Array.Empty<object>())); ShowStorageRadius = config.Bind<bool>("General", "ShowRadiusVisual", false, "Show storage radius outline."); EditTagsShortcut = config.Bind<KeyboardShortcut>("Input", "EditTagsShortcut", new KeyboardShortcut((KeyCode)112, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), "Keyboard shortcut used to edit Auto Storage tags while looking at a container. Supported tags: Copper, Tin, Bronze, Iron, Silver, Blackmetal, Flametal, Coal, Flour, Eitr, Linen"); } } }