using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Microsoft.CodeAnalysis; using TMPro; using UnifiedStorage.Core; using UnifiedStorage.Mod.Config; using UnifiedStorage.Mod.Diagnostics; using UnifiedStorage.Mod.Domain; using UnifiedStorage.Mod.Models; using UnifiedStorage.Mod.Network; using UnifiedStorage.Mod.Patches; using UnifiedStorage.Mod.Pieces; using UnifiedStorage.Mod.Server; using UnifiedStorage.Mod.Session; using UnifiedStorage.Mod.Shared; using UnifiedStorage.Mod.UI; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("UnifiedStorage")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+5d7be5f6efeb56f9a845ddaab6e91dfa01032b38")] [assembly: AssemblyProduct("UnifiedStorage")] [assembly: AssemblyTitle("UnifiedStorage")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.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 UnifiedStorage.Mod { [BepInPlugin("andre.valheim.unifiedstorage", "Unified Storage", "1.1.5")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class UnifiedStoragePlugin : BaseUnityPlugin { public const string PluginGuid = "andre.valheim.unifiedstorage"; public const string PluginName = "Unified Storage"; public const string PluginVersion = "1.1.5"; private const float WorldChangeRefreshMinInterval = 0.2f; private static bool _blockGameInput; private StorageConfig? _config; private StorageTrace? _trace; private Harmony? _harmony; private IContainerScanner? _scanner; private TerminalSessionService? _session; private TerminalAuthorityService? _authority; private TerminalRpcRoutes? _rpcRoutes; private UnifiedTerminalRegistrar? _pieceRegistrar; private TerminalUIManager? _ui; private bool _trackedInventoryDirty; private bool _isApplyingRefresh; private bool _wasDragging; private float _nextAllowedWorldRefreshAt; private string _lastSearch = string.Empty; private int _lastUiRevision = -1; private float _storagFullMessageCooldown; internal static UnifiedStoragePlugin? Instance { get; private set; } private void Awake() { //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Expected O, but got Unknown Instance = this; _config = new StorageConfig(((BaseUnityPlugin)this).Config); _trace = new StorageTrace(((BaseUnityPlugin)this).Logger, _config); _scanner = new ContainerScanner(_config); _authority = new TerminalAuthorityService(_scanner, ((BaseUnityPlugin)this).Logger); _rpcRoutes = new TerminalRpcRoutes(_authority, ((BaseUnityPlugin)this).Logger); _rpcRoutes.EnsureRegistered(); _session = new TerminalSessionService(_config, _scanner, _rpcRoutes, ((BaseUnityPlugin)this).Logger, _trace); _pieceRegistrar = new UnifiedTerminalRegistrar(_config, ((BaseUnityPlugin)this).Logger); _ui = new TerminalUIManager(); PrefabManager.OnVanillaPrefabsAvailable += OnVanillaPrefabsAvailable; _harmony = new Harmony("andre.valheim.unifiedstorage.patches"); _harmony.PatchAll(typeof(ContainerInteractPatch)); _harmony.PatchAll(typeof(InventoryAddItemPatch)); _harmony.PatchAll(typeof(InventoryAddItemAtPositionPatch)); _harmony.PatchAll(typeof(InventoryMoveItemToThisPatch)); _harmony.PatchAll(typeof(InventoryGuiHidePatch)); _harmony.PatchAll(typeof(InventoryGuiAwakePatch)); _harmony.PatchAll(typeof(InventoryGuiUpdatePatch)); _harmony.PatchAll(typeof(InventoryGridOnLeftClickPatch)); _harmony.PatchAll(typeof(InventoryGridOnRightClickPatch)); _harmony.PatchAll(typeof(InventoryGuiOnTakeAllPatch)); _harmony.PatchAll(typeof(InventoryGuiOnStackAllPatch)); _harmony.PatchAll(typeof(InventoryGuiOnDropOutsidePatch)); _harmony.PatchAll(typeof(InventoryChangedPatch)); _harmony.PatchAll(typeof(ZInputGetButtonPatch)); _harmony.PatchAll(typeof(ZInputGetButtonDownPatch)); _harmony.PatchAll(typeof(ZInputGetButtonUpPatch)); _harmony.PatchAll(typeof(ZInputGetKeyPatch)); _harmony.PatchAll(typeof(ZInputGetKeyDownPatch)); _harmony.PatchAll(typeof(ZInputGetKeyUpPatch)); _harmony.PatchAll(typeof(ChatHasFocusPatch)); _harmony.PatchAll(typeof(ContainerHoverTextPatch)); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Unified Storage v1.1.5 loaded."); } private void Update() { _rpcRoutes?.EnsureRegistered(); _authority?.Tick(); _session?.Tick(); TryProcessWorldChangeRefresh(); } private void OnDestroy() { PrefabManager.OnVanillaPrefabsAvailable -= OnVanillaPrefabsAvailable; Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _session?.EndSession(); _blockGameInput = false; Instance = null; } private void OnVanillaPrefabsAvailable() { _pieceRegistrar?.RegisterPiece(); } internal void TryHandleChestInteract(Container container, Humanoid character) { if (_session != null) { Player val = (Player)(object)((character is Player) ? character : null); if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && _session.HandleContainerInteract(container, val)) { _lastSearch = string.Empty; _ui?.ClearSearch(); } } } internal void OnInventoryGuiClosing() { _blockGameInput = false; _lastSearch = string.Empty; _trackedInventoryDirty = false; _isApplyingRefresh = false; _wasDragging = false; _lastUiRevision = -1; _session?.EndSession(); _ui?.Reset(); } internal void OnInventoryGuiAwake(InventoryGui gui) { _ui?.EnsureNativeUi(gui); } internal void OnInventoryGuiUpdate(InventoryGui gui) { if (_ui == null || _session == null) { return; } _ui.EnsureNativeUi(gui); _ui.UpdateChestInclusionToggle(gui); if (!_session.IsActive || !InventoryGui.IsVisible() || !gui.IsContainerOpen()) { _blockGameInput = false; _wasDragging = false; _ui.SetTakeAllButtonEnabled(gui, enabled: true); _ui.SetNativeUiVisible(visible: false); _ui.RestoreLayout(); _lastUiRevision = -1; return; } _ui.SetTakeAllButtonEnabled(gui, enabled: false); _ui.SetNativeUiVisible(visible: true); _ui.UpdateContainerName(gui); bool flag = ReflectionHelpers.IsDragInProgress(); if (flag && !_wasDragging) { OnContainerInteraction(); } _wasDragging = flag; _ui.ApplyExpandedLayout(gui, _session.ContentRows); if (_session.UiRevision != _lastUiRevision) { _lastUiRevision = _session.UiRevision; _ui.UpdateMetaText(_session.SlotsUsedDisplay, _session.SlotsTotalPhysical, _session.ChestsInRange); _ui.RefreshContainerGrid(gui); } _ui.UpdateSearchBinding(gui); UpdateSearchFocusState(); ProcessSearchChanges(gui); } internal static bool ShouldBlockGameInput() { return _blockGameInput; } internal bool IsUnifiedSessionActive() { if (_session != null) { return _session.IsActive; } return false; } internal bool ShouldBlockDeposit(Inventory targetInventory, ItemData? item = null) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) if (_session == null || !_session.IsActive || _session.IsApplyingProjection) { return false; } if (!_session.IsTerminalInventory(targetInventory)) { return false; } if ((Object)(object)item?.m_dropPrefab == (Object)null) { return _session.IsStorageFull; } ItemKey key = default(ItemKey); ((ItemKey)(ref key))..ctor(((Object)item.m_dropPrefab).name, item.m_quality, item.m_variant); if (_session.HasAnyCapacityFor(key)) { return false; } NotifyStorageFull(); return true; } private void NotifyStorageFull() { float unscaledTime = Time.unscaledTime; if (!(unscaledTime < _storagFullMessageCooldown)) { _storagFullMessageCooldown = unscaledTime + 2.5f; Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, "Storage is full.", 0, (Sprite)null); } } } internal int GetNearbyChestCount(Vector3 position) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (_scanner == null || _config == null) { return 0; } return _scanner.GetNearbyContainers(position, _config.ScanRadius.Value).Count; } internal void OnTrackedInventoryChanged(Inventory inventory) { if (!_isApplyingRefresh && _session != null && _session.IsActive && inventory != null && !_session.IsApplyingProjection && _session.IsTrackedInventory(inventory)) { _trackedInventoryDirty = true; } } internal void OnContainerInteraction() { if (_session != null && _session.IsActive && !((Object)(object)InventoryGui.instance == (Object)null)) { _trackedInventoryDirty = false; ExecuteSessionRefresh(delegate { _session.NotifyContainerInteraction(); }); _ui?.UpdateMetaText(_session.SlotsUsedDisplay, _session.SlotsTotalPhysical, _session.ChestsInRange); _ui?.RefreshContainerGrid(InventoryGui.instance); } } internal void OnInventoryGridInteraction(InventoryGrid grid) { if (_session != null && _session.IsActive && !((Object)(object)InventoryGui.instance == (Object)null) && !((Object)(object)grid == (Object)null)) { OnContainerInteraction(); } } private void ProcessSearchChanges(InventoryGui gui) { if (_ui != null && _session != null) { string searchText = _ui.SearchText; if (!string.Equals(searchText, _lastSearch, StringComparison.Ordinal)) { _lastSearch = searchText; _session.SetSearchQuery(_lastSearch); _ui.RefreshContainerGrid(gui); } } } private void UpdateSearchFocusState() { _blockGameInput = _ui != null && _ui.IsSearchFocused; } private void TryProcessWorldChangeRefresh() { if (_trackedInventoryDirty && _session != null && _session.IsActive && !((Object)(object)InventoryGui.instance == (Object)null) && InventoryGui.IsVisible() && InventoryGui.instance.IsContainerOpen() && !(Time.unscaledTime < _nextAllowedWorldRefreshAt) && !ReflectionHelpers.IsDragInProgress()) { _nextAllowedWorldRefreshAt = Time.unscaledTime + 0.2f; _trackedInventoryDirty = false; ExecuteSessionRefresh(delegate { _session.RefreshFromWorldChange(); }); _ui?.UpdateMetaText(_session.SlotsUsedDisplay, _session.SlotsTotalPhysical, _session.ChestsInRange); _ui?.RefreshContainerGrid(InventoryGui.instance); } } private void ExecuteSessionRefresh(Action refreshAction) { if (_isApplyingRefresh) { return; } _isApplyingRefresh = true; try { refreshAction(); } finally { _isApplyingRefresh = false; } } } } namespace UnifiedStorage.Mod.UI { public sealed class TerminalUIManager { private const int VisibleGridRows = 7; private const float FooterPanelBottomOffset = -70f; private const float FooterPanelHeight = 52f; private const float GridTopReserve = 124f; private const float ChestToggleTopOffset = -8f; private RectTransform? _nativeUiRoot; private TMP_Text? _metaText; private Image? _slotBarFill; private TMP_InputField? _searchInputField; private RectTransform? _chestToggleRoot; private Toggle? _chestIncludeToggle; private RectTransform? _containerRect; private Container? _boundChestForToggle; private Vector2 _originalContainerSize; private int _originalGridHeight; private Vector2 _originalGridOffsetMin; private Vector2 _originalGridOffsetMax; private bool _layoutCaptured; private bool _nativeBuilt; private bool _isApplyingChestToggle; private int _lastAppliedVisibleRows; public bool IsSearchFocused { get { if ((Object)(object)_searchInputField != (Object)null) { return _searchInputField.isFocused; } return false; } } public string SearchText { get { TMP_InputField? searchInputField = _searchInputField; return ((searchInputField != null) ? searchInputField.text : null) ?? string.Empty; } } public bool IsLayoutCaptured => _layoutCaptured; public event Action? SearchValueChanged; public event Action? ChestToggleChanged; public void EnsureNativeUi(InventoryGui gui) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) if (!_nativeBuilt) { RectTransform containerRect = ReflectionHelpers.GetContainerRect(gui); if (!((Object)(object)containerRect == (Object)null)) { _containerRect = containerRect; TMP_Text containerName = ReflectionHelpers.GetContainerName(gui); GameObject val = new GameObject("US_NativePanel", new Type[1] { typeof(RectTransform) }); val.transform.SetParent((Transform)(object)containerRect, false); _nativeUiRoot = val.GetComponent(); _nativeUiRoot.anchorMin = new Vector2(0f, 0f); _nativeUiRoot.anchorMax = new Vector2(1f, 0f); _nativeUiRoot.pivot = new Vector2(0.5f, 0f); _nativeUiRoot.anchoredPosition = new Vector2(0f, -70f); _nativeUiRoot.sizeDelta = new Vector2(0f, 52f); BuildSlotBar(val.transform, containerName); BuildSearchField(val.transform, containerName); EnsureChestToggleUi(containerRect, containerName); _nativeBuilt = true; SetNativeUiVisible(visible: false); SetChestToggleVisible(visible: false); } } } private void BuildSlotBar(Transform parent, TMP_Text? containerName) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_004f: 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_0079: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Expected O, but got Unknown //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Expected O, but got Unknown //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Unknown result type (might be due to invalid IL or missing references) float num = 20f; GameObject val = new GameObject("US_SlotBarBg", new Type[2] { typeof(RectTransform), typeof(Image) }); val.transform.SetParent(parent, false); RectTransform component = val.GetComponent(); component.anchorMin = new Vector2(0f, 1f); component.anchorMax = new Vector2(1f, 1f); component.pivot = new Vector2(0.5f, 1f); component.anchoredPosition = new Vector2(0f, 0f); component.sizeDelta = new Vector2(-16f, num); Image component2 = val.GetComponent(); ((Graphic)component2).color = new Color(0f, 0f, 0f, 0.55f); ((Graphic)component2).raycastTarget = false; GameObject val2 = new GameObject("US_SlotBarFill", new Type[2] { typeof(RectTransform), typeof(Image) }); val2.transform.SetParent(val.transform, false); RectTransform component3 = val2.GetComponent(); component3.anchorMin = new Vector2(0f, 0f); component3.anchorMax = new Vector2(0f, 1f); component3.pivot = new Vector2(0f, 0.5f); component3.offsetMin = new Vector2(2f, 2f); component3.offsetMax = new Vector2(2f, -2f); _slotBarFill = val2.GetComponent(); ((Graphic)_slotBarFill).color = new Color(0.85f, 0.75f, 0.2f, 0.85f); ((Graphic)_slotBarFill).raycastTarget = false; GameObject val3 = new GameObject("US_SlotLabel", new Type[2] { typeof(RectTransform), typeof(TextMeshProUGUI) }); val3.transform.SetParent(val.transform, false); RectTransform component4 = val3.GetComponent(); component4.anchorMin = Vector2.zero; component4.anchorMax = Vector2.one; component4.offsetMin = new Vector2(6f, 0f); component4.offsetMax = new Vector2(-6f, 0f); _metaText = (TMP_Text?)(object)val3.GetComponent(); if ((Object)(object)containerName != (Object)null) { _metaText.font = containerName.font; _metaText.fontSharedMaterial = containerName.fontSharedMaterial; } _metaText.fontSize = 14f; ((Graphic)_metaText).color = new Color(1f, 1f, 1f, 0.95f); _metaText.alignment = (TextAlignmentOptions)514; ((Graphic)_metaText).raycastTarget = false; } private void BuildSearchField(Transform parent, TMP_Text? containerName) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Expected O, but got Unknown //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_02e1: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_02fe: Unknown result type (might be due to invalid IL or missing references) //IL_0308: Unknown result type (might be due to invalid IL or missing references) //IL_036a: Unknown result type (might be due to invalid IL or missing references) float num = 22f; float num2 = -24f; GameObject val = new GameObject("US_SearchRow", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(parent, false); RectTransform component = val.GetComponent(); component.anchorMin = new Vector2(0f, 1f); component.anchorMax = new Vector2(1f, 1f); component.pivot = new Vector2(0.5f, 1f); component.anchoredPosition = new Vector2(0f, num2); component.sizeDelta = new Vector2(-16f, num); GameObject val2 = new GameObject("US_SearchInput", new Type[3] { typeof(RectTransform), typeof(Image), typeof(TMP_InputField) }); val2.transform.SetParent(val.transform, false); RectTransform component2 = val2.GetComponent(); component2.anchorMin = Vector2.zero; component2.anchorMax = Vector2.one; component2.offsetMin = Vector2.zero; component2.offsetMax = Vector2.zero; ((Graphic)val2.GetComponent()).color = new Color(0f, 0f, 0f, 0.5f); _searchInputField = val2.GetComponent(); GameObject val3 = new GameObject("Viewport", new Type[2] { typeof(RectTransform), typeof(RectMask2D) }); val3.transform.SetParent(val2.transform, false); RectTransform component3 = val3.GetComponent(); component3.anchorMin = Vector2.zero; component3.anchorMax = Vector2.one; component3.offsetMin = new Vector2(8f, 2f); component3.offsetMax = new Vector2(-8f, -2f); GameObject val4 = new GameObject("Text", new Type[2] { typeof(RectTransform), typeof(TextMeshProUGUI) }); val4.transform.SetParent(val3.transform, false); RectTransform component4 = val4.GetComponent(); component4.anchorMin = Vector2.zero; component4.anchorMax = Vector2.one; component4.offsetMin = Vector2.zero; component4.offsetMax = Vector2.zero; TextMeshProUGUI component5 = val4.GetComponent(); if ((Object)(object)containerName != (Object)null) { ((TMP_Text)component5).font = containerName.font; ((TMP_Text)component5).fontSharedMaterial = containerName.fontSharedMaterial; } ((TMP_Text)component5).fontSize = 14f; ((Graphic)component5).color = new Color(1f, 1f, 1f, 0.9f); ((TMP_Text)component5).alignment = (TextAlignmentOptions)4097; GameObject val5 = new GameObject("Placeholder", new Type[2] { typeof(RectTransform), typeof(TextMeshProUGUI) }); val5.transform.SetParent(val3.transform, false); RectTransform component6 = val5.GetComponent(); component6.anchorMin = Vector2.zero; component6.anchorMax = Vector2.one; component6.offsetMin = Vector2.zero; component6.offsetMax = Vector2.zero; TextMeshProUGUI component7 = val5.GetComponent(); ((TMP_Text)component7).text = "Search items..."; if ((Object)(object)containerName != (Object)null) { ((TMP_Text)component7).font = containerName.font; ((TMP_Text)component7).fontSharedMaterial = containerName.fontSharedMaterial; } ((TMP_Text)component7).fontSize = 13f; ((Graphic)component7).color = new Color(1f, 1f, 1f, 0.35f); ((TMP_Text)component7).alignment = (TextAlignmentOptions)4097; ((TMP_Text)component7).fontStyle = (FontStyles)2; _searchInputField.textViewport = component3; _searchInputField.textComponent = (TMP_Text)(object)component5; _searchInputField.placeholder = (Graphic)(object)component7; _searchInputField.lineType = (LineType)0; ((UnityEvent)(object)_searchInputField.onValueChanged).AddListener((UnityAction)delegate(string value) { this.SearchValueChanged?.Invoke(value ?? string.Empty); }); } private void EnsureChestToggleUi(RectTransform container, TMP_Text? containerName) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Expected O, but got Unknown //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: 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) //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_0319: Unknown result type (might be due to invalid IL or missing references) //IL_032d: Unknown result type (might be due to invalid IL or missing references) //IL_039d: Unknown result type (might be due to invalid IL or missing references) //IL_0376: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_chestToggleRoot != (Object)null) || !((Object)(object)_chestIncludeToggle != (Object)null)) { GameObject val = new GameObject("US_ChestIncludeToggle", new Type[2] { typeof(RectTransform), typeof(Toggle) }); val.transform.SetParent((Transform)(object)container, false); _chestToggleRoot = val.GetComponent(); _chestToggleRoot.anchorMin = new Vector2(1f, 1f); _chestToggleRoot.anchorMax = new Vector2(1f, 1f); _chestToggleRoot.pivot = new Vector2(1f, 1f); _chestToggleRoot.anchoredPosition = new Vector2(-14f, -8f); _chestToggleRoot.sizeDelta = new Vector2(190f, 20f); Toggle component = val.GetComponent(); ((Selectable)component).transition = (Transition)0; component.isOn = true; _chestIncludeToggle = component; GameObject val2 = new GameObject("Background", new Type[2] { typeof(RectTransform), typeof(Image) }); val2.transform.SetParent(val.transform, false); RectTransform component2 = val2.GetComponent(); component2.anchorMin = new Vector2(0f, 0.5f); component2.anchorMax = new Vector2(0f, 0.5f); component2.pivot = new Vector2(0f, 0.5f); component2.anchoredPosition = Vector2.zero; component2.sizeDelta = new Vector2(16f, 16f); Image component3 = val2.GetComponent(); ((Graphic)component3).color = new Color(0f, 0f, 0f, 0.62f); GameObject val3 = new GameObject("Checkmark", new Type[2] { typeof(RectTransform), typeof(Image) }); val3.transform.SetParent(val2.transform, false); RectTransform component4 = val3.GetComponent(); component4.anchorMin = new Vector2(0.5f, 0.5f); component4.anchorMax = new Vector2(0.5f, 0.5f); component4.pivot = new Vector2(0.5f, 0.5f); component4.anchoredPosition = Vector2.zero; component4.sizeDelta = new Vector2(11f, 11f); Image component5 = val3.GetComponent(); ((Graphic)component5).color = new Color(0.82f, 0.93f, 0.66f, 1f); GameObject val4 = new GameObject("Label", new Type[2] { typeof(RectTransform), typeof(TextMeshProUGUI) }); val4.transform.SetParent(val.transform, false); RectTransform component6 = val4.GetComponent(); component6.anchorMin = new Vector2(0f, 0.5f); component6.anchorMax = new Vector2(1f, 0.5f); component6.pivot = new Vector2(0f, 0.5f); component6.anchoredPosition = new Vector2(22f, 0f); component6.sizeDelta = new Vector2(-22f, 18f); TextMeshProUGUI component7 = val4.GetComponent(); ((TMP_Text)component7).text = "Include In Unified"; if ((Object)(object)containerName != (Object)null) { ((TMP_Text)component7).font = containerName.font; ((TMP_Text)component7).fontSize = containerName.fontSize * 0.52f; ((Graphic)component7).color = ((Graphic)containerName).color; ((TMP_Text)component7).fontSharedMaterial = containerName.fontSharedMaterial; } else { ((TMP_Text)component7).fontSize = 14f; ((Graphic)component7).color = Color.white; } ((TMP_Text)component7).alignment = (TextAlignmentOptions)4097; ((Selectable)component).targetGraphic = (Graphic)(object)component3; component.graphic = (Graphic)(object)component5; ((UnityEvent)(object)component.onValueChanged).AddListener((UnityAction)OnChestIncludeToggleChanged); } } public void UpdateChestInclusionToggle(InventoryGui gui) { if ((Object)(object)_chestToggleRoot == (Object)null || (Object)(object)_chestIncludeToggle == (Object)null || !InventoryGui.IsVisible() || !gui.IsContainerOpen()) { _boundChestForToggle = null; SetChestToggleVisible(visible: false); return; } Container currentContainer = ReflectionHelpers.GetCurrentContainer(gui); if ((Object)(object)currentContainer == (Object)null || UnifiedTerminal.IsTerminal(currentContainer)) { _boundChestForToggle = null; SetChestToggleVisible(visible: false); return; } SetChestToggleVisible(visible: true); bool flag = ChestInclusionRules.IsIncludedInUnified(currentContainer); if (_boundChestForToggle != currentContainer || _chestIncludeToggle.isOn != flag) { _isApplyingChestToggle = true; _chestIncludeToggle.isOn = flag; _isApplyingChestToggle = false; _boundChestForToggle = currentContainer; } } private void OnChestIncludeToggleChanged(bool includeInUnified) { if (!_isApplyingChestToggle) { InventoryGui instance = InventoryGui.instance; Container val = (((Object)(object)instance != (Object)null) ? ReflectionHelpers.GetCurrentContainer(instance) : null); if (!((Object)(object)val == (Object)null) && !UnifiedTerminal.IsTerminal(val)) { ChestInclusionRules.TrySetIncludedInUnified(val, includeInUnified); this.ChestToggleChanged?.Invoke(includeInUnified); } } } public void UpdateContainerName(InventoryGui gui) { TMP_Text containerName = ReflectionHelpers.GetContainerName(gui); if ((Object)(object)containerName != (Object)null && !string.Equals(containerName.text, "Storage Interface", StringComparison.Ordinal)) { containerName.text = "Storage Interface"; } } public void UpdateMetaText(int slotsUsed, int slotsTotal, int chestCount) { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: 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_0122: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_metaText != (Object)null) { _metaText.text = $"{slotsUsed} / {slotsTotal}"; } if ((Object)(object)_slotBarFill != (Object)null) { float num = ((slotsTotal > 0) ? Mathf.Clamp01((float)slotsUsed / (float)slotsTotal) : 0f); Transform parent = ((Component)_slotBarFill).transform.parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); float num2; if (!((Object)(object)val != (Object)null)) { num2 = 100f; } else { Rect rect = val.rect; num2 = ((Rect)(ref rect)).width - 4f; } float num3 = num2; ((Component)_slotBarFill).GetComponent().sizeDelta = new Vector2(num3 * num, 0f); if (num >= 0.95f) { ((Graphic)_slotBarFill).color = new Color(0.85f, 0.25f, 0.2f, 0.85f); } else if (num >= 0.75f) { ((Graphic)_slotBarFill).color = new Color(0.9f, 0.6f, 0.15f, 0.85f); } else { ((Graphic)_slotBarFill).color = new Color(0.85f, 0.75f, 0.2f, 0.85f); } } } public void UpdateSearchBinding(InventoryGui gui) { if (!((Object)(object)_searchInputField == (Object)null) && _searchInputField.isFocused && (Input.GetKeyDown((KeyCode)13) || Input.GetKeyDown((KeyCode)271) || Input.GetKeyDown((KeyCode)27))) { _searchInputField.DeactivateInputField(false); } } public void ClearSearch() { if ((Object)(object)_searchInputField != (Object)null) { _searchInputField.text = string.Empty; } } public void ApplyExpandedLayout(InventoryGui gui, int contentRows) { //IL_002f: 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_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_containerRect == (Object)null) { return; } InventoryGrid containerGrid = ReflectionHelpers.GetContainerGrid(gui); if ((Object)(object)containerGrid == (Object)null) { return; } if (!_layoutCaptured) { _originalContainerSize = _containerRect.sizeDelta; _originalGridHeight = ReflectionHelpers.GetGridHeight(containerGrid); RectTransform gridRoot = ReflectionHelpers.GetGridRoot(containerGrid); if ((Object)(object)gridRoot != (Object)null) { _originalGridOffsetMin = gridRoot.offsetMin; _originalGridOffsetMax = gridRoot.offsetMax; } _layoutCaptured = true; _lastAppliedVisibleRows = -1; } int num = Math.Max(1, Math.Min(contentRows, 7)); if (num != _lastAppliedVisibleRows) { _lastAppliedVisibleRows = num; ReflectionHelpers.SetGridHeight(containerGrid, num); float gridElementSpace = ReflectionHelpers.GetGridElementSpace(containerGrid); GameObject gridElementPrefab = ReflectionHelpers.GetGridElementPrefab(containerGrid); float num2 = 64f; RectTransform val = default(RectTransform); if ((Object)(object)gridElementPrefab != (Object)null && gridElementPrefab.TryGetComponent(ref val)) { num2 = val.sizeDelta.y; } int num3 = Math.Max(0, num - _originalGridHeight); float num4 = (num2 + gridElementSpace) * (float)num3; _containerRect.sizeDelta = new Vector2(_originalContainerSize.x, _originalContainerSize.y + num4); RectTransform gridRoot2 = ReflectionHelpers.GetGridRoot(containerGrid); if ((Object)(object)gridRoot2 != (Object)null) { gridRoot2.offsetMax = new Vector2(_originalGridOffsetMax.x, -124f); gridRoot2.offsetMin = _originalGridOffsetMin; } } } public void RestoreLayout() { //IL_008a: 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_0054: Unknown result type (might be due to invalid IL or missing references) if (!_layoutCaptured || (Object)(object)InventoryGui.instance == (Object)null) { return; } InventoryGrid containerGrid = ReflectionHelpers.GetContainerGrid(InventoryGui.instance); if ((Object)(object)containerGrid != (Object)null) { ReflectionHelpers.SetGridHeight(containerGrid, _originalGridHeight); RectTransform gridRoot = ReflectionHelpers.GetGridRoot(containerGrid); if ((Object)(object)gridRoot != (Object)null) { gridRoot.offsetMin = _originalGridOffsetMin; gridRoot.offsetMax = _originalGridOffsetMax; } Inventory inventory = containerGrid.GetInventory(); if (inventory != null) { containerGrid.UpdateInventory(inventory, Player.m_localPlayer, (ItemData)null); } } if ((Object)(object)_containerRect != (Object)null) { _containerRect.sizeDelta = _originalContainerSize; } _layoutCaptured = false; _lastAppliedVisibleRows = -1; } public void RefreshContainerGrid(InventoryGui gui) { InventoryGrid containerGrid = ReflectionHelpers.GetContainerGrid(gui); if (!((Object)(object)containerGrid == (Object)null)) { Inventory inventory = containerGrid.GetInventory(); if (inventory != null) { containerGrid.UpdateInventory(inventory, Player.m_localPlayer, (ItemData)null); } } } public void SetNativeUiVisible(bool visible) { if ((Object)(object)_nativeUiRoot != (Object)null) { ((Component)_nativeUiRoot).gameObject.SetActive(visible); } } public void SetChestToggleVisible(bool visible) { if ((Object)(object)_chestToggleRoot != (Object)null) { ((Component)_chestToggleRoot).gameObject.SetActive(visible); } } public void SetTakeAllButtonEnabled(InventoryGui gui, bool enabled) { Button takeAllButton = ReflectionHelpers.GetTakeAllButton(gui); if ((Object)(object)takeAllButton != (Object)null) { ((Selectable)takeAllButton).interactable = enabled; } } public void Reset() { ClearSearch(); _boundChestForToggle = null; SetNativeUiVisible(visible: false); SetChestToggleVisible(visible: false); RestoreLayout(); } } } namespace UnifiedStorage.Mod.Shared { public sealed class DrawerSnapshot { public string SourceId { get; set; } = string.Empty; public ZNetView ZNetView { get; set; } public string Prefab { get; set; } = string.Empty; public int Amount { get; set; } public int Quality { get; set; } public Vector3 Position { get; set; } } public static class ItemDrawersApi { private static bool _initialized; private static bool _available; private static MethodInfo? _allDrawersMethod; public static bool IsAvailable { get { EnsureInitialized(); return _available; } } private static void EnsureInitialized() { if (!_initialized) { _initialized = true; Type type = Type.GetType("API.ClientSideV2, kg_ItemDrawers"); if (type == null) { _available = false; return; } _allDrawersMethod = type.GetMethod("AllDrawers", BindingFlags.Static | BindingFlags.Public); _available = _allDrawersMethod != null; } } public static List GetAllDrawers() { EnsureInitialized(); if (!_available || _allDrawersMethod == null) { return new List(); } if (!(_allDrawersMethod.Invoke(null, null) is IEnumerable enumerable)) { return new List(); } List list = new List(); foreach (ZNetView item in enumerable) { DrawerSnapshot drawerSnapshot = TryBuildSnapshot(item); if (drawerSnapshot != null) { list.Add(drawerSnapshot); } } return list; } public static List GetDrawersInRange(Vector3 center, float radius) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) List allDrawers = GetAllDrawers(); float num = radius * radius; List list = new List(); foreach (DrawerSnapshot item in allDrawers) { Vector3 val = item.Position - center; if (((Vector3)(ref val)).sqrMagnitude <= num) { list.Add(item); } } return list; } public static void AddToDrawer(DrawerSnapshot snapshot, string prefab, int amount, int quality) { if (!((Object)(object)snapshot.ZNetView == (Object)null)) { snapshot.ZNetView.InvokeRPC("AddItem_Request", new object[3] { prefab, amount, quality }); } } public static void ForceRemoveFromDrawer(DrawerSnapshot snapshot, int amount) { if (!((Object)(object)snapshot.ZNetView == (Object)null)) { snapshot.ZNetView.ClaimOwnership(); snapshot.ZNetView.InvokeRPC("ForceRemove", new object[1] { amount }); } } private static DrawerSnapshot? TryBuildSnapshot(ZNetView znv) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)znv == (Object)null) { return null; } ZDO zDO = znv.GetZDO(); if (zDO == null) { return null; } return new DrawerSnapshot { SourceId = ((object)(ZDOID)(ref zDO.m_uid)).ToString(), ZNetView = znv, Prefab = zDO.GetString("Prefab", ""), Amount = zDO.GetInt("Amount", 0), Quality = zDO.GetInt("Quality", 1), Position = ((Component)znv).transform.position }; } catch { return null; } } } public static class ReflectionHelpers { private static readonly FieldInfo? InventoryWidthField = typeof(Inventory).GetField("m_width", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo? InventoryHeightField = typeof(Inventory).GetField("m_height", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly MethodInfo? InventoryGetWidthMethod = typeof(Inventory).GetMethod("GetWidth", BindingFlags.Instance | BindingFlags.Public); private static readonly MethodInfo? InventoryGetHeightMethod = typeof(Inventory).GetMethod("GetHeight", BindingFlags.Instance | BindingFlags.Public); private static readonly MethodInfo? ContainerCheckAccessMethod = typeof(Container).GetMethod("CheckAccess", BindingFlags.Instance | BindingFlags.Public); private static readonly FieldInfo? ContainerNameField = typeof(Container).GetField("m_name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly MethodInfo? ContainerSetInUseMethod = typeof(Container).GetMethod("SetInUse", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(bool) }, null); private static readonly FieldInfo? ContainerInUseField = typeof(Container).GetField("m_inUse", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo? DragItemField = typeof(InventoryGui).GetField("m_dragItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo? ContainerRectField = AccessTools.Field(typeof(InventoryGui), "m_container"); private static readonly FieldInfo? CurrentContainerField = AccessTools.Field(typeof(InventoryGui), "m_currentContainer"); private static readonly FieldInfo? ContainerGridField = AccessTools.Field(typeof(InventoryGui), "m_containerGrid"); private static readonly FieldInfo? ContainerNameTextField = AccessTools.Field(typeof(InventoryGui), "m_containerName"); private static readonly FieldInfo? TakeAllButtonField = AccessTools.Field(typeof(InventoryGui), "m_takeAllButton"); private static readonly FieldInfo? GridRootField = AccessTools.Field(typeof(InventoryGrid), "m_gridRoot"); private static readonly FieldInfo? GridHeightField = AccessTools.Field(typeof(InventoryGrid), "m_height"); private static readonly FieldInfo? GridElementPrefabField = AccessTools.Field(typeof(InventoryGrid), "m_elementPrefab"); private static readonly FieldInfo? GridElementSpaceField = AccessTools.Field(typeof(InventoryGrid), "m_elementSpace"); private static readonly FieldInfo? InventoryItemListField = AccessTools.Field(typeof(Inventory), "m_inventory"); private static readonly MethodInfo? InventoryChangedMethod = typeof(Inventory).GetMethod("Changed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly MethodInfo?[] DropItemMethods = (from m in typeof(ItemDrop).GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where string.Equals(m.Name, "DropItem", StringComparison.Ordinal) select m).ToArray(); public static int GetInventoryWidth(Inventory inventory) { object obj; if (InventoryGetWidthMethod != null) { obj = InventoryGetWidthMethod.Invoke(inventory, null); if (obj is int) { return (int)obj; } } obj = InventoryWidthField?.GetValue(inventory); if (obj is int) { return (int)obj; } return 1; } public static int GetInventoryHeight(Inventory inventory) { object obj; if (InventoryGetHeightMethod != null) { obj = InventoryGetHeightMethod.Invoke(inventory, null); if (obj is int) { return (int)obj; } } obj = InventoryHeightField?.GetValue(inventory); if (obj is int) { return (int)obj; } return 1; } public static void SetInventorySize(Inventory inventory, int width, int height) { InventoryWidthField?.SetValue(inventory, width); InventoryHeightField?.SetValue(inventory, height); } public static bool CanAccess(Container container, Player player) { if (ContainerCheckAccessMethod == null) { return true; } ParameterInfo[] parameters = ContainerCheckAccessMethod.GetParameters(); if (parameters.Length == 1 && parameters[0].ParameterType == typeof(long)) { return (bool)ContainerCheckAccessMethod.Invoke(container, new object[1] { player.GetPlayerID() }); } if (parameters.Length == 0) { return (bool)ContainerCheckAccessMethod.Invoke(container, null); } return true; } public static void SetContainerDisplayName(Container container, string displayName) { if (ContainerNameField?.FieldType == typeof(string)) { ContainerNameField.SetValue(container, displayName); } } public static void ForceReleaseContainerUse(Container container) { try { ContainerSetInUseMethod?.Invoke(container, new object[1] { false }); } catch { } try { if (ContainerInUseField?.FieldType == typeof(bool)) { ContainerInUseField.SetValue(container, false); } } catch { } } public static string BuildContainerUid(Container container) { ZNetView component = ((Component)container).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); if (val == null) { return ((Object)container).GetInstanceID().ToString(); } return ((object)(ZDOID)(ref val.m_uid)).ToString(); } public static bool MatchKey(ItemData item, ItemKey key) { if ((Object)(object)item?.m_dropPrefab != (Object)null && ((Object)item.m_dropPrefab).name == ((ItemKey)(ref key)).PrefabName && item.m_quality == ((ItemKey)(ref key)).Quality && item.m_variant == ((ItemKey)(ref key)).Variant) { return item.m_stack > 0; } return false; } public static int GetTotalAmount(Inventory inventory, ItemKey key) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) int num = 0; foreach (ItemData allItem in inventory.GetAllItems()) { if (MatchKey(allItem, key)) { num += allItem.m_stack; } } return num; } public static int TryAddItemMeasured(Inventory inventory, ItemKey key, ItemData stack, int requestedAmount) { //IL_0001: 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) int totalAmount = GetTotalAmount(inventory, key); inventory.AddItem(stack); int totalAmount2 = GetTotalAmount(inventory, key); return ChunkedTransfer.ClampMeasuredMove(requestedAmount, totalAmount, totalAmount2); } public static ItemData? CreateItemStack(ItemKey key, int amount) { ObjectDB instance = ObjectDB.instance; GameObject obj = ((instance != null) ? instance.GetItemPrefab(((ItemKey)(ref key)).PrefabName) : null); ItemDrop val = ((obj != null) ? obj.GetComponent() : null); if (val?.m_itemData == null) { return null; } ItemData obj2 = val.m_itemData.Clone(); obj2.m_quality = ((ItemKey)(ref key)).Quality; obj2.m_variant = ((ItemKey)(ref key)).Variant; obj2.m_stack = amount; return obj2; } public static int GetMaxStackSize(ItemKey key) { ObjectDB instance = ObjectDB.instance; GameObject obj = ((instance != null) ? instance.GetItemPrefab(((ItemKey)(ref key)).PrefabName) : null); return (((obj != null) ? obj.GetComponent() : null)?.m_itemData?.m_shared?.m_maxStackSize).GetValueOrDefault(1); } public static int ResolveMaxStackSize(string prefabName) { ObjectDB instance = ObjectDB.instance; GameObject obj = ((instance != null) ? instance.GetItemPrefab(prefabName) : null); return (((obj != null) ? obj.GetComponent() : null)?.m_itemData?.m_shared?.m_maxStackSize).GetValueOrDefault(1); } public static bool TryDropItem(ItemData item, int amount, Vector3 position, Quaternion rotation) { //IL_0131: 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_00b4: Unknown result type (might be due to invalid IL or missing references) MethodInfo[] dropItemMethods = DropItemMethods; foreach (MethodInfo methodInfo in dropItemMethods) { if (methodInfo == null) { continue; } ParameterInfo[] parameters = methodInfo.GetParameters(); try { if (parameters.Length == 4 && parameters[0].ParameterType == typeof(ItemData) && parameters[1].ParameterType == typeof(int) && parameters[2].ParameterType == typeof(Vector3) && parameters[3].ParameterType == typeof(Quaternion)) { methodInfo.Invoke(null, new object[4] { item, amount, position, rotation }); return true; } if (parameters.Length == 3 && parameters[0].ParameterType == typeof(ItemData) && parameters[1].ParameterType == typeof(int) && parameters[2].ParameterType == typeof(Vector3)) { methodInfo.Invoke(null, new object[3] { item, amount, position }); return true; } } catch { } } return false; } public static int GetSubgroupOrder(ItemKey key) { if (string.IsNullOrWhiteSpace(((ItemKey)(ref key)).PrefabName)) { return 999; } string text = ((ItemKey)(ref key)).PrefabName.ToLowerInvariant(); if (text.Contains("ore") || text.Contains("scrap") || text.Contains("metal") || text.Contains("ingot") || text.Contains("bar")) { return 10; } if (text.Contains("wood") || text.Contains("stone")) { return 20; } if (text.Contains("hide") || text.Contains("leather") || text.Contains("scale") || text.Contains("chitin")) { return 30; } if (text.Contains("food") || text.Contains("mead") || text.Contains("stew") || text.Contains("soup") || text.Contains("bread")) { return 40; } return 100; } public static List? GetInventoryItemList(Inventory inventory) { return InventoryItemListField?.GetValue(inventory) as List; } public static void AddItemDirectly(Inventory inventory, ItemData item, int x, int y) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) item.m_gridPos = new Vector2i(x, y); List inventoryItemList = GetInventoryItemList(inventory); if (inventoryItemList != null) { inventoryItemList.Add(item); } else { inventory.AddItem(item); } } public static void NotifyInventoryChanged(Inventory inventory) { InventoryChangedMethod?.Invoke(inventory, null); } public static void ClearInventory(Inventory? inventory) { if (inventory == null) { return; } MethodInfo method = typeof(Inventory).GetMethod("RemoveAll", BindingFlags.Instance | BindingFlags.Public); if (method != null) { method.Invoke(inventory, null); return; } foreach (ItemData item in inventory.GetAllItems().ToList()) { inventory.RemoveItem(item, item.m_stack); } } public static bool IsDragInProgress() { if ((Object)(object)InventoryGui.instance == (Object)null || DragItemField == null) { return false; } object? value = DragItemField.GetValue(InventoryGui.instance); ItemData val = (ItemData)((value is ItemData) ? value : null); if (val != null) { return val.m_stack > 0; } return false; } public static RectTransform? GetContainerRect(InventoryGui gui) { object? obj = ContainerRectField?.GetValue(gui); return (RectTransform?)((obj is RectTransform) ? obj : null); } public static Container? GetCurrentContainer(InventoryGui gui) { object? obj = CurrentContainerField?.GetValue(gui); return (Container?)((obj is Container) ? obj : null); } public static InventoryGrid? GetContainerGrid(InventoryGui gui) { object? obj = ContainerGridField?.GetValue(gui); return (InventoryGrid?)((obj is InventoryGrid) ? obj : null); } public static TMP_Text? GetContainerName(InventoryGui gui) { object? obj = ContainerNameTextField?.GetValue(gui); return (TMP_Text?)((obj is TMP_Text) ? obj : null); } public static Button? GetTakeAllButton(InventoryGui gui) { object? obj = TakeAllButtonField?.GetValue(gui); return (Button?)((obj is Button) ? obj : null); } public static RectTransform? GetGridRoot(InventoryGrid grid) { object? obj = GridRootField?.GetValue(grid); return (RectTransform?)((obj is RectTransform) ? obj : null); } public static int GetGridHeight(InventoryGrid grid) { object obj = GridHeightField?.GetValue(grid); if (obj is int) { return (int)obj; } return 4; } public static void SetGridHeight(InventoryGrid grid, int height) { GridHeightField?.SetValue(grid, height); } public static float GetGridElementSpace(InventoryGrid grid) { object obj = GridElementSpaceField?.GetValue(grid); if (obj is float) { return (float)obj; } return 2f; } public static GameObject? GetGridElementPrefab(InventoryGrid grid) { object? obj = GridElementPrefabField?.GetValue(grid); return (GameObject?)((obj is GameObject) ? obj : null); } } } namespace UnifiedStorage.Mod.Session { public sealed class TerminalSessionService { private sealed class PendingReservation { public string TokenId { get; set; } = string.Empty; public ItemKey Key { get; set; } public int ReservedAmount { get; set; } public float ExpiresAt { get; set; } public bool CommitRequested { get; set; } } private sealed class PendingDeposit { public string RequestId { get; set; } = string.Empty; public ItemKey Key { get; set; } public int Amount { get; set; } } private struct CachedSortEntry { public string DisplayName; public int TypeOrder; public int SubgroupOrder; } private const float CloseGraceSeconds = 0.35f; private const float ReservationTtlSeconds = 3f; private const float TrackedChestRefreshSeconds = 1f; private const float OpenSessionRetryBaseSeconds = 0.5f; private const float OpenSessionRetryMaxSeconds = 8f; private readonly StorageConfig _config; private readonly IContainerScanner _scanner; private readonly TerminalRpcRoutes _routes; private readonly ManualLogSource _logger; private readonly StorageTrace _trace; private readonly Dictionary _authoritativeTotals = new Dictionary(); private readonly Dictionary _displayedTotals = new Dictionary(); private readonly Dictionary _prototypes = new Dictionary(); private readonly Dictionary _stackSizes = new Dictionary(); private readonly List _pendingReservations = new List(); private readonly List _pendingDeposits = new List(); private readonly Dictionary _sortCache = new Dictionary(); private List> _sortedItems = new List>(); private long _lastProjectedContentHash; private Container? _terminal; private Player? _player; private List _trackedChests = new List(); private string _sessionId = string.Empty; private string _terminalUid = string.Empty; private string _searchQuery = string.Empty; private float _scanRadius; private float _pendingCloseSince = -1f; private float _nextTrackedChestRefreshAt; private float _nextSnapshotRetryAt; private bool _isApplyingProjection; private bool _hasAuthoritativeSnapshot; private int _slotsTotalPhysical; private int _slotsUsedDisplay; private int _chestCount; private long _revision; private int _openSessionFailureCount; private int _originalInventoryWidth; private int _originalInventoryHeight; private int _contentRows; private int _uiRevision; public bool IsActive { get { if ((Object)(object)_terminal != (Object)null) { return (Object)(object)_player != (Object)null; } return false; } } public bool IsApplyingProjection => _isApplyingProjection; public int SlotsTotalPhysical => _slotsTotalPhysical; public int SlotsUsedDisplay => _slotsUsedDisplay; public int ChestsInRange => _chestCount; public int UiRevision => _uiRevision; public int ContentRows => _contentRows; public bool IsStorageFull { get { if (_slotsTotalPhysical > 0) { return _slotsUsedDisplay >= _slotsTotalPhysical; } return false; } } public int SlotsUsedVirtual { get { //IL_002a: Unknown result type (might be due to invalid IL or missing references) int num = 0; foreach (KeyValuePair authoritativeTotal in _authoritativeTotals) { if (authoritativeTotal.Value > 0) { int value; int num2 = ((!_stackSizes.TryGetValue(authoritativeTotal.Key, out value) || value <= 0) ? 1 : value); num += (int)Math.Ceiling((double)authoritativeTotal.Value / (double)num2); } } return num; } } public TerminalSessionService(StorageConfig config, IContainerScanner scanner, TerminalRpcRoutes routes, ManualLogSource logger, StorageTrace trace) { _config = config; _scanner = scanner; _routes = routes; _logger = logger; _trace = trace; _routes.SessionSnapshotReceived += OnSessionSnapshotReceived; _routes.ReserveResultReceived += OnReserveResultReceived; _routes.ApplyResultReceived += OnApplyResultReceived; _routes.SessionDeltaReceived += OnSessionDeltaReceived; } public bool IsTerminalInventory(Inventory inventory) { if (!IsActive || (Object)(object)_terminal == (Object)null || inventory == null) { return false; } return inventory == _terminal.GetInventory(); } public bool HasDrawerPriorityFor(ItemKey key) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) return _trackedChests.Any((ChestHandle c) => c.Source.IsValid && c.Source.DepositPriority(key) == 0); } public bool HasAnyCapacityFor(ItemKey key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) int num = ReflectionHelpers.GetMaxStackSize(key); if (num <= 0) { num = 1; } foreach (ChestHandle trackedChest in _trackedChests) { if (trackedChest.Source.IsValid && trackedChest.Source.HasCapacityFor(key, num)) { return true; } } return false; } public bool IsTrackedInventory(Inventory inventory) { Inventory inventory2 = inventory; if (!IsActive || inventory2 == null || (Object)(object)_terminal == (Object)null) { return false; } Inventory inventory3 = _terminal.GetInventory(); if (inventory2 == inventory3) { return false; } return _trackedChests.Any((ChestHandle chest) => chest.Source.OwnsInventory(inventory2)); } public bool HandleContainerInteract(Container container, Player player) { if (!UnifiedTerminal.IsTerminal(container)) { return false; } BeginSession(container, player); return true; } public void BeginSession(Container terminal, Player player) { EndSession(); ReflectionHelpers.ForceReleaseContainerUse(terminal); _terminal = terminal; _player = player; _searchQuery = string.Empty; _sessionId = string.Empty; _terminalUid = ReflectionHelpers.BuildContainerUid(terminal); _pendingCloseSince = -1f; _scanRadius = _config.ScanRadius.Value; CaptureOriginalInventorySize(terminal.GetInventory()); _revision = 0L; _slotsTotalPhysical = 0; _slotsUsedDisplay = 0; _chestCount = 0; _hasAuthoritativeSnapshot = false; _openSessionFailureCount = 0; _nextSnapshotRetryAt = Time.unscaledTime; _pendingReservations.Clear(); _pendingDeposits.Clear(); _authoritativeTotals.Clear(); _displayedTotals.Clear(); _prototypes.Clear(); _stackSizes.Clear(); StorageTrace trace = _trace; Player? player2 = _player; trace.Dev(string.Format("Terminal opened by {0} (radius {1:0.0}m).", ((player2 != null) ? player2.GetPlayerName() : null) ?? "unknown", _scanRadius)); RefreshTrackedChestHandles(); RefreshTerminalInventoryFromAuthoritative(); RequestSessionSnapshot(); _uiRevision++; } public void Tick() { if (!IsActive || (Object)(object)_terminal == (Object)null) { return; } if ((Object)(object)InventoryGui.instance == (Object)null || !InventoryGui.IsVisible() || !InventoryGui.instance.IsContainerOpen()) { if (_pendingCloseSince < 0f) { _pendingCloseSince = Time.unscaledTime; } else if (Time.unscaledTime - _pendingCloseSince >= 0.35f) { EndSession(); } return; } _pendingCloseSince = -1f; if (Time.unscaledTime >= _nextTrackedChestRefreshAt) { RefreshTrackedChestHandles(); } if (!_hasAuthoritativeSnapshot && Time.unscaledTime >= _nextSnapshotRetryAt) { RequestSessionSnapshot(); } ExpireLocalReservations(); if (!ReflectionHelpers.IsDragInProgress()) { CommitPendingReservations(); } } public void SetSearchQuery(string query) { if (IsActive && !((Object)(object)_terminal == (Object)null)) { string text = query?.Trim() ?? string.Empty; if (!string.Equals(text, _searchQuery, StringComparison.Ordinal)) { _searchQuery = text; _lastProjectedContentHash = 0L; RefreshTerminalInventoryFromAuthoritative(); _uiRevision++; } } } public void NotifyContainerInteraction() { if (!IsActive || (Object)(object)_terminal == (Object)null) { return; } if (!IsSessionTerminal(_terminal)) { EndSession(); return; } Dictionary dictionary = CaptureCurrentDisplayedTotals(); ApplyDisplayedDeltaAsOperations(dictionary); ReplaceDisplayedTotals(dictionary); if (!ReflectionHelpers.IsDragInProgress()) { CommitPendingReservations(); } } public void RefreshFromWorldChange() { if (IsActive && !((Object)(object)_terminal == (Object)null)) { if (!IsSessionTerminal(_terminal)) { EndSession(); } else { RequestSessionSnapshot(); } } } public void EndSession() { if (IsActive) { _trace.Dev("Terminal closed."); } if ((Object)(object)_terminal != (Object)null && !string.IsNullOrWhiteSpace(_terminalUid)) { _routes.RequestCloseSession(new CloseSessionRequestDto { RequestId = Guid.NewGuid().ToString("N"), SessionId = _sessionId, TerminalUid = _terminalUid, PlayerId = ResolveLocalPlayerId() }); } if ((Object)(object)_terminal != (Object)null) { ReflectionHelpers.ForceReleaseContainerUse(_terminal); ReflectionHelpers.ClearInventory(_terminal.GetInventory()); RestoreTerminalInventorySize(_terminal.GetInventory()); } _terminal = null; _player = null; _trackedChests.Clear(); _sessionId = string.Empty; _terminalUid = string.Empty; _searchQuery = string.Empty; _scanRadius = 0f; _pendingCloseSince = -1f; _nextTrackedChestRefreshAt = 0f; _nextSnapshotRetryAt = 0f; _pendingReservations.Clear(); _pendingDeposits.Clear(); _authoritativeTotals.Clear(); _displayedTotals.Clear(); _prototypes.Clear(); _stackSizes.Clear(); _sortCache.Clear(); _sortedItems.Clear(); _lastProjectedContentHash = 0L; _slotsTotalPhysical = 0; _slotsUsedDisplay = 0; _chestCount = 0; _revision = 0L; _hasAuthoritativeSnapshot = false; _openSessionFailureCount = 0; _contentRows = 0; _uiRevision++; } private void OnSessionSnapshotReceived(OpenSessionResponseDto response) { if (!IsActive) { return; } if (!response.Success) { if (IsIdentityFailure(response.Reason)) { EndSession(); return; } _openSessionFailureCount = Math.Min(_openSessionFailureCount + 1, 8); _nextSnapshotRetryAt = Time.unscaledTime + GetRetryDelay(_openSessionFailureCount); } else if (CanApplySnapshot(response.Snapshot)) { ApplySnapshot(response.Snapshot); } } private void OnReserveResultReceived(ReserveWithdrawResultDto result) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) if (IsActive && CanApplySnapshot(result.Snapshot)) { if (result.Success && !string.IsNullOrWhiteSpace(result.TokenId) && result.ReservedAmount > 0) { _trace.Dev($"Withdrew {result.ReservedAmount}x {GetDisplayName(result.Key)}."); _pendingReservations.Add(new PendingReservation { TokenId = result.TokenId, Key = result.Key, ReservedAmount = result.ReservedAmount, ExpiresAt = Time.unscaledTime + 3f }); } ApplySnapshot(result.Snapshot); } } private void OnApplyResultReceived(ApplyResultDto result) { if (!IsActive) { return; } if (string.Equals(result.OperationType, "deposit", StringComparison.Ordinal)) { HandleDepositResult(result); } if (string.Equals(result.OperationType, "commit", StringComparison.Ordinal)) { if (result.Success) { RemovePendingToken(result.TokenId); } else { MarkPendingCommitAsRetryable(result.TokenId); RequestSessionSnapshot(); } } else if (string.Equals(result.OperationType, "cancel", StringComparison.Ordinal) && !result.Success) { RemovePendingToken(result.TokenId); RequestSessionSnapshot(); } if (CanApplySnapshot(result.Snapshot)) { ApplySnapshot(result.Snapshot); } else if (!result.Success) { RequestSessionSnapshot(); } } private void OnSessionDeltaReceived(SessionDeltaDto delta) { if (IsActive && string.Equals(delta.TerminalUid, _terminalUid, StringComparison.Ordinal) && (string.IsNullOrWhiteSpace(delta.SessionId) || string.IsNullOrWhiteSpace(_sessionId) || string.Equals(delta.SessionId, _sessionId, StringComparison.Ordinal)) && delta.Revision >= _revision) { ApplySnapshot(delta.Snapshot); } } private bool CanApplySnapshot(SessionSnapshotDto snapshot) { if (!IsActive || (Object)(object)_terminal == (Object)null) { return false; } if (!string.Equals(snapshot.TerminalUid, _terminalUid, StringComparison.Ordinal)) { return false; } if (!string.IsNullOrWhiteSpace(snapshot.SessionId) && !string.IsNullOrWhiteSpace(_sessionId) && !string.Equals(snapshot.SessionId, _sessionId, StringComparison.Ordinal)) { return false; } return true; } private void ApplySnapshot(SessionSnapshotDto snapshot) { //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) if (!CanApplySnapshot(snapshot) || snapshot.Revision < _revision) { return; } if (!string.IsNullOrWhiteSpace(snapshot.SessionId)) { _sessionId = snapshot.SessionId; } _revision = snapshot.Revision; _slotsTotalPhysical = snapshot.SlotsTotalPhysical; _slotsUsedDisplay = snapshot.SlotsUsedVirtual; _chestCount = snapshot.ChestCount; _hasAuthoritativeSnapshot = true; _openSessionFailureCount = 0; _nextSnapshotRetryAt = Time.unscaledTime + 60f; int count = _authoritativeTotals.Count; _authoritativeTotals.Clear(); _stackSizes.Clear(); foreach (AggregatedItem item in snapshot.Items) { if (item.TotalAmount <= 0) { continue; } _authoritativeTotals[item.Key] = item.TotalAmount; _stackSizes[item.Key] = ((item.StackSize <= 0) ? 1 : item.StackSize); if (!_prototypes.ContainsKey(item.Key)) { ObjectDB instance = ObjectDB.instance; object obj; ItemKey key; if (instance == null) { obj = null; } else { key = item.Key; obj = instance.GetItemPrefab(((ItemKey)(ref key)).PrefabName); } ItemDrop val = ((obj != null) ? ((GameObject)obj).GetComponent() : null); if (val?.m_itemData != null) { ItemData val2 = val.m_itemData.Clone(); key = item.Key; val2.m_quality = ((ItemKey)(ref key)).Quality; key = item.Key; val2.m_variant = ((ItemKey)(ref key)).Variant; _prototypes[item.Key] = val2; } } } if (_authoritativeTotals.Count != count) { _sortCache.Clear(); } _trace.Dev($"Storage updated: {_chestCount} chest(s), {_authoritativeTotals.Count} item type(s)."); RefreshTrackedChestHandles(); RefreshTerminalInventoryFromAuthoritative(); _uiRevision++; } private void RefreshTerminalInventoryFromAuthoritative() { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_terminal == (Object)null) { return; } Inventory inventory = _terminal.GetInventory(); if (inventory == null) { return; } long num = ComputeContentHash(); if (num == _lastProjectedContentHash) { return; } _lastProjectedContentHash = num; _isApplyingProjection = true; try { ReflectionHelpers.ClearInventory(inventory); _displayedTotals.Clear(); int num2 = Math.Max(1, ReflectionHelpers.GetInventoryWidth(inventory)); RebuildSortedItems(); int num3 = 0; foreach (KeyValuePair sortedItem in _sortedItems) { int value; int num4 = ((!_stackSizes.TryGetValue(sortedItem.Key, out value) || value <= 0) ? 1 : value); num3 += (int)Math.Ceiling((double)sortedItem.Value / (double)num4); } int num5 = ((_slotsTotalPhysical > _slotsUsedDisplay) ? 1 : 0); int num6 = Math.Max(1, num3 + num5); _contentRows = Math.Max(1, (int)Math.Ceiling((float)num6 / (float)num2)); ReflectionHelpers.SetInventorySize(inventory, num2, _contentRows); int num7 = 0; foreach (KeyValuePair sortedItem2 in _sortedItems) { int num8 = sortedItem2.Value; int value2; int num9 = ((!_stackSizes.TryGetValue(sortedItem2.Key, out value2) || value2 <= 0) ? 1 : value2); while (num8 > 0) { int num10 = Math.Min(num9, num8); ItemData val = CreateProjectedItem(sortedItem2.Key, num10, num9); if (val == null) { break; } int x = num7 % num2; int y = num7 / num2; ReflectionHelpers.AddItemDirectly(inventory, val, x, y); num7++; num8 -= num10; } _displayedTotals[sortedItem2.Key] = sortedItem2.Value - num8; } ReflectionHelpers.NotifyInventoryChanged(inventory); } finally { _isApplyingProjection = false; } } private long ComputeContentHash() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) long num = 17L; foreach (KeyValuePair authoritativeTotal in _authoritativeTotals) { long num2 = num * 31; ItemKey key = authoritativeTotal.Key; num = num2 + ((object)(ItemKey)(ref key)).GetHashCode(); num = num * 31 + authoritativeTotal.Value; } return num * 31 + _searchQuery.GetHashCode(); } private void RebuildSortedItems() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) _sortedItems.Clear(); foreach (KeyValuePair authoritativeTotal in _authoritativeTotals) { if (authoritativeTotal.Value > 0) { EnsureSortCacheEntry(authoritativeTotal.Key); if (MatchesSearch(_sortCache[authoritativeTotal.Key].DisplayName)) { _sortedItems.Add(authoritativeTotal); } } } _sortedItems.Sort(delegate(KeyValuePair a, KeyValuePair b) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) CachedSortEntry cachedSortEntry = _sortCache[a.Key]; CachedSortEntry cachedSortEntry2 = _sortCache[b.Key]; int num = cachedSortEntry.TypeOrder.CompareTo(cachedSortEntry2.TypeOrder); if (num != 0) { return num; } num = cachedSortEntry.SubgroupOrder.CompareTo(cachedSortEntry2.SubgroupOrder); if (num != 0) { return num; } num = string.Compare(cachedSortEntry.DisplayName, cachedSortEntry2.DisplayName, StringComparison.OrdinalIgnoreCase); return (num != 0) ? num : b.Value.CompareTo(a.Value); }); } private void EnsureSortCacheEntry(ItemKey key) { //IL_0006: 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_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) if (!_sortCache.ContainsKey(key)) { _sortCache[key] = new CachedSortEntry { DisplayName = GetDisplayName(key), TypeOrder = GetItemTypeOrder(key), SubgroupOrder = ReflectionHelpers.GetSubgroupOrder(key) }; } } private ItemData? CreateProjectedItem(ItemKey key, int amount, int maxStackSize) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) if (!_prototypes.TryGetValue(key, out ItemData value)) { ObjectDB instance = ObjectDB.instance; GameObject obj = ((instance != null) ? instance.GetItemPrefab(((ItemKey)(ref key)).PrefabName) : null); ItemDrop val = ((obj != null) ? obj.GetComponent() : null); if (val?.m_itemData == null) { return null; } value = val.m_itemData.Clone(); value.m_quality = ((ItemKey)(ref key)).Quality; value.m_variant = ((ItemKey)(ref key)).Variant; _prototypes[key] = value; } ItemData obj2 = value.Clone(); obj2.m_stack = amount; return obj2; } private bool MatchesSearch(string displayName) { if (!string.IsNullOrWhiteSpace(_searchQuery)) { return displayName.IndexOf(_searchQuery, StringComparison.OrdinalIgnoreCase) >= 0; } return true; } private void RefreshTrackedChestHandles() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_terminal == (Object)null) { _trackedChests.Clear(); _nextTrackedChestRefreshAt = Time.unscaledTime + 1f; } else { _trackedChests = _scanner.GetNearbyContainers(((Component)_terminal).transform.position, _scanRadius, _terminal).ToList(); _nextTrackedChestRefreshAt = Time.unscaledTime + 1f; } } private void ApplyDisplayedDeltaAsOperations(Dictionary currentDisplayed) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0042: 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) //IL_006c: Unknown result type (might be due to invalid IL or missing references) HashSet hashSet = new HashSet(_displayedTotals.Keys); hashSet.UnionWith(currentDisplayed.Keys); foreach (ItemKey item in hashSet) { int value; int num = (_displayedTotals.TryGetValue(item, out value) ? value : 0); int value2; int num2 = (currentDisplayed.TryGetValue(item, out value2) ? value2 : 0) - num; if (num2 < 0) { RequestReserveWithdraw(item, -num2); } else if (num2 > 0) { RequestCancelAndOrDeposit(item, num2); } } } private void RequestReserveWithdraw(ItemKey key, int amount) { //IL_0071: Unknown result type (might be due to invalid IL or missing references) if (amount > 0) { _routes.RequestReserveWithdraw(new ReserveWithdrawRequestDto { RequestId = Guid.NewGuid().ToString("N"), SessionId = _sessionId, OperationId = Guid.NewGuid().ToString("N"), TerminalUid = _terminalUid, PlayerId = ResolveLocalPlayerId(), ExpectedRevision = _revision, Key = key, Amount = amount }); } } private void RequestCancelAndOrDeposit(ItemKey key, int amount) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) if (amount <= 0) { return; } int num = amount; foreach (PendingReservation item in _pendingReservations.Where(delegate(PendingReservation r) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: 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) ItemKey key2 = r.Key; return ((ItemKey)(ref key2)).Equals(key) && r.ReservedAmount > 0; }).ToList()) { if (num <= 0) { break; } int num2 = Math.Min(num, item.ReservedAmount); if (num2 > 0) { _routes.RequestCancelReservation(new CancelReservationRequestDto { RequestId = Guid.NewGuid().ToString("N"), SessionId = _sessionId, OperationId = Guid.NewGuid().ToString("N"), TerminalUid = _terminalUid, PlayerId = ResolveLocalPlayerId(), TokenId = item.TokenId, Amount = num2 }); item.ReservedAmount -= num2; num -= num2; if (item.ReservedAmount <= 0) { _pendingReservations.Remove(item); } } } if (num > 0) { string requestId = Guid.NewGuid().ToString("N"); _pendingDeposits.Add(new PendingDeposit { RequestId = requestId, Key = key, Amount = num }); _routes.RequestDeposit(new DepositRequestDto { RequestId = requestId, SessionId = _sessionId, OperationId = Guid.NewGuid().ToString("N"), TerminalUid = _terminalUid, PlayerId = ResolveLocalPlayerId(), ExpectedRevision = _revision, Key = key, Amount = num }); } } private void CommitPendingReservations() { foreach (PendingReservation item in _pendingReservations.Where((PendingReservation r) => !r.CommitRequested && r.ReservedAmount > 0).ToList()) { _routes.RequestCommitReservation(new CommitReservationRequestDto { RequestId = Guid.NewGuid().ToString("N"), SessionId = _sessionId, OperationId = Guid.NewGuid().ToString("N"), TerminalUid = _terminalUid, TokenId = item.TokenId }); item.CommitRequested = true; } } private void ExpireLocalReservations() { float unscaledTime = Time.unscaledTime; bool flag = false; for (int num = _pendingReservations.Count - 1; num >= 0; num--) { if (!(_pendingReservations[num].ExpiresAt > unscaledTime)) { _pendingReservations.RemoveAt(num); flag = true; } } if (flag) { RequestSessionSnapshot(); } } private void RequestSessionSnapshot() { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) if (IsActive && !((Object)(object)_terminal == (Object)null)) { _routes.RequestOpenSession(new OpenSessionRequestDto { RequestId = Guid.NewGuid().ToString("N"), SessionId = _sessionId, TerminalUid = _terminalUid, PlayerId = ResolveLocalPlayerId(), AnchorX = ((Component)_terminal).transform.position.x, AnchorY = ((Component)_terminal).transform.position.y, AnchorZ = ((Component)_terminal).transform.position.z, Radius = _scanRadius }); _nextSnapshotRetryAt = Time.unscaledTime + 0.5f; } } private Dictionary CaptureCurrentDisplayedTotals() { //IL_0079: 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) Dictionary dictionary = new Dictionary(); if ((Object)(object)_terminal == (Object)null) { return dictionary; } Inventory inventory = _terminal.GetInventory(); if (inventory == null) { return dictionary; } ItemKey key = default(ItemKey); foreach (ItemData allItem in inventory.GetAllItems()) { if (!((Object)(object)allItem?.m_dropPrefab == (Object)null) && allItem.m_stack > 0) { ((ItemKey)(ref key))..ctor(((Object)allItem.m_dropPrefab).name, allItem.m_quality, allItem.m_variant); dictionary[key] = (dictionary.TryGetValue(key, out var value) ? (value + allItem.m_stack) : allItem.m_stack); } } return dictionary; } private void ReplaceDisplayedTotals(Dictionary totals) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) _displayedTotals.Clear(); foreach (KeyValuePair total in totals) { _displayedTotals[total.Key] = total.Value; } } private bool IsSessionTerminal(Container container) { if ((Object)(object)container != (Object)null && !string.IsNullOrWhiteSpace(_terminalUid)) { return string.Equals(ReflectionHelpers.BuildContainerUid(container), _terminalUid, StringComparison.Ordinal); } return false; } private void HandleDepositResult(ApplyResultDto result) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) ApplyResultDto result2 = result; PendingDeposit pendingDeposit = _pendingDeposits.FirstOrDefault((PendingDeposit p) => string.Equals(p.RequestId, result2.RequestId, StringComparison.Ordinal)); if (pendingDeposit != null) { _pendingDeposits.Remove(pendingDeposit); if (result2.Success) { _trace.Dev($"Deposited {result2.AppliedAmount}x {GetDisplayName(pendingDeposit.Key)}."); } else if (ShouldRestoreFailedDeposit(result2.Reason)) { RestoreToLocalPlayerInventory(pendingDeposit.Key, pendingDeposit.Amount); } } } private void RestoreToLocalPlayerInventory(ItemKey key, int amount) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) if (amount > 0 && !((Object)(object)_player == (Object)null)) { int num = ReflectionHelpers.GetMaxStackSize(key); if (num <= 0) { num = 1; } Inventory inventory = ((Humanoid)_player).GetInventory(); int num2 = ChunkedTransfer.Move(amount, num, (Func)delegate(int chunkAmount) { //IL_0001: 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) ItemData val = ReflectionHelpers.CreateItemStack(key, chunkAmount); return (val != null) ? ReflectionHelpers.TryAddItemMeasured(inventory, key, val, chunkAmount) : 0; }); int num3 = amount - num2; if (num3 > 0) { DropNearPlayer(key, num3); } } } private void DropNearPlayer(ItemKey key, int amount) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_player == (Object)null || amount <= 0) { return; } int num = ReflectionHelpers.GetMaxStackSize(key); if (num <= 0) { num = 1; } int num2 = amount; while (num2 > 0) { int num3 = Math.Min(num, num2); ItemData val = ReflectionHelpers.CreateItemStack(key, num3); if (val != null && ReflectionHelpers.TryDropItem(val, num3, ((Component)_player).transform.position + ((Component)_player).transform.forward + Vector3.up, Quaternion.identity)) { num2 -= num3; continue; } break; } } private void RemovePendingToken(string tokenId) { string tokenId2 = tokenId; if (!string.IsNullOrWhiteSpace(tokenId2)) { _pendingReservations.RemoveAll((PendingReservation r) => string.Equals(r.TokenId, tokenId2, StringComparison.Ordinal)); } } private void MarkPendingCommitAsRetryable(string tokenId) { string tokenId2 = tokenId; foreach (PendingReservation item in _pendingReservations.Where((PendingReservation r) => string.Equals(r.TokenId, tokenId2, StringComparison.Ordinal))) { item.CommitRequested = false; } } private long ResolveLocalPlayerId() { if (!((Object)(object)_player != (Object)null)) { return 0L; } return _player.GetPlayerID(); } private string GetDisplayName(ItemKey key) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (_prototypes.TryGetValue(key, out ItemData value)) { string name = value.m_shared.m_name; Localization instance = Localization.instance; string text = ((instance != null) ? instance.Localize(name) : null); if (string.IsNullOrEmpty(text)) { return name; } return text; } return ((ItemKey)(ref key)).PrefabName; } private int GetItemTypeOrder(ItemKey key) { //IL_0006: 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) //IL_001c: Expected I4, but got Unknown if (_prototypes.TryGetValue(key, out ItemData value)) { return (int)value.m_shared.m_itemType; } return int.MaxValue; } private void CaptureOriginalInventorySize(Inventory? inventory) { if (inventory == null) { _originalInventoryWidth = 8; _originalInventoryHeight = 4; } else { _originalInventoryWidth = ReflectionHelpers.GetInventoryWidth(inventory); _originalInventoryHeight = ReflectionHelpers.GetInventoryHeight(inventory); } } private void RestoreTerminalInventorySize(Inventory? inventory) { if (inventory != null && _originalInventoryWidth > 0 && _originalInventoryHeight > 0) { ReflectionHelpers.SetInventorySize(inventory, _originalInventoryWidth, _originalInventoryHeight); } } private static bool ShouldRestoreFailedDeposit(string reason) { if (!string.IsNullOrWhiteSpace(reason)) { switch (reason) { default: return reason == "Unable to resolve player identity"; case "Conflict": case "Player not found": case "Player has no matching item": case "Player/terminal has no matching item": case "Session not found": case "No storage space": case "Player identity mismatch": return true; } } return false; } private static bool IsIdentityFailure(string reason) { if (!(reason == "Player identity mismatch")) { return reason == "Unable to resolve player identity"; } return true; } private static float GetRetryDelay(int failureCount) { if (failureCount <= 0) { return 0.5f; } int num = Math.Min(6, failureCount - 1); float num2 = 0.5f * Mathf.Pow(2f, (float)num); return Mathf.Min(8f, num2); } } } namespace UnifiedStorage.Mod.Server { public static class ChestInclusionRules { private const string IncludeInUnifiedKey = "US_IncludeInUnified"; private static readonly HashSet VanillaChestPrefabNames = new HashSet(StringComparer.OrdinalIgnoreCase) { "piece_chest", "piece_chest_wood", "piece_chest_private", "piece_chest_blackmetal" }; public static bool IsVanillaChest(Container? container) { if ((Object)(object)container == (Object)null) { return false; } string item = NormalizePrefabName(((Object)((Component)container).gameObject).name); return VanillaChestPrefabNames.Contains(item); } public static bool IsIncludedInUnified(Container? container) { if ((Object)(object)container == (Object)null) { return false; } return IsIncludedInUnified(((Component)container).GetComponent()); } public static bool IsIncludedInUnified(ZNetView? znetView) { ZDO val = ((znetView != null) ? znetView.GetZDO() : null); if (val == null) { return true; } return val.GetBool("US_IncludeInUnified", true); } public static bool TrySetIncludedInUnified(Container? container, bool includeInUnified) { if ((Object)(object)container == (Object)null) { return false; } return TrySetIncludedInUnified(((Component)container).GetComponent(), includeInUnified); } public static bool TrySetIncludedInUnified(ZNetView? znetView, bool includeInUnified) { ZDO val = ((znetView != null) ? znetView.GetZDO() : null); if (val == null) { return false; } try { if ((Object)(object)znetView != (Object)null && !znetView.IsOwner()) { znetView.ClaimOwnership(); } } catch { } val.Set("US_IncludeInUnified", includeInUnified); return true; } private static string NormalizePrefabName(string name) { if (name.EndsWith("(Clone)", StringComparison.Ordinal)) { return name.Substring(0, name.Length - "(Clone)".Length); } return name; } } public sealed class ContainerScanner : IContainerScanner { private readonly StorageConfig _config; public ContainerScanner(StorageConfig config) { _config = config; } public IReadOnlyList GetNearbyContainers(Vector3 center, float radius, Container? ignoreContainer = null) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) Container ignoreContainer2 = ignoreContainer; int maxContainersScannedLimit = _config.MaxContainersScannedLimit; IEnumerable first = from handle in (from container in (from container in Object.FindObjectsByType((FindObjectsSortMode)0) where (Object)(object)container != (Object)null select container).Where(IsStaticChest) where (Object)(object)ignoreContainer2 == (Object)null || (Object)(object)container != (Object)(object)ignoreContainer2 where !UnifiedTerminal.IsTerminal(container) select container).Where(ChestInclusionRules.IsIncludedInUnified).Select(delegate(Container container) { //IL_0001: 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) float distance = Vector3.Distance(center, ((Component)container).transform.position); string sourceId = BuildSourceId(container); IStorageSource source = new ContainerStorageSource(sourceId, container, distance, _config); return new ChestHandle(sourceId, source, distance); }) where handle.Distance <= radius select handle; IEnumerable second = BuildDrawerHandles(center, radius); return (from handle in first.Concat(second) orderby handle.Distance, handle.SourceId select handle).Take(maxContainersScannedLimit).ToList(); } private static IEnumerable BuildDrawerHandles(Vector3 center, float radius) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) if (!ItemDrawersApi.IsAvailable) { yield break; } foreach (DrawerSnapshot item in ItemDrawersApi.GetDrawersInRange(center, radius)) { if (ChestInclusionRules.IsIncludedInUnified(item.ZNetView)) { float distance = Vector3.Distance(center, item.Position); IStorageSource source = new DrawerStorageSource(item, distance); yield return new ChestHandle(item.SourceId, source, distance); } } } private static bool IsStaticChest(Container container) { if (!((Component)container).gameObject.activeInHierarchy) { return false; } if ((Object)(object)((Component)container).GetComponentInParent() != (Object)null) { return false; } if ((Object)(object)((Component)container).GetComponentInParent() != (Object)null) { return false; } return true; } private static string BuildSourceId(Container container) { ZNetView component = ((Component)container).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); if (val != null) { return ((object)(ZDOID)(ref val.m_uid)).ToString(); } return ((Object)container).GetInstanceID().ToString(); } } public sealed class ContainerStorageSource : IStorageSource { private readonly Container _container; private readonly StorageConfig _config; public string SourceId { get; } public float Distance { get; } public bool IsValid { get { if ((Object)(object)_container != (Object)null) { return ((Component)_container).gameObject.activeInHierarchy; } return false; } } public int PhysicalSlotCount { get { Inventory inventory = _container.GetInventory(); if (inventory == null) { return 0; } return ReflectionHelpers.GetInventoryWidth(inventory) * ReflectionHelpers.GetInventoryHeight(inventory); } } public int DisplaySlotsUsed { get { Inventory inventory = _container.GetInventory(); if (inventory == null) { return 0; } return inventory.GetAllItems().Count; } } public ContainerStorageSource(string sourceId, Container container, float distance, StorageConfig config) { SourceId = sourceId; _container = container; Distance = distance; _config = config; } public IReadOnlyList ReadStacks() { //IL_0052: 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_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: 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_00c4: Expected O, but got Unknown Inventory inventory = _container.GetInventory(); if (inventory == null) { return Array.Empty(); } List list = new List(); foreach (ItemData allItem in inventory.GetAllItems()) { if (!((Object)(object)allItem?.m_dropPrefab == (Object)null) && allItem.m_stack > 0) { list.Add(new SourceStack { Key = new ItemKey(((Object)allItem.m_dropPrefab).name, allItem.m_quality, allItem.m_variant), DisplayName = allItem.m_shared.m_name, Amount = allItem.m_stack, StackSize = allItem.m_shared.m_maxStackSize, Distance = Distance, SourceId = SourceId }); } } return list; } public ItemData? GetItemPrototype(ItemKey key) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) Inventory inventory = _container.GetInventory(); if (inventory == null) { return null; } ItemKey val = default(ItemKey); foreach (ItemData allItem in inventory.GetAllItems()) { if (!((Object)(object)allItem?.m_dropPrefab == (Object)null)) { ((ItemKey)(ref val))..ctor(((Object)allItem.m_dropPrefab).name, allItem.m_quality, allItem.m_variant); if (((ItemKey)(ref val)).Equals(key)) { return allItem.Clone(); } } } return null; } public bool CanPlayerAccess(Player? player) { if (!_config.RequireAccessCheck || (Object)(object)player == (Object)null) { return true; } return ReflectionHelpers.CanAccess(_container, player); } public int DepositPriority(ItemKey key) { return 1; } public bool HasCapacityFor(ItemKey key, int maxStack) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) Inventory inventory = _container.GetInventory(); if (inventory == null) { return false; } List allItems = inventory.GetAllItems(); foreach (ItemData item in allItems) { if (ReflectionHelpers.MatchKey(item, key) && item.m_stack < maxStack) { return true; } } int num = ReflectionHelpers.GetInventoryWidth(inventory) * ReflectionHelpers.GetInventoryHeight(inventory); return allItems.Count < num; } public int RemoveItems(ItemKey key, int amount) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (amount <= 0) { return 0; } Inventory inventory = _container.GetInventory(); if (inventory == null) { return 0; } int num = amount; foreach (ItemData item in inventory.GetAllItems().FindAll((ItemData i) => ReflectionHelpers.MatchKey(i, key))) { if (num <= 0) { break; } int num2 = Math.Min(item.m_stack, num); inventory.RemoveItem(item, num2); num -= num2; } return amount - num; } public int AddItems(ItemKey key, int amount) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (amount <= 0) { return 0; } Inventory inv = _container.GetInventory(); if (inv == null) { return 0; } int num = ReflectionHelpers.GetMaxStackSize(key); if (num <= 0) { num = 1; } return ChunkedTransfer.Move(amount, num, (Func)delegate(int chunkAmount) { //IL_0001: 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) ItemData val = ReflectionHelpers.CreateItemStack(key, chunkAmount); return (val != null) ? ReflectionHelpers.TryAddItemMeasured(inv, key, val, chunkAmount) : 0; }); } public bool OwnsInventory(Inventory inventory) { if (inventory != null) { return inventory == _container.GetInventory(); } return false; } } public sealed class DrawerStorageSource : IStorageSource { private readonly DrawerSnapshot _snapshot; public string SourceId { get; } public float Distance { get; } public bool IsValid { get { if ((Object)(object)_snapshot.ZNetView != (Object)null && ((Behaviour)_snapshot.ZNetView).isActiveAndEnabled) { return _snapshot.ZNetView.GetZDO() != null; } return false; } } public int PhysicalSlotCount => IsValid ? 1 : 0; public int DisplaySlotsUsed { get { if (!IsValid) { return 0; } return (_snapshot.ZNetView.GetZDO().GetInt("Amount", 0) > 0) ? 1 : 0; } } public DrawerStorageSource(DrawerSnapshot snapshot, float distance) { _snapshot = snapshot; SourceId = snapshot.SourceId; Distance = distance; } public IReadOnlyList ReadStacks() { //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_007c: 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_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown if (!IsValid) { return Array.Empty(); } ZDO zDO = _snapshot.ZNetView.GetZDO(); string @string = zDO.GetString("Prefab", ""); int @int = zDO.GetInt("Amount", 0); int int2 = zDO.GetInt("Quality", 1); if (string.IsNullOrEmpty(@string) || @int <= 0) { return Array.Empty(); } int num = ReflectionHelpers.ResolveMaxStackSize(@string); if (num <= 0) { num = int.MaxValue; } return (IReadOnlyList)(object)new SourceStack[1] { new SourceStack { Key = new ItemKey(@string, int2, 0), DisplayName = LocalizeName(@string), Amount = @int, StackSize = num, Distance = Distance, SourceId = SourceId } }; } public ItemData? GetItemPrototype(ItemKey key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return ReflectionHelpers.CreateItemStack(key, 1); } public bool CanPlayerAccess(Player? player) { return true; } public int DepositPriority(ItemKey key) { if (!IsValid) { return 1; } ZDO zDO = _snapshot.ZNetView.GetZDO(); if (zDO.GetInt("Amount", 0) <= 0) { return 1; } string @string = zDO.GetString("Prefab", ""); int @int = zDO.GetInt("Quality", 1); if (!string.Equals(@string, ((ItemKey)(ref key)).PrefabName, StringComparison.Ordinal) || @int != ((ItemKey)(ref key)).Quality) { return 1; } return 0; } public bool HasCapacityFor(ItemKey key, int maxStack) { if (!IsValid) { return false; } ZDO zDO = _snapshot.ZNetView.GetZDO(); string @string = zDO.GetString("Prefab", ""); int @int = zDO.GetInt("Quality", 1); if (string.IsNullOrEmpty(@string)) { return true; } if (string.Equals(@string, ((ItemKey)(ref key)).PrefabName, StringComparison.Ordinal)) { return @int == ((ItemKey)(ref key)).Quality; } return false; } public int RemoveItems(ItemKey key, int amount) { if (!IsValid || amount <= 0) { return 0; } ZDO zDO = _snapshot.ZNetView.GetZDO(); string @string = zDO.GetString("Prefab", ""); int @int = zDO.GetInt("Quality", 1); if (!string.Equals(@string, ((ItemKey)(ref key)).PrefabName, StringComparison.Ordinal) || @int != ((ItemKey)(ref key)).Quality) { return 0; } int num = Math.Min(zDO.GetInt("Amount", 0), amount); if (num <= 0) { return 0; } ItemDrawersApi.ForceRemoveFromDrawer(_snapshot, num); return num; } public int AddItems(ItemKey key, int amount) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (!IsValid || amount <= 0) { return 0; } if (!HasCapacityFor(key, int.MaxValue)) { return 0; } ItemDrawersApi.AddToDrawer(_snapshot, ((ItemKey)(ref key)).PrefabName, amount, ((ItemKey)(ref key)).Quality); return amount; } public bool OwnsInventory(Inventory inventory) { return false; } private static string LocalizeName(string prefabName) { ObjectDB instance = ObjectDB.instance; GameObject obj = ((instance != null) ? instance.GetItemPrefab(prefabName) : null); return ((obj != null) ? obj.GetComponent() : null)?.m_itemData?.m_shared?.m_name ?? prefabName; } } public sealed class TerminalAuthorityService { private sealed class TerminalState { public string SessionId { get; set; } = string.Empty; public string TerminalUid { get; set; } = string.Empty; public Vector3 AnchorPosition { get; set; } public float Radius { get; set; } public long Revision { get; set; } public int SlotsTotalPhysical { get; set; } public int ChestCount { get; set; } public List Chests { get; set; } = new List(); public HashSet Subscribers { get; } = new HashSet(); public Dictionary PeerPlayerIds { get; } = new Dictionary(); public Dictionary Reservations { get; } = new Dictionary(StringComparer.Ordinal); public HashSet ProcessedOperations { get; } = new HashSet(StringComparer.Ordinal); public Queue OperationOrder { get; } = new Queue(); public bool SnapshotDirty { get; set; } = true; public float CachedSnapshotAt { get; set; } public SessionSnapshotDto? CachedSnapshot { get; set; } } private sealed class ReservationRecord { public string TokenId { get; set; } = string.Empty; public string SessionId { get; set; } = string.Empty; public long PeerId { get; set; } public ItemKey Key { get; set; } public int Amount { get; set; } public float ExpiresAt { get; set; } } private const float ReservationTtlSeconds = 3f; private const float SnapshotCacheSeconds = 0.2f; private const int MaxOperationHistory = 2048; private readonly object _sync = new object(); private readonly IContainerScanner _scanner; private readonly ManualLogSource _logger; private readonly Dictionary _states = new Dictionary(StringComparer.Ordinal); public event Action, SessionDeltaDto>? DeltaReady; public TerminalAuthorityService(IContainerScanner scanner, ManualLogSource logger) { _scanner = scanner; _logger = logger; } public void Tick() { //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) List<(IReadOnlyCollection, SessionDeltaDto)> list = new List<(IReadOnlyCollection, SessionDeltaDto)>(); lock (_sync) { if (_states.Count == 0) { return; } float now = Time.unscaledTime; foreach (TerminalState item in _states.Values.ToList()) { item.Subscribers.RemoveWhere((long peer) => !IsPeerConnected(peer)); foreach (long item2 in item.PeerPlayerIds.Keys.Where((long peer) => !IsPeerConnected(peer)).ToList()) { item.PeerPlayerIds.Remove(item2); } List list2 = item.Reservations.Values.Where((ReservationRecord r) => r.ExpiresAt <= now || !IsPeerConnected(r.PeerId)).ToList(); bool flag = false; foreach (ReservationRecord item3 in list2) { int num = AddToChests(item, item3.Key, item3.Amount, null); int num2 = item3.Amount - num; if (num2 > 0) { DropNearTerminal(item, item3.Key, num2); } item.Reservations.Remove(item3.TokenId); flag = flag || num > 0 || num2 > 0; } if (flag) { MarkSnapshotDirty(item); item.Revision++; SessionSnapshotDto snapshot = BuildSnapshot(item); list.Add((item.Subscribers.ToList(), BuildDelta(item, snapshot))); } if (item.Subscribers.Count == 0 && item.Reservations.Count == 0) { _states.Remove(item.TerminalUid); } } } EmitDeltas(list); } public OpenSessionResponseDto HandleOpenSession(long sender, OpenSessionRequestDto request) { lock (_sync) { if (string.IsNullOrWhiteSpace(request.TerminalUid)) { return new OpenSessionResponseDto { RequestId = request.RequestId, Success = false, Reason = "Invalid terminal" }; } TerminalState orCreateState = GetOrCreateState(request); if (!TryResolveAndBindPlayerId(orCreateState, sender, request.PlayerId, out var resolvedPlayerId)) { return new OpenSessionResponseDto { RequestId = request.RequestId, Success = false, Reason = "Player identity mismatch" }; } if (resolvedPlayerId <= 0) { return new OpenSessionResponseDto { RequestId = request.RequestId, Success = false, Reason = "Unable to resolve player identity" }; } orCreateState.Subscribers.Add(sender); SessionSnapshotDto snapshot = BuildSnapshot(orCreateState); return new OpenSessionResponseDto { RequestId = request.RequestId, Success = true, Snapshot = snapshot }; } } public ReserveWithdrawResultDto HandleReserveWithdraw(long sender, ReserveWithdrawRequestDto request) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) List<(IReadOnlyCollection, SessionDeltaDto)> list = new List<(IReadOnlyCollection, SessionDeltaDto)>(); ReserveWithdrawResultDto result; lock (_sync) { TerminalState state = GetState(request.TerminalUid); if (state == null) { return ReserveFailure(request, "Session not found", null, 0L); } if (IsDuplicateOperation(state, request.OperationId)) { return new ReserveWithdrawResultDto { RequestId = request.RequestId, Success = true, Reason = "Duplicate", Key = request.Key, Revision = state.Revision, Snapshot = BuildSnapshot(state) }; } if (request.ExpectedRevision >= 0 && request.ExpectedRevision != state.Revision) { return ReserveFailure(request, "Conflict", BuildSnapshot(state), state.Revision); } if (!TryGetMappedPlayerId(state, sender, request.PlayerId, out var playerId)) { return ReserveFailure(request, "Player identity mismatch", BuildSnapshot(state), state.Revision); } Player player = FindPlayerById(playerId); int num = RemoveFromChests(state, request.Key, request.Amount, player); if (num <= 0) { RememberOperation(state, request.OperationId); return ReserveFailure(request, "Not enough items", BuildSnapshot(state), state.Revision); } string text = Guid.NewGuid().ToString("N"); state.Reservations[text] = new ReservationRecord { TokenId = text, PeerId = sender, SessionId = (request.SessionId ?? string.Empty), Key = request.Key, Amount = num, ExpiresAt = Time.unscaledTime + 3f }; state.Revision++; MarkSnapshotDirty(state); RememberOperation(state, request.OperationId); SessionSnapshotDto snapshot = BuildSnapshot(state); list.Add((state.Subscribers.ToList(), BuildDelta(state, snapshot))); result = new ReserveWithdrawResultDto { RequestId = request.RequestId, Success = true, TokenId = text, Key = request.Key, ReservedAmount = num, Revision = state.Revision, Snapshot = snapshot }; } EmitDeltas(list); return result; } public ApplyResultDto HandleCommitReservation(long sender, CommitReservationRequestDto request) { lock (_sync) { TerminalState state = GetState(request.TerminalUid); if (state == null) { return ApplyFailure(request.RequestId, request.TokenId ?? "", "Session not found", null, 0L, "commit"); } if (IsDuplicateOperation(state, request.OperationId)) { return new ApplyResultDto { RequestId = request.RequestId, Success = true, TokenId = (request.TokenId ?? ""), Reason = "Duplicate", OperationType = "commit", Revision = state.Revision, Snapshot = BuildSnapshot(state) }; } if (!state.Reservations.TryGetValue(request.TokenId ?? "", out ReservationRecord value)) { RememberOperation(state, request.OperationId); return ApplyFailure(request.RequestId, request.TokenId ?? "", "Reservation not found", BuildSnapshot(state), state.Revision, "commit"); } if (value.PeerId != sender) { RememberOperation(state, request.OperationId); return ApplyFailure(request.RequestId, request.TokenId ?? "", "Reservation owner mismatch", BuildSnapshot(state), state.Revision, "commit"); } int amount = value.Amount; state.Reservations.Remove(value.TokenId); RememberOperation(state, request.OperationId); return new ApplyResultDto { RequestId = request.RequestId, Success = true, OperationType = "commit", TokenId = value.TokenId, AppliedAmount = amount, Revision = state.Revision, Snapshot = BuildSnapshot(state) }; } } public ApplyResultDto HandleCancelReservation(long sender, CancelReservationRequestDto request) { //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) List<(IReadOnlyCollection, SessionDeltaDto)> list = new List<(IReadOnlyCollection, SessionDeltaDto)>(); ApplyResultDto result; lock (_sync) { TerminalState state = GetState(request.TerminalUid); if (state == null) { return ApplyFailure(request.RequestId, request.TokenId ?? "", "Session not found", null, 0L, "cancel"); } if (IsDuplicateOperation(state, request.OperationId)) { return new ApplyResultDto { RequestId = request.RequestId, Success = true, TokenId = (request.TokenId ?? ""), Reason = "Duplicate", OperationType = "cancel", Revision = state.Revision, Snapshot = BuildSnapshot(state) }; } if (!state.Reservations.TryGetValue(request.TokenId ?? "", out ReservationRecord value)) { RememberOperation(state, request.OperationId); return ApplyFailure(request.RequestId, request.TokenId ?? "", "Reservation not found", BuildSnapshot(state), state.Revision, "cancel"); } if (value.PeerId != sender) { RememberOperation(state, request.OperationId); return ApplyFailure(request.RequestId, request.TokenId ?? "", "Reservation owner mismatch", BuildSnapshot(state), state.Revision, "cancel"); } int num = ((request.Amount > 0) ? Math.Min(request.Amount, value.Amount) : value.Amount); if (!TryGetMappedPlayerId(state, sender, request.PlayerId, out var playerId)) { RememberOperation(state, request.OperationId); return ApplyFailure(request.RequestId, request.TokenId ?? "", "Player identity mismatch", BuildSnapshot(state), state.Revision, "cancel"); } Player player = FindPlayerById(playerId); int num2 = AddToChests(state, value.Key, num, player); int num3 = num - num2; if (num3 > 0) { DropNearTerminal(state, value.Key, num3); } value.Amount -= num; if (value.Amount <= 0) { state.Reservations.Remove(value.TokenId); } if (num2 > 0 || num3 > 0) { MarkSnapshotDirty(state); state.Revision++; SessionSnapshotDto snapshot = BuildSnapshot(state); list.Add((state.Subscribers.ToList(), BuildDelta(state, snapshot))); RememberOperation(state, request.OperationId); result = new ApplyResultDto { RequestId = request.RequestId, Success = true, OperationType = "cancel", TokenId = (request.TokenId ?? ""), AppliedAmount = num, Revision = state.Revision, Snapshot = snapshot }; } else { RememberOperation(state, request.OperationId); result = ApplyFailure(request.RequestId, request.TokenId ?? "", "Nothing restored", BuildSnapshot(state), state.Revision, "cancel"); } } EmitDeltas(list); return result; } public ApplyResultDto HandleDeposit(long sender, DepositRequestDto request) { //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_0306: Unknown result type (might be due to invalid IL or missing references) List<(IReadOnlyCollection, SessionDeltaDto)> list = new List<(IReadOnlyCollection, SessionDeltaDto)>(); ApplyResultDto result; lock (_sync) { TerminalState state = GetState(request.TerminalUid); if (state == null) { return ApplyFailure(request.RequestId, "", "Session not found", null, 0L, "deposit"); } if (IsDuplicateOperation(state, request.OperationId)) { return new ApplyResultDto { RequestId = request.RequestId, Success = true, Reason = "Duplicate", OperationType = "deposit", Revision = state.Revision, Snapshot = BuildSnapshot(state) }; } if (request.ExpectedRevision >= 0 && request.ExpectedRevision != state.Revision) { return ApplyFailure(request.RequestId, "", "Conflict", BuildSnapshot(state), state.Revision, "deposit"); } if (!TryGetMappedPlayerId(state, sender, request.PlayerId, out var playerId)) { RememberOperation(state, request.OperationId); return ApplyFailure(request.RequestId, "", "Player identity mismatch", BuildSnapshot(state), state.Revision, "deposit"); } Player val = FindPlayerById(playerId); if ((Object)(object)val == (Object)null) { RememberOperation(state, request.OperationId); return ApplyFailure(request.RequestId, "", "Player not found", BuildSnapshot(state), state.Revision, "deposit"); } if (!HasAnyCapacityFor(state, request.Key, val)) { RememberOperation(state, request.OperationId); return ApplyFailure(request.RequestId, "", "No storage space", BuildSnapshot(state), state.Revision, "deposit"); } int num = RemoveFromTerminalInventory(FindTerminalByUid(state.TerminalUid), request.Key, request.Amount); if (num <= 0) { num = RemoveFromPlayerInventory(val, request.Key, request.Amount); } if (num <= 0) { RememberOperation(state, request.OperationId); return ApplyFailure(request.RequestId, "", "Player/terminal has no matching item", BuildSnapshot(state), state.Revision, "deposit"); } int num2 = AddToChests(state, request.Key, num, val); if (num2 <= 0) { RememberOperation(state, request.OperationId); result = new ApplyResultDto { RequestId = request.RequestId, Success = false, Reason = "No storage space", OperationType = "deposit", AppliedAmount = 0, Revision = state.Revision, Snapshot = BuildSnapshot(state) }; } else { int num3 = num - num2; if (num3 > 0) { int num4 = AddToPlayerInventory(val, request.Key, num3); int num5 = num3 - num4; if (num5 > 0) { DropNearTerminal(state, request.Key, num5); } } MarkSnapshotDirty(state); state.Revision++; RememberOperation(state, request.OperationId); SessionSnapshotDto snapshot = BuildSnapshot(state); list.Add((state.Subscribers.ToList(), BuildDelta(state, snapshot))); result = new ApplyResultDto { RequestId = request.RequestId, Success = true, Reason = "", OperationType = "deposit", AppliedAmount = num2, Revision = state.Revision, Snapshot = snapshot }; } } EmitDeltas(list); return result; } public void HandleCloseSession(long sender, CloseSessionRequestDto request) { //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) List<(IReadOnlyCollection, SessionDeltaDto)> list = new List<(IReadOnlyCollection, SessionDeltaDto)>(); lock (_sync) { TerminalState state = GetState(request.TerminalUid); if (state == null) { return; } state.PeerPlayerIds.TryGetValue(sender, out var value); state.Subscribers.Remove(sender); state.PeerPlayerIds.Remove(sender); List list2 = state.Reservations.Values.Where((ReservationRecord r) => r.PeerId == sender).ToList(); Player player = FindPlayerById((value > 0) ? value : request.PlayerId); bool flag = false; foreach (ReservationRecord item in list2) { int num = AddToChests(state, item.Key, item.Amount, player); int num2 = item.Amount - num; if (num2 > 0) { DropNearTerminal(state, item.Key, num2); } state.Reservations.Remove(item.TokenId); flag = flag || num > 0 || num2 > 0; } if (flag) { MarkSnapshotDirty(state); state.Revision++; SessionSnapshotDto snapshot = BuildSnapshot(state); list.Add((state.Subscribers.ToList(), BuildDelta(state, snapshot))); } if (state.Subscribers.Count == 0 && state.Reservations.Count == 0) { _states.Remove(state.TerminalUid); } } EmitDeltas(list); } private SessionSnapshotDto BuildSnapshot(TerminalState state) { //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) float unscaledTime = Time.unscaledTime; if (!state.SnapshotDirty && state.CachedSnapshot != null && unscaledTime - state.CachedSnapshotAt <= 0.2f) { return state.CachedSnapshot; } RefreshChestHandles(state); List list = new List(); Dictionary prototypes = new Dictionary(); int num = 0; foreach (ChestHandle chest in state.Chests) { if (!chest.Source.IsValid) { continue; } num += chest.Source.PhysicalSlotCount; foreach (SourceStack item in chest.Source.ReadStacks()) { if (!prototypes.ContainsKey(item.Key)) { ItemData itemPrototype = chest.Source.GetItemPrototype(item.Key); if (itemPrototype != null) { prototypes[item.Key] = itemPrototype; } } list.Add(item); } } List items = (from x in (from x in AggregationService.Aggregate((IReadOnlyList)list, (Func)ReflectionHelpers.ResolveMaxStackSize).Select(delegate(AggregatedItem a) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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) //IL_0038: Expected I4, but got O //IL_0038: Expected O, but got I4 //IL_0028->IL0028: Incompatible stack types: O vs I4 //IL_001d->IL0028: Incompatible stack types: I4 vs O //IL_001d->IL0028: Incompatible stack types: O vs I4 object obj = a; int num3; if (prototypes.TryGetValue(a.Key, out ItemData value)) { obj = value.m_shared.m_itemType; num3 = (int)obj; } else { num3 = int.MaxValue; obj = num3; num3 = (int)obj; } return new { Item = (AggregatedItem)(object)num3, TypeOrder = (int)obj, SubgroupOrder = ReflectionHelpers.GetSubgroupOrder(a.Key) }; }) orderby x.TypeOrder, x.SubgroupOrder select x).ThenBy(x => x.Item.DisplayName, StringComparer.OrdinalIgnoreCase).ThenByDescending(x => x.Item.TotalAmount) select x.Item).ToList(); state.ChestCount = state.Chests.Count; state.SlotsTotalPhysical = num; int num2 = 0; foreach (ChestHandle chest2 in state.Chests) { if (chest2.Source.IsValid) { num2 += chest2.Source.DisplaySlotsUsed; } } SessionSnapshotDto result = (state.CachedSnapshot = new SessionSnapshotDto { SessionId = state.SessionId, TerminalUid = state.TerminalUid, Revision = state.Revision, SlotsTotalPhysical = num, SlotsUsedVirtual = num2, ChestCount = state.ChestCount, Items = items }); state.CachedSnapshotAt = unscaledTime; state.SnapshotDirty = false; return result; } private void RefreshChestHandles(TerminalState state) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) Container val = FindTerminalByUid(state.TerminalUid); if ((Object)(object)val != (Object)null) { state.AnchorPosition = ((Component)val).transform.position; } state.Chests = _scanner.GetNearbyContainers(state.AnchorPosition, state.Radius, val).ToList(); } private static int RemoveFromChests(TerminalState state, ItemKey key, int amount, Player? player) { //IL_0091: Unknown result type (might be due to invalid IL or missing references) if (amount <= 0) { return 0; } int num = amount; foreach (ChestHandle item in state.Chests.OrderBy((ChestHandle c) => c.Distance).ThenBy((ChestHandle c) => c.SourceId, StringComparer.Ordinal)) { if (num <= 0) { break; } if (item.Source.IsValid && item.Source.CanPlayerAccess(player)) { int num2 = item.Source.RemoveItems(key, num); num -= num2; } } return amount - num; } private static int AddToChests(TerminalState state, ItemKey key, int amount, Player? player) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) if (amount <= 0) { return 0; } int num = amount; foreach (ChestHandle item in (from c in state.Chests orderby c.Source.DepositPriority(key), c.Distance select c).ThenBy((ChestHandle c) => c.SourceId, StringComparer.Ordinal)) { if (num <= 0) { break; } if (item.Source.IsValid && item.Source.CanPlayerAccess(player)) { int num2 = item.Source.AddItems(key, num); num -= num2; } } return amount - num; } private static int RemoveFromPlayerInventory(Player player, ItemKey key, int amount) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (amount <= 0) { return 0; } int num = amount; Inventory inventory = ((Humanoid)player).GetInventory(); foreach (ItemData item in (from i in inventory.GetAllItems() where ReflectionHelpers.MatchKey(i, key) select i).ToList()) { if (num <= 0) { break; } int num2 = Math.Min(item.m_stack, num); inventory.RemoveItem(item, num2); num -= num2; } return amount - num; } private static int RemoveFromTerminalInventory(Container? terminal, ItemKey key, int amount) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)terminal == (Object)null || amount <= 0) { return 0; } Inventory inventory = terminal.GetInventory(); if (inventory == null) { return 0; } int num = amount; foreach (ItemData item in (from i in inventory.GetAllItems() where ReflectionHelpers.MatchKey(i, key) select i).ToList()) { if (num <= 0) { break; } int num2 = Math.Min(item.m_stack, num); inventory.RemoveItem(item, num2); num -= num2; } return amount - num; } private int AddToPlayerInventory(Player player, ItemKey key, int amount) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (amount <= 0) { return 0; } int num = ReflectionHelpers.GetMaxStackSize(key); if (num <= 0) { num = 1; } Inventory inventory = ((Humanoid)player).GetInventory(); return ChunkedTransfer.Move(amount, num, (Func)delegate(int chunkAmount) { //IL_0001: 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) ItemData val = ReflectionHelpers.CreateItemStack(key, chunkAmount); return (val != null) ? ReflectionHelpers.TryAddItemMeasured(inventory, key, val, chunkAmount) : 0; }); } private void DropNearTerminal(TerminalState state, ItemKey key, int amount) { //IL_0000: 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_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) int num = ReflectionHelpers.GetMaxStackSize(key); if (num <= 0) { num = 1; } int num2 = amount; while (num2 > 0) { int num3 = Math.Min(num, num2); ItemData val = ReflectionHelpers.CreateItemStack(key, num3); if (val != null && ReflectionHelpers.TryDropItem(val, num3, state.AnchorPosition + Vector3.up, Quaternion.identity)) { num2 -= num3; continue; } break; } } private int AddToTerminalInventory(Container terminal, ItemKey key, int amount) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (amount <= 0) { return 0; } int num = ReflectionHelpers.GetMaxStackSize(key); if (num <= 0) { num = 1; } Inventory inventory = terminal.GetInventory(); if (inventory == null) { return 0; } return ChunkedTransfer.Move(amount, num, (Func)delegate(int chunkAmount) { //IL_0001: 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) ItemData val = ReflectionHelpers.CreateItemStack(key, chunkAmount); return (val != null) ? ReflectionHelpers.TryAddItemMeasured(inventory, key, val, chunkAmount) : 0; }); } private static bool HasAnyCapacityFor(TerminalState state, ItemKey key, Player? player) { //IL_0000: 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) int num = ReflectionHelpers.GetMaxStackSize(key); if (num <= 0) { num = 1; } foreach (ChestHandle chest in state.Chests) { if (chest.Source.IsValid && chest.Source.CanPlayerAccess(player) && chest.Source.HasCapacityFor(key, num)) { return true; } } return false; } private static Container? FindTerminalByUid(string terminalUid) { if (string.IsNullOrWhiteSpace(terminalUid)) { return null; } Container[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Container val in array) { if (!((Object)(object)val == (Object)null) && UnifiedTerminal.IsTerminal(val) && string.Equals(ReflectionHelpers.BuildContainerUid(val), terminalUid, StringComparison.Ordinal)) { return val; } } return null; } private static void MarkSnapshotDirty(TerminalState state) { state.SnapshotDirty = true; state.CachedSnapshot = null; } private TerminalState GetOrCreateState(OpenSessionRequestDto request) { //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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_0035: 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_005d: Unknown result type (might be due to invalid IL or missing references) if (_states.TryGetValue(request.TerminalUid, out TerminalState value)) { Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(request.AnchorX, request.AnchorY, request.AnchorZ); Vector3 val2 = value.AnchorPosition - val; if (((Vector3)(ref val2)).sqrMagnitude > 0.0001f || !Mathf.Approximately(value.Radius, request.Radius)) { value.AnchorPosition = val; value.Radius = request.Radius; MarkSnapshotDirty(value); } return value; } TerminalState terminalState = new TerminalState { TerminalUid = request.TerminalUid, SessionId = Guid.NewGuid().ToString("N"), AnchorPosition = new Vector3(request.AnchorX, request.AnchorY, request.AnchorZ), Radius = request.Radius }; _states[request.TerminalUid] = terminalState; return terminalState; } private TerminalState? GetState(string terminalUid) { if (string.IsNullOrWhiteSpace(terminalUid) || !_states.TryGetValue(terminalUid, out TerminalState value)) { return null; } return value; } private static ReserveWithdrawResultDto ReserveFailure(ReserveWithdrawRequestDto req, string reason, SessionSnapshotDto? snapshot = null, long revision = 0L) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) return new ReserveWithdrawResultDto { RequestId = req.RequestId, Success = false, Reason = reason, Key = req.Key, Revision = revision, Snapshot = (snapshot ?? new SessionSnapshotDto()) }; } private static ApplyResultDto ApplyFailure(string reqId, string tokenId, string reason, SessionSnapshotDto? snapshot = null, long revision = 0L, string operationType = "") { return new ApplyResultDto { RequestId = reqId, Success = false, Reason = reason, OperationType = operationType, TokenId = tokenId, Revision = revision, Snapshot = (snapshot ?? new SessionSnapshotDto()) }; } private static bool TryResolveAndBindPlayerId(TerminalState state, long sender, long requestedPlayerId, out long resolvedPlayerId) { resolvedPlayerId = 0L; long num = ResolvePlayerIdFromPeer(sender); if (num > 0) { if (requestedPlayerId > 0 && requestedPlayerId != num) { return false; } state.PeerPlayerIds[sender] = num; resolvedPlayerId = num; return true; } if (state.PeerPlayerIds.TryGetValue(sender, out var value)) { if (requestedPlayerId > 0 && requestedPlayerId != value) { return false; } resolvedPlayerId = value; return true; } if (requestedPlayerId > 0 && sender <= 0) { state.PeerPlayerIds[sender] = requestedPlayerId; resolvedPlayerId = requestedPlayerId; return true; } return false; } private static bool TryGetMappedPlayerId(TerminalState state, long sender, long requestedPlayerId, out long playerId) { playerId = 0L; if (!state.PeerPlayerIds.TryGetValue(sender, out var value)) { return false; } if (requestedPlayerId > 0 && requestedPlayerId != value) { return false; } playerId = value; return true; } private static long ResolvePlayerIdFromPeer(long sender) { if (sender <= 0) { Player localPlayer = Player.m_localPlayer; if (localPlayer == null) { return 0L; } return localPlayer.GetPlayerID(); } foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer == (Object)null) { continue; } ZNetView component = ((Component)allPlayer).GetComponent(); ZDO val = ((component != null) ? component.GetZDO() : null); if (val != null) { Type type = ((object)val).GetType(); MethodInfo method = type.GetMethod("GetOwner", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null && method.Invoke(val, null) is long num && num == sender) { return allPlayer.GetPlayerID(); } FieldInfo field = type.GetField("m_owner", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.GetValue(val) is long num2 && num2 == sender) { return allPlayer.GetPlayerID(); } } } return 0L; } private static Player? FindPlayerById(long playerId) { if (playerId <= 0) { return Player.m_localPlayer; } foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer != (Object)null && allPlayer.GetPlayerID() == playerId) { return allPlayer; } } if ((Object)(object)Player.m_localPlayer != (Object)null && Player.m_localPlayer.GetPlayerID() == playerId) { return Player.m_localPlayer; } return null; } private static bool IsPeerConnected(long peerId) { if (peerId <= 0 || (Object)(object)ZNet.instance == (Object)null) { return true; } MethodInfo method = typeof(ZNet).GetMethod("GetPeer", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(long) }, null); if (method != null && method.Invoke(ZNet.instance, new object[1] { peerId }) != null) { return true; } if (typeof(ZNet).GetField("m_peers", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(ZNet.instance) is IEnumerable enumerable) { foreach (object item in enumerable) { if (item != null && item.GetType().GetField("m_uid", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(item) is long num && num == peerId) { return true; } } } return false; } private static bool IsDuplicateOperation(TerminalState state, string operationId) { if (!string.IsNullOrWhiteSpace(operationId)) { return state.ProcessedOperations.Contains(operationId); } return false; } private static void RememberOperation(TerminalState state, string operationId) { if (!string.IsNullOrWhiteSpace(operationId)) { if (state.ProcessedOperations.Add(operationId)) { state.OperationOrder.Enqueue(operationId); } while (state.OperationOrder.Count > 2048) { string item = state.OperationOrder.Dequeue(); state.ProcessedOperations.Remove(item); } } } private static SessionDeltaDto BuildDelta(TerminalState state, SessionSnapshotDto snapshot) { return new SessionDeltaDto { SessionId = state.SessionId, TerminalUid = state.TerminalUid, Revision = state.Revision, Snapshot = snapshot }; } private void EmitDeltas(IEnumerable<(IReadOnlyCollection Peers, SessionDeltaDto Delta)> deltas) { foreach (var delta in deltas) { try { this.DeltaReady?.Invoke(delta.Peers, delta.Delta); } catch (Exception arg) { _logger.LogError((object)$"Failed to emit session delta: {arg}"); } } } } } namespace UnifiedStorage.Mod.Pieces { public sealed class UnifiedTerminal : MonoBehaviour { public const string TerminalPrefabName = "piece_unified_chest_terminal"; private bool _tintApplied; private bool _tintEnabled = true; private Color _tintColor = new Color(0.431f, 0.659f, 0.29f, 1f); private float _tintStrength = 0.55f; public static bool IsTerminal(Container? container) { if ((Object)(object)container != (Object)null) { return (Object)(object)((Component)container).GetComponent() != (Object)null; } return false; } public static bool IsTerminal(GameObject? go) { if ((Object)(object)go != (Object)null) { return (Object)(object)go.GetComponent() != (Object)null; } return false; } public void ConfigureVisuals(bool tintEnabled, string tintColorHex, float tintStrength) { //IL_0026: 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) _tintEnabled = tintEnabled; _tintStrength = Mathf.Clamp01(tintStrength); Color tintColor = default(Color); if (!string.IsNullOrWhiteSpace(tintColorHex) && ColorUtility.TryParseHtmlString(tintColorHex, ref tintColor)) { _tintColor = tintColor; } } public Container? GetContainer() { return ((Component)this).GetComponent(); } private void Awake() { ApplyTintIfNeeded(); } private void ApplyTintIfNeeded() { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) if (!_tintEnabled || _tintApplied || _tintStrength <= 0f) { return; } float num = Mathf.Clamp01(_tintStrength * 1.7f); float num2 = 0.16f * num; _tintApplied = true; Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if ((Object)(object)val == (Object)null || ((object)val).GetType().Name == "ParticleSystemRenderer") { continue; } Material[] materials = val.materials; foreach (Material val2 in materials) { if (!((Object)(object)val2 == (Object)null)) { if (val2.HasProperty("_Color")) { Color color = val2.GetColor("_Color"); val2.SetColor("_Color", Color.Lerp(color, _tintColor, num)); } if (val2.HasProperty("_EmissionColor")) { Color color2 = val2.GetColor("_EmissionColor"); val2.SetColor("_EmissionColor", color2 + _tintColor * num2); val2.EnableKeyword("_EMISSION"); } } } } } } public sealed class UnifiedTerminalRegistrar { private readonly StorageConfig _config; private readonly ManualLogSource _logger; private bool _registered; public UnifiedTerminalRegistrar(StorageConfig config, ManualLogSource logger) { _config = config; _logger = logger; } public void RegisterPiece() { //IL_003a: 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_0050: 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) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Expected O, but got Unknown if (_registered || !_config.TerminalPieceEnabled.Value) { return; } string text = ResolveBaseChestPrefabName(); if (string.IsNullOrWhiteSpace(text)) { _logger.LogError((object)"Unified Terminal: could not resolve a vanilla chest prefab to clone."); return; } PieceConfig val = new PieceConfig { Name = _config.TerminalDisplayName, PieceTable = "_HammerPieceTable", Category = "Furniture", CraftingStation = "piece_workbench" }; val.AddRequirement("Wood", 10, true); CustomPiece val2 = new CustomPiece("piece_unified_chest_terminal", text, val); if (!val2.IsValid()) { _logger.LogError((object)("Unified Terminal: failed to create CustomPiece from base '" + text + "'.")); return; } GameObject piecePrefab = val2.PiecePrefab; if ((Object)(object)piecePrefab == (Object)null) { _logger.LogError((object)"Unified Terminal: PiecePrefab is null after CustomPiece creation."); return; } ((Object)piecePrefab).name = "piece_unified_chest_terminal"; UnifiedTerminal unifiedTerminal = piecePrefab.GetComponent(); if ((Object)(object)unifiedTerminal == (Object)null) { unifiedTerminal = piecePrefab.AddComponent(); } unifiedTerminal.ConfigureVisuals(_config.TerminalTintEnabled, _config.TerminalTintColor, _config.TerminalTintStrength); Piece component = piecePrefab.GetComponent(); if ((Object)(object)component != (Object)null) { component.m_name = _config.TerminalDisplayName; component.m_description = "Access nearby chests in a unified view."; } Container component2 = piecePrefab.GetComponent(); if ((Object)(object)component2 != (Object)null) { ReflectionHelpers.SetContainerDisplayName(component2, _config.TerminalDisplayName); } PieceManager.Instance.AddPiece(val2); _registered = true; _logger.LogInfo((object)("Unified Terminal piece registered (base: " + text + ").")); } private static string? ResolveBaseChestPrefabName() { string[] array = new string[4] { "piece_chest", "piece_chest_wood", "piece_chest_private", "piece_chest_blackmetal" }; foreach (string text in array) { if ((Object)(object)PrefabManager.Instance.GetPrefab(text) != (Object)null) { return text; } } return null; } } } namespace UnifiedStorage.Mod.Patches { [HarmonyPatch(typeof(Chat), "HasFocus")] public static class ChatHasFocusPatch { private static void Postfix(ref bool __result) { if (UnifiedStoragePlugin.ShouldBlockGameInput()) { __result = true; } } } [HarmonyPatch(typeof(Container), "GetHoverText")] public static class ContainerHoverTextPatch { private static int _cachedCount; private static float _cacheExpiry; private static int _cachedInstanceId; private static bool Prefix(Container __instance, ref string __result) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (!UnifiedTerminal.IsTerminal(__instance)) { return true; } int instanceID = ((Object)__instance).GetInstanceID(); float unscaledTime = Time.unscaledTime; if (instanceID != _cachedInstanceId || unscaledTime >= _cacheExpiry) { _cachedCount = UnifiedStoragePlugin.Instance?.GetNearbyChestCount(((Component)__instance).transform.position) ?? 0; _cacheExpiry = unscaledTime + 2f; _cachedInstanceId = instanceID; } string arg = ((_cachedCount == 1) ? "chest" : "chests"); __result = Localization.instance.Localize($"Unified Storage Terminal\n[$KEY_Use] Open ({_cachedCount} {arg} in range)"); return false; } } [HarmonyPatch(typeof(Container), "Interact")] public static class ContainerInteractPatch { private static bool Prefix(Container __instance, Humanoid character, bool hold, ref bool __result) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) if (!hold && UnifiedTerminal.IsTerminal(__instance)) { Player val = (Player)(object)((character is Player) ? character : null); if (val != null) { UnifiedStoragePlugin instance = UnifiedStoragePlugin.Instance; if ((Object)(object)instance != (Object)null && instance.GetNearbyChestCount(((Component)__instance).transform.position) == 0) { ((Character)val).Message((MessageType)2, "No chests in range.", 0, (Sprite)null); __result = false; return false; } return true; } } return true; } private static void Postfix(Container __instance, Humanoid character, bool hold, bool __result) { if (!(!__result || hold)) { UnifiedStoragePlugin.Instance?.TryHandleChestInteract(__instance, character); } } } [HarmonyPatch(typeof(ZInput), "GetButton", new Type[] { typeof(string) })] public static class ZInputGetButtonPatch { private static bool Prefix(ref bool __result) { if (!UnifiedStoragePlugin.ShouldBlockGameInput()) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(ZInput), "GetButtonDown", new Type[] { typeof(string) })] public static class ZInputGetButtonDownPatch { private static bool Prefix(ref bool __result) { if (!UnifiedStoragePlugin.ShouldBlockGameInput()) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(ZInput), "GetButtonUp", new Type[] { typeof(string) })] public static class ZInputGetButtonUpPatch { private static bool Prefix(ref bool __result) { if (!UnifiedStoragePlugin.ShouldBlockGameInput()) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(ZInput), "GetKey", new Type[] { typeof(KeyCode), typeof(bool) })] public static class ZInputGetKeyPatch { private static bool Prefix(ref bool __result) { if (!UnifiedStoragePlugin.ShouldBlockGameInput()) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(ZInput), "GetKeyDown", new Type[] { typeof(KeyCode), typeof(bool) })] public static class ZInputGetKeyDownPatch { private static bool Prefix(ref bool __result) { if (!UnifiedStoragePlugin.ShouldBlockGameInput()) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(ZInput), "GetKeyUp", new Type[] { typeof(KeyCode), typeof(bool) })] public static class ZInputGetKeyUpPatch { private static bool Prefix(ref bool __result) { if (!UnifiedStoragePlugin.ShouldBlockGameInput()) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })] public static class InventoryAddItemPatch { private static bool Prefix(Inventory __instance, ItemData item, ref bool __result) { if ((Object)(object)UnifiedStoragePlugin.Instance == (Object)null) { return true; } if (!UnifiedStoragePlugin.Instance.ShouldBlockDeposit(__instance, item)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData), typeof(int), typeof(int), typeof(int) })] public static class InventoryAddItemAtPositionPatch { private static bool Prefix(Inventory __instance, ItemData item, ref bool __result) { if ((Object)(object)UnifiedStoragePlugin.Instance == (Object)null) { return true; } if (!UnifiedStoragePlugin.Instance.ShouldBlockDeposit(__instance, item)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Inventory), "MoveItemToThis", new Type[] { typeof(Inventory), typeof(ItemData), typeof(int), typeof(int), typeof(int) })] public static class InventoryMoveItemToThisPatch { private static bool Prefix(Inventory __instance, ItemData item, ref bool __result) { if ((Object)(object)UnifiedStoragePlugin.Instance == (Object)null) { return true; } if (!UnifiedStoragePlugin.Instance.ShouldBlockDeposit(__instance, item)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Inventory), "Changed")] public static class InventoryChangedPatch { private static void Postfix(Inventory __instance) { UnifiedStoragePlugin.Instance?.OnTrackedInventoryChanged(__instance); } } [HarmonyPatch(typeof(InventoryGui), "Hide")] public static class InventoryGuiHidePatch { private static void Prefix() { UnifiedStoragePlugin.Instance?.OnInventoryGuiClosing(); } } [HarmonyPatch(typeof(InventoryGui), "Awake")] public static class InventoryGuiAwakePatch { private static void Postfix(InventoryGui __instance) { UnifiedStoragePlugin.Instance?.OnInventoryGuiAwake(__instance); } } [HarmonyPatch(typeof(InventoryGui), "Update")] public static class InventoryGuiUpdatePatch { private static void Postfix(InventoryGui __instance) { UnifiedStoragePlugin.Instance?.OnInventoryGuiUpdate(__instance); } } [HarmonyPatch(typeof(InventoryGrid), "OnLeftClick")] public static class InventoryGridOnLeftClickPatch { private static void Postfix(InventoryGrid __instance) { UnifiedStoragePlugin.Instance?.OnInventoryGridInteraction(__instance); } } [HarmonyPatch(typeof(InventoryGrid), "OnRightClick")] public static class InventoryGridOnRightClickPatch { private static void Postfix(InventoryGrid __instance) { UnifiedStoragePlugin.Instance?.OnInventoryGridInteraction(__instance); } } [HarmonyPatch(typeof(InventoryGui), "OnTakeAll")] public static class InventoryGuiOnTakeAllPatch { private static bool Prefix() { UnifiedStoragePlugin? instance = UnifiedStoragePlugin.Instance; if (instance == null) { return true; } return !instance.IsUnifiedSessionActive(); } private static void Postfix() { UnifiedStoragePlugin.Instance?.OnContainerInteraction(); } } [HarmonyPatch(typeof(InventoryGui), "OnStackAll")] public static class InventoryGuiOnStackAllPatch { private static void Postfix() { UnifiedStoragePlugin.Instance?.OnContainerInteraction(); } } [HarmonyPatch(typeof(InventoryGui), "OnDropOutside")] public static class InventoryGuiOnDropOutsidePatch { private static void Postfix() { UnifiedStoragePlugin.Instance?.OnContainerInteraction(); } } } namespace UnifiedStorage.Mod.Network { public static class TerminalCodec { public static string EncodeOpenSessionRequest(OpenSessionRequestDto dto) { OpenSessionRequestDto dto2 = dto; return WritePayload(delegate(ZPackage pkg) { pkg.Write(dto2.RequestId ?? string.Empty); pkg.Write(dto2.SessionId ?? string.Empty); pkg.Write(dto2.TerminalUid ?? string.Empty); pkg.Write(dto2.PlayerId); pkg.Write(dto2.AnchorX); pkg.Write(dto2.AnchorY); pkg.Write(dto2.AnchorZ); pkg.Write(dto2.Radius); }); } public static OpenSessionRequestDto DecodeOpenSessionRequest(string payload) { OpenSessionRequestDto dto = new OpenSessionRequestDto(); ReadPayload(payload, delegate(ZPackage pkg) { dto.RequestId = pkg.ReadString(); dto.SessionId = pkg.ReadString(); dto.TerminalUid = pkg.ReadString(); dto.PlayerId = pkg.ReadLong(); dto.AnchorX = pkg.ReadSingle(); dto.AnchorY = pkg.ReadSingle(); dto.AnchorZ = pkg.ReadSingle(); dto.Radius = pkg.ReadSingle(); }); return dto; } public static string EncodeOpenSessionResponse(OpenSessionResponseDto dto) { OpenSessionResponseDto dto2 = dto; return WritePayload(delegate(ZPackage pkg) { pkg.Write(dto2.RequestId ?? string.Empty); pkg.Write(dto2.Success); pkg.Write(dto2.Reason ?? string.Empty); WriteSnapshot(pkg, dto2.Snapshot); }); } public static OpenSessionResponseDto DecodeOpenSessionResponse(string payload) { OpenSessionResponseDto dto = new OpenSessionResponseDto(); ReadPayload(payload, delegate(ZPackage pkg) { dto.RequestId = pkg.ReadString(); dto.Success = pkg.ReadBool(); dto.Reason = pkg.ReadString(); dto.Snapshot = ReadSnapshot(pkg); }); return dto; } public static string EncodeReserveWithdrawRequest(ReserveWithdrawRequestDto dto) { ReserveWithdrawRequestDto dto2 = dto; return WritePayload(delegate(ZPackage pkg) { //IL_0091: Unknown result type (might be due to invalid IL or missing references) pkg.Write(dto2.RequestId ?? string.Empty); pkg.Write(dto2.SessionId ?? string.Empty); pkg.Write(dto2.OperationId ?? string.Empty); pkg.Write(dto2.TerminalUid ?? string.Empty); pkg.Write(dto2.PlayerId); pkg.Write(dto2.ExpectedRevision); WriteItemKey(pkg, dto2.Key); pkg.Write(dto2.Amount); }); } public static ReserveWithdrawRequestDto DecodeReserveWithdrawRequest(string payload) { ReserveWithdrawRequestDto dto = new ReserveWithdrawRequestDto(); ReadPayload(payload, delegate(ZPackage pkg) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) dto.RequestId = pkg.ReadString(); dto.SessionId = pkg.ReadString(); dto.OperationId = pkg.ReadString(); dto.TerminalUid = pkg.ReadString(); dto.PlayerId = pkg.ReadLong(); dto.ExpectedRevision = pkg.ReadLong(); dto.Key = ReadItemKey(pkg); dto.Amount = pkg.ReadInt(); }); return dto; } public static string EncodeReserveWithdrawResult(ReserveWithdrawResultDto dto) { ReserveWithdrawResultDto dto2 = dto; return WritePayload(delegate(ZPackage pkg) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) pkg.Write(dto2.RequestId ?? string.Empty); pkg.Write(dto2.Success); pkg.Write(dto2.Reason ?? string.Empty); pkg.Write(dto2.TokenId ?? string.Empty); WriteItemKey(pkg, dto2.Key); pkg.Write(dto2.ReservedAmount); pkg.Write(dto2.Revision); WriteSnapshot(pkg, dto2.Snapshot); }); } public static ReserveWithdrawResultDto DecodeReserveWithdrawResult(string payload) { ReserveWithdrawResultDto dto = new ReserveWithdrawResultDto(); ReadPayload(payload, delegate(ZPackage pkg) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) dto.RequestId = pkg.ReadString(); dto.Success = pkg.ReadBool(); dto.Reason = pkg.ReadString(); dto.TokenId = pkg.ReadString(); dto.Key = ReadItemKey(pkg); dto.ReservedAmount = pkg.ReadInt(); dto.Revision = pkg.ReadLong(); dto.Snapshot = ReadSnapshot(pkg); }); return dto; } public static string EncodeCommitReservationRequest(CommitReservationRequestDto dto) { CommitReservationRequestDto dto2 = dto; return WritePayload(delegate(ZPackage pkg) { pkg.Write(dto2.RequestId ?? string.Empty); pkg.Write(dto2.SessionId ?? string.Empty); pkg.Write(dto2.OperationId ?? string.Empty); pkg.Write(dto2.TerminalUid ?? string.Empty); pkg.Write(dto2.TokenId ?? string.Empty); }); } public static CommitReservationRequestDto DecodeCommitReservationRequest(string payload) { CommitReservationRequestDto dto = new CommitReservationRequestDto(); ReadPayload(payload, delegate(ZPackage pkg) { dto.RequestId = pkg.ReadString(); dto.SessionId = pkg.ReadString(); dto.OperationId = pkg.ReadString(); dto.TerminalUid = pkg.ReadString(); dto.TokenId = pkg.ReadString(); }); return dto; } public static string EncodeCancelReservationRequest(CancelReservationRequestDto dto) { CancelReservationRequestDto dto2 = dto; return WritePayload(delegate(ZPackage pkg) { pkg.Write(dto2.RequestId ?? string.Empty); pkg.Write(dto2.SessionId ?? string.Empty); pkg.Write(dto2.OperationId ?? string.Empty); pkg.Write(dto2.TerminalUid ?? string.Empty); pkg.Write(dto2.PlayerId); pkg.Write(dto2.TokenId ?? string.Empty); pkg.Write(dto2.Amount); }); } public static CancelReservationRequestDto DecodeCancelReservationRequest(string payload) { CancelReservationRequestDto dto = new CancelReservationRequestDto(); ReadPayload(payload, delegate(ZPackage pkg) { dto.RequestId = pkg.ReadString(); dto.SessionId = pkg.ReadString(); dto.OperationId = pkg.ReadString(); dto.TerminalUid = pkg.ReadString(); dto.PlayerId = pkg.ReadLong(); dto.TokenId = pkg.ReadString(); dto.Amount = pkg.ReadInt(); }); return dto; } public static string EncodeDepositRequest(DepositRequestDto dto) { DepositRequestDto dto2 = dto; return WritePayload(delegate(ZPackage pkg) { //IL_0091: Unknown result type (might be due to invalid IL or missing references) pkg.Write(dto2.RequestId ?? string.Empty); pkg.Write(dto2.SessionId ?? string.Empty); pkg.Write(dto2.OperationId ?? string.Empty); pkg.Write(dto2.TerminalUid ?? string.Empty); pkg.Write(dto2.PlayerId); pkg.Write(dto2.ExpectedRevision); WriteItemKey(pkg, dto2.Key); pkg.Write(dto2.Amount); }); } public static DepositRequestDto DecodeDepositRequest(string payload) { DepositRequestDto dto = new DepositRequestDto(); ReadPayload(payload, delegate(ZPackage pkg) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) dto.RequestId = pkg.ReadString(); dto.SessionId = pkg.ReadString(); dto.OperationId = pkg.ReadString(); dto.TerminalUid = pkg.ReadString(); dto.PlayerId = pkg.ReadLong(); dto.ExpectedRevision = pkg.ReadLong(); dto.Key = ReadItemKey(pkg); dto.Amount = pkg.ReadInt(); }); return dto; } public static string EncodeApplyResult(ApplyResultDto dto) { ApplyResultDto dto2 = dto; return WritePayload(delegate(ZPackage pkg) { pkg.Write(dto2.RequestId ?? string.Empty); pkg.Write(dto2.Success); pkg.Write(dto2.Reason ?? string.Empty); pkg.Write(dto2.OperationType ?? string.Empty); pkg.Write(dto2.TokenId ?? string.Empty); pkg.Write(dto2.AppliedAmount); pkg.Write(dto2.Revision); WriteSnapshot(pkg, dto2.Snapshot); }); } public static ApplyResultDto DecodeApplyResult(string payload) { ApplyResultDto dto = new ApplyResultDto(); ReadPayload(payload, delegate(ZPackage pkg) { dto.RequestId = pkg.ReadString(); dto.Success = pkg.ReadBool(); dto.Reason = pkg.ReadString(); dto.OperationType = pkg.ReadString(); dto.TokenId = pkg.ReadString(); dto.AppliedAmount = pkg.ReadInt(); dto.Revision = pkg.ReadLong(); dto.Snapshot = ReadSnapshot(pkg); }); return dto; } public static string EncodeCloseSessionRequest(CloseSessionRequestDto dto) { CloseSessionRequestDto dto2 = dto; return WritePayload(delegate(ZPackage pkg) { pkg.Write(dto2.RequestId ?? string.Empty); pkg.Write(dto2.SessionId ?? string.Empty); pkg.Write(dto2.TerminalUid ?? string.Empty); pkg.Write(dto2.PlayerId); }); } public static CloseSessionRequestDto DecodeCloseSessionRequest(string payload) { CloseSessionRequestDto dto = new CloseSessionRequestDto(); ReadPayload(payload, delegate(ZPackage pkg) { dto.RequestId = pkg.ReadString(); dto.SessionId = pkg.ReadString(); dto.TerminalUid = pkg.ReadString(); dto.PlayerId = pkg.ReadLong(); }); return dto; } public static string EncodeSessionDelta(SessionDeltaDto dto) { SessionDeltaDto dto2 = dto; return WritePayload(delegate(ZPackage pkg) { pkg.Write(dto2.SessionId ?? string.Empty); pkg.Write(dto2.TerminalUid ?? string.Empty); pkg.Write(dto2.Revision); WriteSnapshot(pkg, dto2.Snapshot); }); } public static SessionDeltaDto DecodeSessionDelta(string payload) { SessionDeltaDto dto = new SessionDeltaDto(); ReadPayload(payload, delegate(ZPackage pkg) { dto.SessionId = pkg.ReadString(); dto.TerminalUid = pkg.ReadString(); dto.Revision = pkg.ReadLong(); dto.Snapshot = ReadSnapshot(pkg); }); return dto; } private static string WritePayload(Action writer) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); writer(val); return Convert.ToBase64String(val.GetArray()); } private static void ReadPayload(string payload, Action reader) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown if (!string.IsNullOrWhiteSpace(payload)) { byte[] array = Convert.FromBase64String(payload); ZPackage val = new ZPackage(); val.Load(array); reader(val); } } private static void WriteSnapshot(ZPackage pkg, SessionSnapshotDto snapshot) { //IL_0083: Unknown result type (might be due to invalid IL or missing references) pkg.Write(snapshot.SessionId ?? string.Empty); pkg.Write(snapshot.TerminalUid ?? string.Empty); pkg.Write(snapshot.Revision); pkg.Write(snapshot.SlotsUsedVirtual); pkg.Write(snapshot.SlotsTotalPhysical); pkg.Write(snapshot.ChestCount); pkg.Write(snapshot.Items.Count); foreach (AggregatedItem item in snapshot.Items) { WriteItemKey(pkg, item.Key); pkg.Write(item.DisplayName ?? string.Empty); pkg.Write(item.TotalAmount); pkg.Write(item.SourceCount); pkg.Write(item.StackSize); } } private static SessionSnapshotDto ReadSnapshot(ZPackage pkg) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006e: 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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown SessionSnapshotDto sessionSnapshotDto = new SessionSnapshotDto { SessionId = pkg.ReadString(), TerminalUid = pkg.ReadString(), Revision = pkg.ReadLong(), SlotsUsedVirtual = pkg.ReadInt(), SlotsTotalPhysical = pkg.ReadInt(), ChestCount = pkg.ReadInt() }; int num = pkg.ReadInt(); if (num < 0) { num = 0; } List list = new List(num); for (int i = 0; i < num; i++) { list.Add(new AggregatedItem { Key = ReadItemKey(pkg), DisplayName = pkg.ReadString(), TotalAmount = pkg.ReadInt(), SourceCount = pkg.ReadInt(), StackSize = pkg.ReadInt() }); } sessionSnapshotDto.Items = list; return sessionSnapshotDto; } private static void WriteItemKey(ZPackage pkg, ItemKey key) { pkg.Write(((ItemKey)(ref key)).PrefabName ?? string.Empty); pkg.Write(((ItemKey)(ref key)).Quality); pkg.Write(((ItemKey)(ref key)).Variant); } private static ItemKey ReadItemKey(ZPackage pkg) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) string text = pkg.ReadString(); int num = pkg.ReadInt(); int num2 = pkg.ReadInt(); return new ItemKey(text, num, num2); } } public sealed class TerminalRpcRoutes { public const string OpenSessionRoute = "US_OpenSession"; public const string SessionSnapshotRoute = "US_SessionSnapshot"; public const string ReserveWithdrawRoute = "US_ReserveWithdraw"; public const string ReserveResultRoute = "US_ReserveResult"; public const string CommitReservationRoute = "US_CommitReservation"; public const string CancelReservationRoute = "US_CancelReservation"; public const string DepositRequestRoute = "US_DepositRequest"; public const string ApplyResultRoute = "US_ApplyResult"; public const string CloseSessionRoute = "US_CloseSession"; public const string SessionDeltaRoute = "US_SessionDelta"; private static readonly MethodInfo? ZNetGetServerPeerID = typeof(ZNet).GetMethod("GetServerPeerID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly MethodInfo? RoutedRpcGetServerPeerID = typeof(ZRoutedRpc).GetMethod("GetServerPeerID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo? RoutedRpcServerPeerIDField = typeof(ZRoutedRpc).GetField("m_serverPeerID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? typeof(ZRoutedRpc).GetField("m_serverUID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly MethodInfo? ZNetIsServerMethod = typeof(ZNet).GetMethod("IsServer", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo? ZNetIsServerField = typeof(ZNet).GetField("m_isServer", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private readonly TerminalAuthorityService _authority; private readonly ManualLogSource _logger; private bool _registered; public event Action? SessionSnapshotReceived; public event Action? ReserveResultReceived; public event Action? ApplyResultReceived; public event Action? SessionDeltaReceived; public TerminalRpcRoutes(TerminalAuthorityService authority, ManualLogSource logger) { _authority = authority; _logger = logger; _authority.DeltaReady += OnAuthorityDeltaReady; } public bool EnsureRegistered() { if (_registered) { return true; } if (ZRoutedRpc.instance == null) { return false; } ZRoutedRpc.instance.Register("US_OpenSession", (Action)OnOpenSessionRequest); ZRoutedRpc.instance.Register("US_SessionSnapshot", (Action)OnSessionSnapshot); ZRoutedRpc.instance.Register("US_ReserveWithdraw", (Action)OnReserveWithdrawRequest); ZRoutedRpc.instance.Register("US_ReserveResult", (Action)OnReserveResult); ZRoutedRpc.instance.Register("US_CommitReservation", (Action)OnCommitReservationRequest); ZRoutedRpc.instance.Register("US_CancelReservation", (Action)OnCancelReservationRequest); ZRoutedRpc.instance.Register("US_DepositRequest", (Action)OnDepositRequest); ZRoutedRpc.instance.Register("US_ApplyResult", (Action)OnApplyResult); ZRoutedRpc.instance.Register("US_CloseSession", (Action)OnCloseSessionRequest); ZRoutedRpc.instance.Register("US_SessionDelta", (Action)OnSessionDelta); _registered = true; _logger.LogInfo((object)"Unified Storage terminal RPC routes registered."); return true; } public void RequestOpenSession(OpenSessionRequestDto request) { if (EnsureRegistered()) { long peerId = ResolveServerPeerId(); string payload = TerminalCodec.EncodeOpenSessionRequest(request); InvokeToPeer(peerId, "US_OpenSession", payload); } } public void RequestReserveWithdraw(ReserveWithdrawRequestDto request) { if (EnsureRegistered()) { long peerId = ResolveServerPeerId(); string payload = TerminalCodec.EncodeReserveWithdrawRequest(request); InvokeToPeer(peerId, "US_ReserveWithdraw", payload); } } public void RequestCommitReservation(CommitReservationRequestDto request) { if (EnsureRegistered()) { long peerId = ResolveServerPeerId(); string payload = TerminalCodec.EncodeCommitReservationRequest(request); InvokeToPeer(peerId, "US_CommitReservation", payload); } } public void RequestCancelReservation(CancelReservationRequestDto request) { if (EnsureRegistered()) { long peerId = ResolveServerPeerId(); string payload = TerminalCodec.EncodeCancelReservationRequest(request); InvokeToPeer(peerId, "US_CancelReservation", payload); } } public void RequestDeposit(DepositRequestDto request) { if (EnsureRegistered()) { long peerId = ResolveServerPeerId(); string payload = TerminalCodec.EncodeDepositRequest(request); InvokeToPeer(peerId, "US_DepositRequest", payload); } } public void RequestCloseSession(CloseSessionRequestDto request) { if (EnsureRegistered()) { long peerId = ResolveServerPeerId(); string payload = TerminalCodec.EncodeCloseSessionRequest(request); InvokeToPeer(peerId, "US_CloseSession", payload); } } private void OnAuthorityDeltaReady(IReadOnlyCollection peers, SessionDeltaDto delta) { if (!EnsureRegistered()) { return; } string payload = TerminalCodec.EncodeSessionDelta(delta); foreach (long peer in peers) { InvokeToPeer(peer, "US_SessionDelta", payload); } } private void OnOpenSessionRequest(long sender, string payload) { if (!IsServer()) { return; } try { OpenSessionRequestDto request = TerminalCodec.DecodeOpenSessionRequest(payload); string payload2 = TerminalCodec.EncodeOpenSessionResponse(_authority.HandleOpenSession(sender, request)); InvokeToPeer(sender, "US_SessionSnapshot", payload2); } catch (Exception arg) { _logger.LogError((object)$"Open session request failed: {arg}"); } } private void OnReserveWithdrawRequest(long sender, string payload) { if (!IsServer()) { return; } try { ReserveWithdrawRequestDto request = TerminalCodec.DecodeReserveWithdrawRequest(payload); string payload2 = TerminalCodec.EncodeReserveWithdrawResult(_authority.HandleReserveWithdraw(sender, request)); InvokeToPeer(sender, "US_ReserveResult", payload2); } catch (Exception arg) { _logger.LogError((object)$"Reserve withdraw request failed: {arg}"); } } private void OnCommitReservationRequest(long sender, string payload) { if (!IsServer()) { return; } try { CommitReservationRequestDto request = TerminalCodec.DecodeCommitReservationRequest(payload); string payload2 = TerminalCodec.EncodeApplyResult(_authority.HandleCommitReservation(sender, request)); InvokeToPeer(sender, "US_ApplyResult", payload2); } catch (Exception arg) { _logger.LogError((object)$"Commit reservation request failed: {arg}"); } } private void OnCancelReservationRequest(long sender, string payload) { if (!IsServer()) { return; } try { CancelReservationRequestDto request = TerminalCodec.DecodeCancelReservationRequest(payload); string payload2 = TerminalCodec.EncodeApplyResult(_authority.HandleCancelReservation(sender, request)); InvokeToPeer(sender, "US_ApplyResult", payload2); } catch (Exception arg) { _logger.LogError((object)$"Cancel reservation request failed: {arg}"); } } private void OnDepositRequest(long sender, string payload) { if (!IsServer()) { return; } try { DepositRequestDto request = TerminalCodec.DecodeDepositRequest(payload); string payload2 = TerminalCodec.EncodeApplyResult(_authority.HandleDeposit(sender, request)); InvokeToPeer(sender, "US_ApplyResult", payload2); } catch (Exception arg) { _logger.LogError((object)$"Deposit request failed: {arg}"); } } private void OnCloseSessionRequest(long sender, string payload) { if (!IsServer()) { return; } try { CloseSessionRequestDto request = TerminalCodec.DecodeCloseSessionRequest(payload); _authority.HandleCloseSession(sender, request); } catch (Exception arg) { _logger.LogError((object)$"Close session request failed: {arg}"); } } private void OnSessionSnapshot(long _, string payload) { try { OpenSessionResponseDto obj = TerminalCodec.DecodeOpenSessionResponse(payload); this.SessionSnapshotReceived?.Invoke(obj); } catch (Exception arg) { _logger.LogError((object)$"Session snapshot decode failed: {arg}"); } } private void OnReserveResult(long _, string payload) { try { ReserveWithdrawResultDto obj = TerminalCodec.DecodeReserveWithdrawResult(payload); this.ReserveResultReceived?.Invoke(obj); } catch (Exception arg) { _logger.LogError((object)$"Reserve result decode failed: {arg}"); } } private void OnApplyResult(long _, string payload) { try { ApplyResultDto obj = TerminalCodec.DecodeApplyResult(payload); this.ApplyResultReceived?.Invoke(obj); } catch (Exception arg) { _logger.LogError((object)$"Apply result decode failed: {arg}"); } } private void OnSessionDelta(long _, string payload) { try { SessionDeltaDto obj = TerminalCodec.DecodeSessionDelta(payload); this.SessionDeltaReceived?.Invoke(obj); } catch (Exception arg) { _logger.LogError((object)$"Session delta decode failed: {arg}"); } } private static void InvokeToPeer(long peerId, string route, string payload) { if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC(peerId, route, new object[1] { payload }); } } private static long ResolveServerPeerId() { if ((Object)(object)ZNet.instance != (Object)null && ZNetGetServerPeerID != null) { object obj = ZNetGetServerPeerID.Invoke(ZNet.instance, null); if (obj is long) { return (long)obj; } } if (ZRoutedRpc.instance != null) { if (RoutedRpcGetServerPeerID != null) { object obj = RoutedRpcGetServerPeerID.Invoke(ZRoutedRpc.instance, null); if (obj is long) { return (long)obj; } } if (RoutedRpcServerPeerIDField != null) { object obj = RoutedRpcServerPeerIDField.GetValue(ZRoutedRpc.instance); if (obj is long) { return (long)obj; } } } return 0L; } private static bool IsServer() { if ((Object)(object)ZNet.instance == (Object)null) { return true; } if (ZNetIsServerMethod != null) { object obj = ZNetIsServerMethod.Invoke(ZNet.instance, null); if (obj is bool) { return (bool)obj; } } if (ZNetIsServerField != null) { object obj = ZNetIsServerField.GetValue(ZNet.instance); if (obj is bool) { return (bool)obj; } } return true; } } } namespace UnifiedStorage.Mod.Models { public sealed class ChestHandle { public string SourceId { get; } public IStorageSource Source { get; } public float Distance { get; } public ChestHandle(string sourceId, IStorageSource source, float distance) { SourceId = sourceId; Source = source; Distance = distance; } } public sealed class OpenSessionRequestDto { public string RequestId { get; set; } = string.Empty; public string SessionId { get; set; } = string.Empty; public string TerminalUid { get; set; } = string.Empty; public long PlayerId { get; set; } public float AnchorX { get; set; } public float AnchorY { get; set; } public float AnchorZ { get; set; } public float Radius { get; set; } } public sealed class OpenSessionResponseDto { public string RequestId { get; set; } = string.Empty; public bool Success { get; set; } public string Reason { get; set; } = string.Empty; public SessionSnapshotDto Snapshot { get; set; } = new SessionSnapshotDto(); } public sealed class ReserveWithdrawRequestDto { public string RequestId { get; set; } = string.Empty; public string SessionId { get; set; } = string.Empty; public string OperationId { get; set; } = string.Empty; public string TerminalUid { get; set; } = string.Empty; public long PlayerId { get; set; } public long ExpectedRevision { get; set; } public ItemKey Key { get; set; } public int Amount { get; set; } } public sealed class ReserveWithdrawResultDto { public string RequestId { get; set; } = string.Empty; public bool Success { get; set; } public string Reason { get; set; } = string.Empty; public string TokenId { get; set; } = string.Empty; public ItemKey Key { get; set; } public int ReservedAmount { get; set; } public long Revision { get; set; } public SessionSnapshotDto Snapshot { get; set; } = new SessionSnapshotDto(); } public sealed class CommitReservationRequestDto { public string RequestId { get; set; } = string.Empty; public string SessionId { get; set; } = string.Empty; public string OperationId { get; set; } = string.Empty; public string TerminalUid { get; set; } = string.Empty; public string TokenId { get; set; } = string.Empty; } public sealed class CancelReservationRequestDto { public string RequestId { get; set; } = string.Empty; public string SessionId { get; set; } = string.Empty; public string OperationId { get; set; } = string.Empty; public string TerminalUid { get; set; } = string.Empty; public long PlayerId { get; set; } public string TokenId { get; set; } = string.Empty; public int Amount { get; set; } } public sealed class DepositRequestDto { public string RequestId { get; set; } = string.Empty; public string SessionId { get; set; } = string.Empty; public string OperationId { get; set; } = string.Empty; public string TerminalUid { get; set; } = string.Empty; public long PlayerId { get; set; } public long ExpectedRevision { get; set; } public ItemKey Key { get; set; } public int Amount { get; set; } } public sealed class ApplyResultDto { public string RequestId { get; set; } = string.Empty; public bool Success { get; set; } public string Reason { get; set; } = string.Empty; public string OperationType { get; set; } = string.Empty; public string TokenId { get; set; } = string.Empty; public int AppliedAmount { get; set; } public long Revision { get; set; } public SessionSnapshotDto Snapshot { get; set; } = new SessionSnapshotDto(); } public sealed class CloseSessionRequestDto { public string RequestId { get; set; } = string.Empty; public string SessionId { get; set; } = string.Empty; public string TerminalUid { get; set; } = string.Empty; public long PlayerId { get; set; } } public sealed class SessionDeltaDto { public string SessionId { get; set; } = string.Empty; public string TerminalUid { get; set; } = string.Empty; public long Revision { get; set; } public SessionSnapshotDto Snapshot { get; set; } = new SessionSnapshotDto(); } public sealed class SessionSnapshotDto { public string SessionId { get; set; } = string.Empty; public string TerminalUid { get; set; } = string.Empty; public long Revision { get; set; } public int SlotsUsedVirtual { get; set; } public int SlotsTotalPhysical { get; set; } public int ChestCount { get; set; } public List Items { get; set; } = new List(); } } namespace UnifiedStorage.Mod.Domain { public interface IStorageSource { string SourceId { get; } float Distance { get; } bool IsValid { get; } int PhysicalSlotCount { get; } int DisplaySlotsUsed { get; } IReadOnlyList ReadStacks(); ItemData? GetItemPrototype(ItemKey key); bool CanPlayerAccess(Player? player); bool HasCapacityFor(ItemKey key, int maxStack); int DepositPriority(ItemKey key); int RemoveItems(ItemKey key, int amount); int AddItems(ItemKey key, int amount); bool OwnsInventory(Inventory inventory); } public interface IContainerScanner { IReadOnlyList GetNearbyContainers(Vector3 center, float radius, Container? ignoreContainer = null); } } namespace UnifiedStorage.Mod.Diagnostics { public sealed class StorageTrace { private readonly ManualLogSource _logger; private readonly StorageConfig _config; public bool IsDevMode => _config.EnableDevLogs.Value; public StorageTrace(ManualLogSource logger, StorageConfig config) { _logger = logger; _config = config; } public void Dev(string message) { if (IsDevMode) { _logger.LogInfo((object)("[UnifiedStorage] " + message)); } } public void Warn(string message) { if (IsDevMode) { _logger.LogWarning((object)("[UnifiedStorage] " + message)); } } } } namespace UnifiedStorage.Mod.Config { public sealed class StorageConfig { private const int DefaultMaxContainersScanned = 128; private const bool DefaultRequireAccessCheck = true; private const string DefaultTerminalDisplayName = "Unified Chest"; private const bool DefaultTerminalTintEnabled = true; private const string DefaultTerminalTintColor = "#6EA84A"; private const float DefaultTerminalTintStrength = 0.55f; public ConfigEntry ScanRadius { get; } public ConfigEntry MaxContainersScanned { get; } public ConfigEntry EnableDevLogs { get; } public ConfigEntry TerminalPieceEnabled { get; } public int MaxContainersScannedLimit { get { int num = MaxContainersScanned?.Value ?? 128; if (num <= 0) { return 128; } return num; } } public bool RequireAccessCheck => true; public string TerminalDisplayName => "Unified Chest"; public bool TerminalTintEnabled => true; public string TerminalTintColor => "#6EA84A"; public float TerminalTintStrength => 0.55f; public StorageConfig(ConfigFile config) { ScanRadius = config.Bind("General", "ScanRadius", 20f, "Radius in meters used to include nearby chests."); MaxContainersScanned = config.Bind("General", "MaxContainersScanned", 128, "Maximum number of storage sources scanned per terminal refresh (containers + drawers). Default: 128. Increasing this value can significantly impact performance, especially in large bases."); EnableDevLogs = config.Bind("Debug", "EnableDevLogs", false, "Enable dev logs: session open/close, deposits, withdrawals, and storage updates."); TerminalPieceEnabled = config.Bind("Terminal", "TerminalPieceEnabled", true, "Enable Unified Chest terminal piece."); } } }