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 UFO Storage Manager v1.0.0
UFOStorageManager.dll
Decompiled 11 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using Unity.Collections; using Unity.Netcode; 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("UFOStorageManager")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("UFOStorageManager")] [assembly: AssemblyTitle("UFOStorageManager")] [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 UFOStorageManager { internal static class DefaultPosition { private static readonly string[] SaveKeys = new string[3] { "ShipUnlockMoved_", "ShipUnlockPos_", "ShipUnlockRot_" }; internal static void Prepare(int unlockableId) { ResetLocal(unlockableId); DeleteSavedPosition(unlockableId); StorageNetwork.BroadcastResetDefault(unlockableId); } internal static void ResetLocal(int unlockableId) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) List<UnlockableItem> unlockables = StorageData.Unlockables; if (unlockables == null || unlockableId < 0 || unlockableId >= unlockables.Count) { return; } UnlockableItem val = unlockables[unlockableId]; PlaceableShipObject val2 = Find(unlockableId); if ((Object)(object)val2 != (Object)null) { if (!val.spawnPrefab) { if ((Object)(object)ShipBuildModeManager.Instance != (Object)null) { ShipBuildModeManager.Instance.ResetShipObjectToDefaultPosition(val2); } } else { MoveToPrefabDefault(val, val2); } } val.placedPosition = Vector3.zero; val.placedRotation = Vector3.zero; val.hasBeenMoved = false; } private static void MoveToPrefabDefault(UnlockableItem item, PlaceableShipObject placeable) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) AutoParentToShip val = (((Object)(object)item.prefabObject != (Object)null) ? item.prefabObject.GetComponentInChildren<AutoParentToShip>(true) : null); if (!((Object)(object)val == (Object)null) && val.overrideOffset && !((Object)(object)placeable.parentObjectSecondary != (Object)null)) { placeable.parentObject.positionOffset = val.positionOffset; placeable.parentObject.rotationOffset = val.rotationOffset; placeable.parentObject.MoveToOffset(); } } private static PlaceableShipObject Find(int unlockableId) { PlaceableShipObject[] array = Object.FindObjectsOfType<PlaceableShipObject>(); foreach (PlaceableShipObject val in array) { if ((Object)(object)val != (Object)null && val.unlockableID == unlockableId && (Object)(object)val.parentObject != (Object)null) { return val; } } return null; } private static void DeleteSavedPosition(int unlockableId) { GameNetworkManager instance = GameNetworkManager.Instance; List<UnlockableItem> unlockables = StorageData.Unlockables; if ((Object)(object)instance == (Object)null || string.IsNullOrEmpty(instance.currentSaveFileName) || unlockables == null) { return; } string unlockableName = unlockables[unlockableId].unlockableName; try { string[] saveKeys = SaveKeys; foreach (string text in saveKeys) { if (ES3.KeyExists(text + unlockableName, instance.currentSaveFileName)) { ES3.DeleteKey(text + unlockableName, instance.currentSaveFileName); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not clear the saved position of " + unlockableName + ": " + ex.Message)); } } } [HarmonyPatch] internal static class Patches { [HarmonyPatch(typeof(Terminal), "ParsePlayerSentence")] [HarmonyPrefix] [HarmonyPriority(600)] private static bool ParsePlayerSentencePrefix(Terminal __instance, ref TerminalNode __result) { if ((Object)(object)__instance.screenText == (Object)null) { return true; } string text = __instance.screenText.text ?? ""; int textAdded = __instance.textAdded; if (textAdded <= 0 || textAdded > text.Length) { return true; } try { if (StorageMenu.TryHandle(__instance, text.Substring(text.Length - textAdded), out var result)) { __result = result; return false; } } catch (Exception arg) { Plugin.Log.LogError((object)$"Storage menu error: {arg}"); StorageMenu.Reset(); } return true; } [HarmonyPatch(typeof(Terminal), "QuitTerminal")] [HarmonyPostfix] private static void QuitTerminalPostfix() { StorageMenu.Reset(); } [HarmonyPatch(typeof(Terminal), "BeginUsingTerminal")] [HarmonyPrefix] private static void BeginUsingTerminalPrefix() { StorageMenu.Reset(); } [HarmonyPatch(typeof(ShipBuildModeManager), "StoreObjectLocalClient")] [HarmonyPrefix] private static bool StoreObjectLocalClientPrefix(ShipBuildModeManager __instance, PlaceableShipObject ___placingObject, float ___timeSincePlacingObject) { if (!__instance.InBuildMode || (Object)(object)___placingObject == (Object)null || ___timeSincePlacingObject <= 0.25f || !StorageNetwork.LimitBuildMode) { return true; } int limit = StorageNetwork.Limit; if (limit <= 0) { return true; } int num = StorageData.StoredCount(); if (num < limit) { return true; } __instance.CancelBuildMode(true); HUDManager.Instance.DisplayTip("Storage full!", $"Storage limit reached ({num}/{limit}). Retrieve something in the terminal (STORAGE).", true, false, "LC_Tip1"); return false; } [HarmonyPatch(typeof(PlayerControllerB), "ConnectClientToPlayerObject")] [HarmonyPostfix] private static void ConnectClientPostfix() { StorageNetwork.Register(); if ((Object)(object)NetworkManager.Singleton != (Object)null && !NetworkManager.Singleton.IsServer) { StorageNetwork.SendHello(); } } } [BepInPlugin("UFO.StorageManager", "UFO Storage Manager", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string Guid = "UFO.StorageManager"; public const string Name = "UFO Storage Manager"; public const string Version = "1.0.0"; internal static ManualLogSource Log; internal static ConfigEntry<int> StorageLimit; internal static ConfigEntry<bool> HostOnly; internal static ConfigEntry<bool> LimitBuildMode; private void Awake() { //IL_0071: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; StorageLimit = ((BaseUnityPlugin)this).Config.Bind<int>("Host", "StorageLimit", 10, "How many objects can be in storage at once. 0 = no limit. Host setting."); HostOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("Host", "HostOnly", false, "Only the host can store and retrieve objects from the terminal. Host setting."); LimitBuildMode = ((BaseUnityPlugin)this).Config.Bind<bool>("Host", "LimitBuildMode", true, "The storage limit also applies when storing objects in build mode (X key). Host setting."); new Harmony("UFO.StorageManager").PatchAll(typeof(Plugin).Assembly); Log.LogInfo((object)"UFO Storage Manager 1.0.0 loaded."); } } internal enum StorageAction : byte { Store = 1, Retrieve, StoreAll, RetrieveAll } internal static class StorageActions { private const int NoPlayer = -1; internal static string Execute(StorageAction action, ulong objectId, int unlockableId, int limit, bool toDefault) { if ((Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)ShipBuildModeManager.Instance == (Object)null) { return "The ship is not ready."; } return action switch { StorageAction.Store => Store(objectId, limit), StorageAction.Retrieve => Retrieve(unlockableId, toDefault), StorageAction.StoreAll => StoreAll(limit), StorageAction.RetrieveAll => RetrieveAll(toDefault), _ => "Unknown action.", }; } private static string Store(ulong objectId, int limit) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) DeployedObject deployedObject = StorageData.GetDeployed().Find((DeployedObject d) => d.ObjectId == objectId); if (deployedObject == null) { return "That object is no longer on the ship."; } if (deployedObject.Problem != null) { return "Can't store " + deployedObject.Label + ": " + deployedObject.Problem + "."; } int num = StorageData.StoredCount(); if (limit > 0 && num >= limit) { return $"Storage is full ({num}/{limit}). Retrieve something first."; } ShipBuildModeManager.Instance.StoreObjectServerRpc(new NetworkObjectReference(deployedObject.NetworkObject), -1); return deployedObject.Label + " stored."; } private static string Retrieve(int unlockableId, bool toDefault) { StoredObject storedObject = StorageData.GetStored().Find((StoredObject s) => s.UnlockableId == unlockableId); if (storedObject == null) { return "That object is not in storage."; } if (toDefault) { DefaultPosition.Prepare(unlockableId); } StartOfRound.Instance.ReturnUnlockableFromStorageServerRpc(unlockableId); if (!toDefault) { return storedObject.Label + " retrieved to its last position."; } return storedObject.Label + " retrieved to its default position."; } private static string StoreAll(int limit) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) int num = StorageData.StoredCount(); int num2 = 0; int num3 = 0; List<string> list = new List<string>(); foreach (DeployedObject item in StorageData.GetDeployed()) { if (item.Storable) { if (item.Problem != null) { list.Add(item.Label + " (" + item.Problem + ")"); continue; } if (limit > 0 && num >= limit) { num3++; continue; } ShipBuildModeManager.Instance.StoreObjectServerRpc(new NetworkObjectReference(item.NetworkObject), -1); num++; num2++; } } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append((num2 == 0) ? "Nothing was stored." : ("Stored " + Plural(num2) + ".")); if (num3 > 0) { stringBuilder.Append("\nStorage is full: " + Plural(num3) + " did not fit."); } if (list.Count > 0) { stringBuilder.Append("\nSkipped:"); foreach (string item2 in list) { stringBuilder.Append("\n * ").Append(item2); } } return stringBuilder.ToString(); } private static string RetrieveAll(bool toDefault) { List<StoredObject> stored = StorageData.GetStored(); if (stored.Count == 0) { return "Storage is empty."; } foreach (StoredObject item in stored) { if (toDefault) { DefaultPosition.Prepare(item.UnlockableId); } StartOfRound.Instance.ReturnUnlockableFromStorageServerRpc(item.UnlockableId); } return "Retrieved " + Plural(stored.Count) + " to their " + (toDefault ? "default" : "last") + " positions."; } private static string Plural(int count) { if (count != 1) { return count + " objects"; } return "1 object"; } } internal sealed class DeployedObject { public PlaceableShipObject Placeable; public NetworkObject NetworkObject; public ulong ObjectId; public int UnlockableId; public string Name; public string Label; public bool Storable; public string Problem; public Vector3 Position; } internal sealed class StoredObject { public int UnlockableId; public string Name; public string Label; } internal static class StorageData { private const float ScrapPlacedCooldown = 6f; internal static List<UnlockableItem> Unlockables { get { StartOfRound instance = StartOfRound.Instance; if (!((Object)(object)instance != (Object)null) || !((Object)(object)instance.unlockablesList != (Object)null)) { return null; } return instance.unlockablesList.unlockables; } } internal static List<DeployedObject> GetDeployed() { //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) List<DeployedObject> list = new List<DeployedObject>(); List<UnlockableItem> unlockables = Unlockables; if (unlockables == null) { return list; } HashSet<ulong> hashSet = new HashSet<ulong>(); PlaceableShipObject[] array = Object.FindObjectsOfType<PlaceableShipObject>(); foreach (PlaceableShipObject val in array) { if ((Object)(object)val == (Object)null || (Object)(object)val.parentObject == (Object)null) { continue; } int unlockableID = val.unlockableID; if (unlockableID < 0 || unlockableID >= unlockables.Count) { continue; } UnlockableItem val2 = unlockables[unlockableID]; if (val2.inStorage || val.parentObject.disableObject) { continue; } NetworkObject networkObject = ((NetworkBehaviour)val.parentObject).NetworkObject; if (!((Object)(object)networkObject == (Object)null) && networkObject.IsSpawned && hashSet.Add(networkObject.NetworkObjectId)) { DeployedObject deployedObject = new DeployedObject { Placeable = val, NetworkObject = networkObject, ObjectId = networkObject.NetworkObjectId, UnlockableId = unlockableID, Name = CleanName(val2.unlockableName), Storable = val2.canBeStored, Position = (((Object)(object)val.mainMesh != (Object)null) ? ((Component)val.mainMesh).transform.position : ((Component)val).transform.position) }; if (!val2.canBeStored) { deployedObject.Problem = "can't be stored"; } else if ((Object)(object)((Component)val.parentObject).GetComponentInChildren<GrabbableObject>() != (Object)null) { deployedObject.Problem = "has items on it"; } else if (Time.realtimeSinceStartup - val.lastTimeScrapWasPlaced <= 6f) { deployedObject.Problem = "items were just placed on it, try again in a moment"; } list.Add(deployedObject); } } list.Sort(delegate(DeployedObject a, DeployedObject b) { int num = string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase); return (num == 0) ? a.ObjectId.CompareTo(b.ObjectId) : num; }); NumberLabels(list, (DeployedObject d) => d.Name, delegate(DeployedObject d, string label) { d.Label = label; }); return list; } internal static List<StoredObject> GetStored() { List<StoredObject> list = new List<StoredObject>(); List<UnlockableItem> unlockables = Unlockables; if (unlockables == null) { return list; } for (int i = 0; i < unlockables.Count; i++) { UnlockableItem val = unlockables[i]; if (val.unlockableType != 0 && val.inStorage && (val.hasBeenUnlockedByPlayer || val.alreadyUnlocked)) { list.Add(new StoredObject { UnlockableId = i, Name = CleanName(val.unlockableName) }); } } list.Sort(delegate(StoredObject a, StoredObject b) { int num = string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase); return (num == 0) ? a.UnlockableId.CompareTo(b.UnlockableId) : num; }); NumberLabels(list, (StoredObject s) => s.Name, delegate(StoredObject s, string label) { s.Label = label; }); return list; } internal static int StoredCount() { return GetStored().Count; } private static void NumberLabels<T>(List<T> list, Func<T, string> name, Action<T, string> setLabel) { Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); foreach (T item in list) { string text = name(item); dictionary.TryGetValue(text, out var value); value = (dictionary[text] = value + 1); setLabel(item, text + " #" + value); } } internal static string CleanName(string name) { if (string.IsNullOrWhiteSpace(name)) { return "Unknown object"; } name = name.Trim(); return char.ToUpperInvariant(name[0]) + name.Substring(1); } internal static string DescribePosition(Vector3 position, Terminal terminal) { //IL_000f: 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) if ((Object)(object)terminal == (Object)null) { return ""; } return Vector3.Distance(position, ((Component)terminal).transform.position).ToString("0.0", CultureInfo.InvariantCulture) + "m from terminal"; } } internal static class StorageMenu { private enum State { None, Main, View, Store, StoreConfirm, Retrieve, RetrieveWhere, StoreAllConfirm, RetrieveAllConfirm } private const string Header = "SHIP STORAGE MANAGER"; private const string Line = "-----------------------------"; private static readonly string[] OpenWords = new string[3] { "storage", "storage manager", "ship storage" }; private static readonly string[] BackWords = new string[3] { "back", "menu", "return" }; private static readonly string[] ConfirmWords = new string[4] { "confirm", "c", "yes", "y" }; private static readonly string[] CancelWords = new string[4] { "cancel", "deny", "no", "n" }; private static State state = State.None; private static TerminalNode node; private static Terminal activeTerminal; private static List<DeployedObject> shownDeployed = new List<DeployedObject>(); private static List<StoredObject> shownStored = new List<StoredObject>(); private static DeployedObject selected; private static StoredObject selectedStored; private static bool waitingForHost; internal static void Reset() { state = State.None; selected = null; selectedStored = null; waitingForHost = false; shownDeployed.Clear(); shownStored.Clear(); } internal static bool TryHandle(Terminal terminal, string rawInput, out TerminalNode result) { result = null; string text = Normalize(rawInput); if (text.Length == 0) { return false; } activeTerminal = terminal; if (Is(text, OpenWords)) { result = ShowMain(null); return true; } if (state == State.None) { return false; } if (Is(text, BackWords)) { result = ((state == State.StoreConfirm) ? ShowStoreList("Cancelled.") : ((state == State.RetrieveWhere) ? ShowRetrieveList("Cancelled.") : ShowMain(null))); return true; } switch (state) { case State.Main: result = HandleMain(text); break; case State.View: result = (IsNumber(text) ? ShowView("Type BACK to return to the menu.") : null); break; case State.Store: result = HandleStore(text); break; case State.StoreConfirm: result = HandleStoreConfirm(text); break; case State.Retrieve: result = HandleRetrieve(text); break; case State.RetrieveWhere: result = HandleRetrieveWhere(text); break; case State.StoreAllConfirm: result = HandleAllConfirm(text, StorageAction.StoreAll); break; case State.RetrieveAllConfirm: result = HandleRetrieveAll(text); break; } if ((Object)(object)result == (Object)null) { Reset(); return false; } return true; } internal static void ShowResult(string text) { bool num = waitingForHost; waitingForHost = false; Terminal val = activeTerminal; if (num && state == State.Main && (Object)(object)val != (Object)null && val.terminalInUse) { val.LoadNewNode(ShowMain(text)); } else if ((Object)(object)HUDManager.Instance != (Object)null && !string.IsNullOrEmpty(text)) { HUDManager.Instance.DisplayTip("Ship storage", text, false, false, "LC_Tip1"); } } private static TerminalNode ShowMain(string message) { state = State.Main; selected = null; List<DeployedObject> deployed = StorageData.GetDeployed(); int stored = StorageData.StoredCount(); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("SHIP STORAGE MANAGER").Append("\n\n"); if (!string.IsNullOrEmpty(message)) { stringBuilder.Append(message).Append("\n").Append("-----------------------------") .Append("\n\n"); } stringBuilder.Append("1. View deployed objects\n"); stringBuilder.Append("2. Store an object\n"); stringBuilder.Append("3. Retrieve an object\n"); stringBuilder.Append("4. Store all objects\n"); stringBuilder.Append("5. Retrieve all objects\n\n"); stringBuilder.Append("Deployed: ").Append(deployed.Count).Append(" Stored: ") .Append(StorageUsage(stored)) .Append("\n"); if (!StorageNetwork.CanUse) { stringBuilder.Append("Only the host can store or retrieve objects.\n"); } stringBuilder.Append("\nPlease select an option:\n"); return Node(stringBuilder); } private static TerminalNode ShowView(string message) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) state = State.View; List<DeployedObject> deployed = StorageData.GetDeployed(); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("DEPLOYED OBJECTS\n\n"); if (deployed.Count == 0) { stringBuilder.Append("No objects are deployed on the ship.\n"); } for (int i = 0; i < deployed.Count; i++) { DeployedObject deployedObject = deployed[i]; stringBuilder.Append(i + 1).Append(". ").Append(deployedObject.Label); string text = StorageData.DescribePosition(deployedObject.Position, activeTerminal); if (text.Length > 0) { stringBuilder.Append(" - ").Append(text); } if (deployedObject.Problem != null) { stringBuilder.Append(" (").Append(deployedObject.Problem).Append(")"); } stringBuilder.Append("\n"); } stringBuilder.Append("\n"); if (!string.IsNullOrEmpty(message)) { stringBuilder.Append(message).Append("\n"); } else { stringBuilder.Append("Type BACK to return to the menu.\n"); } return Node(stringBuilder); } private static TerminalNode ShowStoreList(string message) { state = State.Store; selected = null; shownDeployed = StorageData.GetDeployed().FindAll((DeployedObject d) => d.Storable); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("SELECT OBJECT TO STORE\n\n"); if (!string.IsNullOrEmpty(message)) { stringBuilder.Append(message).Append("\n\n"); } if (shownDeployed.Count == 0) { stringBuilder.Append("There is nothing to store.\n\nType BACK to return to the menu.\n"); return Node(stringBuilder); } for (int num = 0; num < shownDeployed.Count; num++) { DeployedObject deployedObject = shownDeployed[num]; stringBuilder.Append(num + 1).Append(". ").Append(deployedObject.Label); if (deployedObject.Problem != null) { stringBuilder.Append(" (").Append(deployedObject.Problem).Append(")"); } stringBuilder.Append("\n"); } stringBuilder.Append("\nStorage: ").Append(StorageUsage(StorageData.StoredCount())).Append("\n"); stringBuilder.Append("\nType object number:\n"); return Node(stringBuilder); } private static TerminalNode ShowStoreConfirm(DeployedObject target) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) state = State.StoreConfirm; selected = target; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(target.Label).Append(" selected.\n"); string text = StorageData.DescribePosition(target.Position, activeTerminal); if (text.Length > 0) { stringBuilder.Append("Position: ").Append(text).Append("\n"); } stringBuilder.Append("\nDo you want to store this object?\n"); stringBuilder.Append("[CONFIRM] / [CANCEL]\n"); return Node(stringBuilder); } private static TerminalNode ShowRetrieveList(string message) { state = State.Retrieve; shownStored = StorageData.GetStored(); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("SHIP STORAGE\n\n"); if (!string.IsNullOrEmpty(message)) { stringBuilder.Append(message).Append("\n\n"); } stringBuilder.Append("Stored objects:\n\n"); if (shownStored.Count == 0) { stringBuilder.Append("Storage is empty.\n"); } for (int i = 0; i < shownStored.Count; i++) { stringBuilder.Append(i + 1).Append(". ").Append(shownStored[i].Label) .Append("\n"); } stringBuilder.Append("\n").Append(AvailableLine(shownStored.Count)).Append("\n"); stringBuilder.Append((shownStored.Count == 0) ? "\nType BACK to return to the menu.\n" : "\nType object number to retrieve:\n"); return Node(stringBuilder); } private static TerminalNode ShowAllConfirm(StorageAction action) { StringBuilder stringBuilder = new StringBuilder(); if (action == StorageAction.StoreAll) { state = State.StoreAllConfirm; int count = StorageData.GetDeployed().FindAll((DeployedObject d) => d.Storable && d.Problem == null).Count; stringBuilder.Append("STORE ALL OBJECTS\n\n"); stringBuilder.Append(count).Append((count == 1) ? " object" : " objects").Append(" can be stored.\n"); stringBuilder.Append(AvailableLine(StorageData.StoredCount())).Append("\n"); stringBuilder.Append("\nDo you want to store all objects?\n"); stringBuilder.Append("[CONFIRM] / [CANCEL]\n"); return Node(stringBuilder); } state = State.RetrieveAllConfirm; int num = StorageData.StoredCount(); stringBuilder.Append("RETRIEVE ALL OBJECTS\n\n"); stringBuilder.Append(num).Append((num == 1) ? " object is" : " objects are").Append(" in storage.\n"); stringBuilder.Append("\nWhere should they go?\n"); AppendWhereOptions(stringBuilder); return Node(stringBuilder); } private static TerminalNode ShowRetrieveWhere(StoredObject target) { state = State.RetrieveWhere; selectedStored = target; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(target.Label).Append(" selected.\n"); stringBuilder.Append("\nWhere should it go?\n"); AppendWhereOptions(stringBuilder); return Node(stringBuilder); } private static void AppendWhereOptions(StringBuilder text) { text.Append("1. Last position\n"); text.Append("2. Default position\n\n"); text.Append("Use DEFAULT if an object got stuck outside the ship.\n"); text.Append("\nType option number or CANCEL:\n"); } private static bool? ParseWhere(string input) { if (input != null) { switch (input.Length) { case 1: { char c = input[0]; if (c != '1') { if (c == '2') { goto IL_00ed; } if (c != 'c') { break; } } goto IL_00e6; } case 7: { char c = input[0]; if (c != 'c') { if (c != 'd' || !(input == "default")) { break; } goto IL_00ed; } if (!(input == "confirm")) { break; } goto IL_00e6; } case 4: if (!(input == "last")) { break; } goto IL_00e6; case 13: if (!(input == "last position")) { break; } goto IL_00e6; case 14: if (!(input == "last positions")) { break; } goto IL_00e6; case 16: if (!(input == "default position")) { break; } goto IL_00ed; case 17: { if (!(input == "default positions")) { break; } goto IL_00ed; } IL_00e6: return false; IL_00ed: return true; } } return null; } private static TerminalNode HandleMain(string input) { switch (input) { case "1": case "list": case "view": case "deployed": case "view deployed objects": return ShowView(null); case "2": case "store": case "store an object": return ShowStoreList(null); case "3": case "retrieve": case "retrieve an object": return ShowRetrieveList(null); case "4": case "store all": case "store all objects": return ShowAllConfirm(StorageAction.StoreAll); case "5": case "retrieve all": case "retrieve all objects": return ShowAllConfirm(StorageAction.RetrieveAll); default: if (!IsNumber(input)) { return null; } return ShowMain("Invalid option."); } } private static TerminalNode HandleStore(string input) { if (shownDeployed.Count == 0) { if (!IsNumber(input)) { return null; } return ShowStoreList(null); } string error; DeployedObject deployedObject = Pick(shownDeployed, input, (DeployedObject d) => d.Label, (DeployedObject d) => d.Name, out error); if (deployedObject == null) { if (error == null) { return null; } return ShowStoreList(error); } if (deployedObject.Problem != null) { return ShowStoreList("Can't store " + deployedObject.Label + ": " + deployedObject.Problem + "."); } return ShowStoreConfirm(deployedObject); } private static TerminalNode HandleStoreConfirm(string input) { if (Is(input, CancelWords)) { return ShowStoreList("Cancelled."); } if (!Is(input, ConfirmWords)) { if (!IsNumber(input)) { return null; } return ShowStoreConfirm(selected); } DeployedObject deployedObject = selected; if (!StorageNetwork.CanUse) { return ShowMain("Only the host can store or retrieve objects."); } int limit = StorageNetwork.Limit; int num = StorageData.StoredCount(); if (limit > 0 && num >= limit) { return ShowMain($"Storage is full ({num}/{limit}). Retrieve something first."); } return RunAction(StorageAction.Store, deployedObject.ObjectId, -1); } private static TerminalNode HandleRetrieve(string input) { if (shownStored.Count == 0) { if (!IsNumber(input)) { return null; } return ShowRetrieveList(null); } string error; StoredObject storedObject = Pick(shownStored, input, (StoredObject s) => s.Label, (StoredObject s) => s.Name, out error); if (storedObject == null) { if (error == null) { return null; } return ShowRetrieveList(error); } return ShowRetrieveWhere(storedObject); } private static TerminalNode HandleRetrieveWhere(string input) { if (Is(input, CancelWords)) { return ShowRetrieveList("Cancelled."); } bool? flag = ParseWhere(input); if (!flag.HasValue) { if (!IsNumber(input)) { return null; } return ShowRetrieveWhere(selectedStored); } if (!StorageNetwork.CanUse) { return ShowMain("Only the host can store or retrieve objects."); } return RunAction(StorageAction.Retrieve, 0uL, selectedStored.UnlockableId, flag.Value); } private static TerminalNode HandleRetrieveAll(string input) { if (Is(input, CancelWords)) { return ShowMain("Cancelled."); } bool? flag = ParseWhere(input); if (!flag.HasValue) { if (!IsNumber(input)) { return null; } return ShowAllConfirm(StorageAction.RetrieveAll); } if (!StorageNetwork.CanUse) { return ShowMain("Only the host can store or retrieve objects."); } return RunAction(StorageAction.RetrieveAll, 0uL, -1, flag.Value); } private static TerminalNode HandleAllConfirm(string input, StorageAction action) { if (Is(input, CancelWords)) { return ShowMain("Cancelled."); } if (!Is(input, ConfirmWords)) { if (!IsNumber(input)) { return null; } return ShowAllConfirm(action); } if (!StorageNetwork.CanUse) { return ShowMain("Only the host can store or retrieve objects."); } return RunAction(action, 0uL, -1); } private static TerminalNode RunAction(StorageAction action, ulong objectId, int unlockableId, bool toDefault = false) { string text = StorageNetwork.Request(action, objectId, unlockableId, toDefault); if (text != null) { waitingForHost = false; return ShowMain(text); } TerminalNode result = ShowMain("Sending request to the ship..."); waitingForHost = true; return result; } private static T Pick<T>(List<T> list, string input, Func<T, string> label, Func<T, string> name, out string error) where T : class { error = null; if (int.TryParse(input, out var result)) { if (result >= 1 && result <= list.Count) { return list[result - 1]; } error = "Invalid number."; return null; } string wanted = Regex.Replace(input.Replace("#", " "), "\\s+", " ").Trim(); foreach (T item in list) { if (Normalize(label(item).Replace("#", " ")) == wanted) { return item; } } List<T> list2 = list.FindAll((T e) => Normalize(name(e)) == wanted); if (list2.Count == 0) { list2 = list.FindAll((T e) => Normalize(name(e)).StartsWith(wanted, StringComparison.Ordinal)); } if (list2.Count == 1) { return list2[0]; } if (list2.Count > 1) { error = "Several objects match. Type the number instead."; } return null; } private static string StorageUsage(int stored) { int limit = StorageNetwork.Limit; if (limit <= 0) { return stored.ToString(); } return $"{stored}/{limit}"; } private static string AvailableLine(int stored) { int limit = StorageNetwork.Limit; if (limit <= 0) { return "Available storage: unlimited"; } return $"Available storage: {Mathf.Max(0, limit - stored)}/{limit}"; } private static string Normalize(string text) { if (text == null) { return ""; } return Regex.Replace(text.Trim().ToLowerInvariant(), "\\s+", " ").TrimEnd('.', '!', '?'); } private static bool Is(string input, string[] words) { return Array.IndexOf(words, input) >= 0; } private static bool IsNumber(string input) { int result; return int.TryParse(input, out result); } private static TerminalNode Node(StringBuilder text) { if ((Object)(object)node == (Object)null) { node = ScriptableObject.CreateInstance<TerminalNode>(); ((Object)node).name = "UFOStorageManagerNode"; ((Object)node).hideFlags = (HideFlags)32; node.clearPreviousText = true; node.maxCharactersToType = 35; } node.displayText = text.Append("\n").ToString(); return node; } } internal static class StorageNetwork { [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__OnHello; public static HandleNamedMessageDelegate <1>__OnSettings; public static HandleNamedMessageDelegate <2>__OnRequest; public static HandleNamedMessageDelegate <3>__OnResult; public static HandleNamedMessageDelegate <4>__OnResetDefault; } private const string HelloMessage = "UFO.StorageManager.Hello"; private const string SettingsMessage = "UFO.StorageManager.Settings"; private const string RequestMessage = "UFO.StorageManager.Request"; private const string ResultMessage = "UFO.StorageManager.Result"; private const string ResetDefaultMessage = "UFO.StorageManager.ResetDefault"; internal static bool HostHasMod; private static int hostLimit; private static bool hostOnly; private static bool hostLimitBuildMode; private static bool settingsHooked; internal static bool IsHost { get { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton != (Object)null) { return singleton.IsServer; } return false; } } internal static int Limit { get { if (IsHost) { return Mathf.Max(0, Plugin.StorageLimit.Value); } if (!HostHasMod) { return 0; } return hostLimit; } } internal static bool LimitBuildMode { get { if (!IsHost) { if (HostHasMod) { return hostLimitBuildMode; } return false; } return Plugin.LimitBuildMode.Value; } } internal static bool CanUse { get { if (!IsHost && HostHasMod) { return !hostOnly; } return true; } } internal static void Register() { //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) //IL_003e: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Expected O, but got Unknown //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown NetworkManager singleton = NetworkManager.Singleton; CustomMessagingManager val = (((Object)(object)singleton != (Object)null) ? singleton.CustomMessagingManager : null); if (val == null) { return; } object obj = <>O.<0>__OnHello; if (obj == null) { HandleNamedMessageDelegate val2 = OnHello; <>O.<0>__OnHello = val2; obj = (object)val2; } val.RegisterNamedMessageHandler("UFO.StorageManager.Hello", (HandleNamedMessageDelegate)obj); object obj2 = <>O.<1>__OnSettings; if (obj2 == null) { HandleNamedMessageDelegate val3 = OnSettings; <>O.<1>__OnSettings = val3; obj2 = (object)val3; } val.RegisterNamedMessageHandler("UFO.StorageManager.Settings", (HandleNamedMessageDelegate)obj2); object obj3 = <>O.<2>__OnRequest; if (obj3 == null) { HandleNamedMessageDelegate val4 = OnRequest; <>O.<2>__OnRequest = val4; obj3 = (object)val4; } val.RegisterNamedMessageHandler("UFO.StorageManager.Request", (HandleNamedMessageDelegate)obj3); object obj4 = <>O.<3>__OnResult; if (obj4 == null) { HandleNamedMessageDelegate val5 = OnResult; <>O.<3>__OnResult = val5; obj4 = (object)val5; } val.RegisterNamedMessageHandler("UFO.StorageManager.Result", (HandleNamedMessageDelegate)obj4); object obj5 = <>O.<4>__OnResetDefault; if (obj5 == null) { HandleNamedMessageDelegate val6 = OnResetDefault; <>O.<4>__OnResetDefault = val6; obj5 = (object)val6; } val.RegisterNamedMessageHandler("UFO.StorageManager.ResetDefault", (HandleNamedMessageDelegate)obj5); HostHasMod = singleton.IsServer; if (!settingsHooked) { settingsHooked = true; Plugin.StorageLimit.SettingChanged += delegate { BroadcastSettings(); }; Plugin.HostOnly.SettingChanged += delegate { BroadcastSettings(); }; Plugin.LimitBuildMode.SettingChanged += delegate { BroadcastSettings(); }; } } internal unsafe static void SendHello() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || singleton.CustomMessagingManager == null || singleton.IsServer) { return; } HostHasMod = false; FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(8, (Allocator)2, -1); try { singleton.CustomMessagingManager.SendNamedMessage("UFO.StorageManager.Hello", 0uL, val, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } internal unsafe static string Request(StorageAction action, ulong objectId, int unlockableId, bool toDefault) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009e: 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) NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsListening) { return "Not connected."; } if (singleton.IsServer) { return StorageActions.Execute(action, objectId, unlockableId, Limit, toDefault); } if (!HostHasMod) { if (toDefault) { return "The host needs UFO Storage Manager to retrieve to the default position."; } return StorageActions.Execute(action, objectId, unlockableId, 0, toDefault: false); } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(32, (Allocator)2, -1); try { byte b = (byte)action; ((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref objectId, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref unlockableId, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref toDefault, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessage("UFO.StorageManager.Request", 0uL, val, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } return null; } private static void BroadcastSettings() { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsServer || !singleton.IsListening || singleton.CustomMessagingManager == null) { return; } foreach (ulong connectedClientsId in singleton.ConnectedClientsIds) { if (connectedClientsId != singleton.LocalClientId) { SendSettings(connectedClientsId); } } } private unsafe static void SendSettings(ulong clientId) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(16, (Allocator)2, -1); try { int num = Mathf.Max(0, Plugin.StorageLimit.Value); ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num, default(ForPrimitives)); bool value = Plugin.HostOnly.Value; ((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref value, default(ForPrimitives)); value = Plugin.LimitBuildMode.Value; ((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref value, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessage("UFO.StorageManager.Settings", clientId, val, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private unsafe static void SendResult(ulong clientId, string text) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(256, (Allocator)2, 8192); try { ((FastBufferWriter)(ref val)).WriteValueSafe(text ?? "", false); singleton.CustomMessagingManager.SendNamedMessage("UFO.StorageManager.Result", clientId, val, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private static void OnHello(ulong senderClientId, FastBufferReader reader) { if (IsHost) { SendSettings(senderClientId); } } private static void OnSettings(ulong senderClientId, FastBufferReader reader) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) if (senderClientId == 0L) { HostHasMod = true; ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref hostLimit, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref hostOnly, default(ForPrimitives)); ((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref hostLimitBuildMode, default(ForPrimitives)); } } private static void OnRequest(ulong senderClientId, FastBufferReader reader) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: 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_004d: Unknown result type (might be due to invalid IL or missing references) if (IsHost) { byte action = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref action, default(ForPrimitives)); ulong objectId = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref objectId, default(ForPrimitives)); int unlockableId = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref unlockableId, default(ForPrimitives)); bool toDefault = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref toDefault, default(ForPrimitives)); string text = ((!Plugin.HostOnly.Value || senderClientId == 0L) ? StorageActions.Execute((StorageAction)action, objectId, unlockableId, Mathf.Max(0, Plugin.StorageLimit.Value), toDefault) : "Only the host can store or retrieve objects."); SendResult(senderClientId, text); } } internal unsafe static void BroadcastResetDefault(int unlockableId) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsServer || !singleton.IsListening || singleton.CustomMessagingManager == null) { return; } FastBufferWriter val = default(FastBufferWriter); foreach (ulong connectedClientsId in singleton.ConnectedClientsIds) { if (connectedClientsId != singleton.LocalClientId) { ((FastBufferWriter)(ref val))..ctor(8, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref unlockableId, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessage("UFO.StorageManager.ResetDefault", connectedClientsId, val, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } } } private static void OnResetDefault(ulong senderClientId, FastBufferReader reader) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (senderClientId == 0L && !IsHost) { int unlockableId = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref unlockableId, default(ForPrimitives)); DefaultPosition.ResetLocal(unlockableId); } } private static void OnResult(ulong senderClientId, FastBufferReader reader) { if (senderClientId == 0L) { string text = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false); StorageMenu.ShowResult(text); } } } }