Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of RunicStorage v1.0.0
RunicStorage.dll
Decompiled 9 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Runic.Foundation.Core; using RunicStorage.Engine; using RunicStorage.Runtime; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Runic Storage")] [assembly: AssemblyDescription("Secure cached chest hover and player-directed authorized storage tools for Valheim.")] [assembly: AssemblyCompany("Chazman")] [assembly: AssemblyProduct("Runic Storage")] [assembly: ComVisible(false)] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Runic.Foundation.Core { public static class RunicIdentifier { public static bool IsValid(string value) { if (string.IsNullOrEmpty(value) || value.Length > 128) { return false; } bool flag = false; bool flag2 = false; foreach (char c in value) { if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) { flag = true; flag2 = false; continue; } switch (c) { case '-': if (!flag || flag2) { return false; } flag2 = true; break; case '.': if (!flag || flag2) { return false; } flag = false; flag2 = false; break; default: return false; } } if (flag) { return !flag2; } return false; } public static string Require(string value, string parameterName) { if (!IsValid(value)) { throw new ArgumentException("Runic identifiers must be lowercase dot-separated tokens containing only ASCII letters, digits, and internal hyphens.", parameterName); } return value; } } public sealed class InputChord : IEquatable<InputChord> { private readonly IReadOnlyList<string> _modifiers; public string DeviceId { get; } public string PrimaryControl { get; } public IReadOnlyList<string> Modifiers => _modifiers; public string CanonicalId { get; } public InputChord(string deviceId, string primaryControl, IEnumerable<string> modifiers = null) { DeviceId = RunicIdentifier.Require(deviceId, "deviceId"); PrimaryControl = RequireControl(primaryControl, "primaryControl"); string text = CanonicalizeControl(PrimaryControl); SortedDictionary<string, string> sortedDictionary = new SortedDictionary<string, string>(StringComparer.Ordinal); if (modifiers != null) { foreach (string modifier in modifiers) { string text2 = RequireControl(modifier, "modifiers"); string text3 = CanonicalizeControl(text2); if (string.Equals(text3, text, StringComparison.Ordinal)) { throw new ArgumentException("The primary control cannot also be a modifier.", "modifiers"); } if (sortedDictionary.ContainsKey(text3)) { throw new ArgumentException("Duplicate input modifier: " + text2, "modifiers"); } sortedDictionary.Add(text3, text2); } } string[] array = new string[sortedDictionary.Count]; int num = 0; foreach (string value in sortedDictionary.Values) { array[num++] = value; } _modifiers = Array.AsReadOnly(array); StringBuilder stringBuilder = new StringBuilder(DeviceId).Append(':'); foreach (string key in sortedDictionary.Keys) { stringBuilder.Append(key).Append('+'); } stringBuilder.Append(text); CanonicalId = stringBuilder.ToString(); } public bool Equals(InputChord other) { if (other != null) { return string.Equals(CanonicalId, other.CanonicalId, StringComparison.Ordinal); } return false; } public override bool Equals(object obj) { return Equals(obj as InputChord); } public override int GetHashCode() { return StringComparer.Ordinal.GetHashCode(CanonicalId); } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < _modifiers.Count; i++) { if (i > 0) { stringBuilder.Append(" + "); } stringBuilder.Append(_modifiers[i]); } if (_modifiers.Count > 0) { stringBuilder.Append(" + "); } return stringBuilder.Append(PrimaryControl).Append(" [").Append(DeviceId) .Append(']') .ToString(); } private static string RequireControl(string value, string parameterName) { if (string.IsNullOrWhiteSpace(value)) { throw new ArgumentException("An input control name is required.", parameterName); } string text = value.Trim(); if (text.Length > 64) { throw new ArgumentException("Input control names cannot exceed 64 characters.", parameterName); } string text2 = text; foreach (char c in text2) { if (char.IsControl(c) || c == ':' || c == '+' || c == '|') { throw new ArgumentException("The input control name contains a reserved character.", parameterName); } } if (CanonicalizeControl(text).Length == 0) { throw new ArgumentException("The input control name has no canonical characters.", parameterName); } return text; } private static string CanonicalizeControl(string value) { StringBuilder stringBuilder = new StringBuilder(value.Length); foreach (char c in value) { if (!char.IsWhiteSpace(c) && c != '_' && c != '-') { stringBuilder.Append(char.ToLowerInvariant(c)); } } return stringBuilder.ToString(); } } public sealed class KeybindingDescriptor { public string ModuleId { get; } public string BindingId { get; } public string QualifiedId => ModuleId + "/" + BindingId; public string DisplayName { get; } public InputChord Chord { get; } public string Context { get; } public KeybindingDescriptor(string moduleId, string bindingId, string displayName, InputChord chord, string context = "gameplay") { ModuleId = RunicIdentifier.Require(moduleId, "moduleId"); BindingId = RunicIdentifier.Require(bindingId, "bindingId"); Context = RunicIdentifier.Require(context, "context"); if (string.IsNullOrWhiteSpace(displayName)) { throw new ArgumentException("A keybinding display name is required.", "displayName"); } Chord = chord ?? throw new ArgumentNullException("chord"); DisplayName = displayName.Trim(); } } public sealed class KeybindingConflict { public InputChord Chord { get; } public IReadOnlyList<KeybindingDescriptor> Bindings { get; } internal KeybindingConflict(InputChord chord, IReadOnlyList<KeybindingDescriptor> bindings) { Chord = chord; Bindings = bindings; } } public enum KeybindingChangeKind { Registered, Unregistered } public sealed class KeybindingsChangedEventArgs : EventArgs { public KeybindingChangeKind Kind { get; } public KeybindingDescriptor Binding { get; } public KeybindingConflict Conflict { get; } internal KeybindingsChangedEventArgs(KeybindingChangeKind kind, KeybindingDescriptor binding, KeybindingConflict conflict) { Kind = kind; Binding = binding; Conflict = conflict; } } public sealed class KeybindingConflictRegistry { private sealed class BindingEntry { internal KeybindingDescriptor Binding { get; } internal long Token { get; } internal BindingEntry(KeybindingDescriptor binding, long token) { Binding = binding; Token = token; } } private readonly object _sync = new object(); private readonly Dictionary<string, BindingEntry> _byQualifiedId = new Dictionary<string, BindingEntry>(StringComparer.Ordinal); private readonly Dictionary<string, List<BindingEntry>> _byChord = new Dictionary<string, List<BindingEntry>>(StringComparer.Ordinal); private long _nextToken; public event EventHandler<KeybindingsChangedEventArgs> Changed; public KeybindingRegistration Register(KeybindingDescriptor binding) { if (binding == null) { throw new ArgumentNullException("binding"); } long token; KeybindingConflict conflict; lock (_sync) { if (_byQualifiedId.ContainsKey(binding.QualifiedId)) { throw new InvalidOperationException("A keybinding is already registered as '" + binding.QualifiedId + "'."); } if (_nextToken == long.MaxValue) { throw new InvalidOperationException("The keybinding registration token space is exhausted."); } token = ++_nextToken; BindingEntry bindingEntry = new BindingEntry(binding, token); _byQualifiedId.Add(binding.QualifiedId, bindingEntry); if (!_byChord.TryGetValue(binding.Chord.CanonicalId, out var value)) { value = new List<BindingEntry>(); _byChord.Add(binding.Chord.CanonicalId, value); } value.Add(bindingEntry); conflict = CreateConflictLocked(binding.Chord.CanonicalId); } RaiseChanged(new KeybindingsChangedEventArgs(KeybindingChangeKind.Registered, binding, conflict)); return new KeybindingRegistration(this, binding, token); } public bool Unregister(string moduleId, string bindingId) { RunicIdentifier.Require(moduleId, "moduleId"); RunicIdentifier.Require(bindingId, "bindingId"); return Unregister(moduleId + "/" + bindingId, (long?)null); } public bool TryGetBinding(string moduleId, string bindingId, out KeybindingDescriptor binding) { binding = null; if (!RunicIdentifier.IsValid(moduleId) || !RunicIdentifier.IsValid(bindingId)) { return false; } lock (_sync) { if (!_byQualifiedId.TryGetValue(moduleId + "/" + bindingId, out var value)) { return false; } binding = value.Binding; return true; } } public IReadOnlyList<KeybindingDescriptor> GetBindings() { lock (_sync) { List<KeybindingDescriptor> list = new List<KeybindingDescriptor>(_byQualifiedId.Count); foreach (BindingEntry value in _byQualifiedId.Values) { list.Add(value.Binding); } list.Sort((KeybindingDescriptor left, KeybindingDescriptor right) => StringComparer.Ordinal.Compare(left.QualifiedId, right.QualifiedId)); return list.AsReadOnly(); } } public IReadOnlyList<KeybindingConflict> GetConflicts() { lock (_sync) { List<KeybindingConflict> list = new List<KeybindingConflict>(); foreach (string key in _byChord.Keys) { KeybindingConflict keybindingConflict = CreateConflictLocked(key); if (keybindingConflict != null) { list.Add(keybindingConflict); } } list.Sort((KeybindingConflict left, KeybindingConflict right) => StringComparer.Ordinal.Compare(left.Chord.CanonicalId, right.Chord.CanonicalId)); return list.AsReadOnly(); } } public IReadOnlyList<KeybindingConflict> GetConflictsFor(string moduleId) { RunicIdentifier.Require(moduleId, "moduleId"); IReadOnlyList<KeybindingConflict> conflicts = GetConflicts(); List<KeybindingConflict> list = new List<KeybindingConflict>(); foreach (KeybindingConflict item in conflicts) { foreach (KeybindingDescriptor binding in item.Bindings) { if (string.Equals(binding.ModuleId, moduleId, StringComparison.Ordinal)) { list.Add(item); break; } } } return list.AsReadOnly(); } internal bool IsActive(string qualifiedId, long token) { lock (_sync) { BindingEntry value; return _byQualifiedId.TryGetValue(qualifiedId, out value) && value.Token == token; } } internal bool Unregister(string qualifiedId, long? requiredToken) { KeybindingDescriptor binding; KeybindingConflict conflict; lock (_sync) { if (!_byQualifiedId.TryGetValue(qualifiedId, out var entry) || (requiredToken.HasValue && entry.Token != requiredToken.Value)) { return false; } binding = entry.Binding; _byQualifiedId.Remove(qualifiedId); List<BindingEntry> list = _byChord[binding.Chord.CanonicalId]; list.RemoveAll((BindingEntry candidate) => candidate.Token == entry.Token); if (list.Count == 0) { _byChord.Remove(binding.Chord.CanonicalId); } conflict = CreateConflictLocked(binding.Chord.CanonicalId); } RaiseChanged(new KeybindingsChangedEventArgs(KeybindingChangeKind.Unregistered, binding, conflict)); return true; } private KeybindingConflict CreateConflictLocked(string chordId) { if (!_byChord.TryGetValue(chordId, out var value) || value.Count < 2) { return null; } List<KeybindingDescriptor> list = new List<KeybindingDescriptor>(value.Count); foreach (BindingEntry item in value) { list.Add(item.Binding); } list.Sort((KeybindingDescriptor left, KeybindingDescriptor right) => StringComparer.Ordinal.Compare(left.QualifiedId, right.QualifiedId)); return new KeybindingConflict(list[0].Chord, list.AsReadOnly()); } private void RaiseChanged(KeybindingsChangedEventArgs arguments) { EventHandler<KeybindingsChangedEventArgs> eventHandler = this.Changed; if (eventHandler == null) { return; } Delegate[] invocationList = eventHandler.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { EventHandler<KeybindingsChangedEventArgs> eventHandler2 = (EventHandler<KeybindingsChangedEventArgs>)invocationList[i]; try { eventHandler2(this, arguments); } catch (Exception) { } } } } public sealed class KeybindingRegistration : IDisposable { private readonly KeybindingConflictRegistry _registry; private readonly long _token; public KeybindingDescriptor Binding { get; } public bool IsActive => _registry.IsActive(Binding.QualifiedId, _token); internal KeybindingRegistration(KeybindingConflictRegistry registry, KeybindingDescriptor binding, long token) { _registry = registry; Binding = binding; _token = token; } public void Dispose() { _registry.Unregister(Binding.QualifiedId, _token); } } } namespace RunicStorage { [BepInPlugin("chazman.RunicStorage", "Runic Storage", "1.0.0")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "chazman.RunicStorage"; public const string Name = "Runic Storage"; public const string Version = "1.0.0"; private readonly List<KeybindingRegistration> _keybindingRegistrations = new List<KeybindingRegistration>(); private readonly KeybindingConflictRegistry _keybindings = new KeybindingConflictRegistry(); private static readonly FieldInfo DragItemField = AccessTools.Field(typeof(InventoryGui), "m_dragItem"); private static readonly FieldInfo CurrentContainerField = AccessTools.Field(typeof(InventoryGui), "m_currentContainer"); private readonly Harmony _harmony = new Harmony("chazman.RunicStorage"); private readonly StorageInputReader _inputReader = new StorageInputReader(); private StorageActions _actions; private StorageSearchPanel _searchPanel; private bool _readyMessageShown; internal static ContainerIndex Index { get; private set; } internal static ManualLogSource Log { get; private set; } private static StorageSearchPanel ActiveSearchPanel { get; set; } internal static bool SearchPanelOpen => ActiveSearchPanel?.IsOpen ?? false; private void Awake() { Log = ((BaseUnityPlugin)this).Logger; PluginConfig.Bind(((BaseUnityPlugin)this).Config); try { ((BaseUnityPlugin)this).Config.SettingChanged += OnSettingChanged; ZInput.OnInputLayoutChanged += OnInputLayoutChanged; Localization.OnLanguageChange = (Action)Delegate.Combine(Localization.OnLanguageChange, new Action(ContainerHoverContents.InvalidateConfiguration)); Index = new ContainerIndex(); _searchPanel = new StorageSearchPanel(); ActiveSearchPanel = _searchPanel; _actions = new StorageActions(Index, _searchPanel); RegisterKeybindings(); _harmony.PatchAll(typeof(Plugin).Assembly); Index.RefreshLoadedContainers(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Runic Storage v1.0.0 ready: chest hover, Quick Stack, Restock, Search, Sort, Store All, and Consolidate use native local ownership."); if (!ContainerHoverContents.IsSupported) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Chest-content hover is unavailable because its installed Container/PrivateArea adapter signatures did not match. Other Storage features remain enabled."); } LogConfigurationSummary("startup"); } catch (Exception arg) { Shutdown(); ((BaseUnityPlugin)this).Logger.LogError((object)string.Format("{0} failed closed during startup: {1}", "Runic Storage", arg)); } } private void Update() { if (_actions == null) { return; } try { ShowReadyMessage(); _searchPanel?.Tick(); StorageSearchPanel searchPanel = _searchPanel; if (searchPanel != null && searchPanel.IsOpen) { return; } StorageRouteContext context = CaptureRouteContext(); StorageActionEdges num = _inputReader.ReadKeyboardEdges(); StorageActionEdges storageActionEdges = _inputReader.ReadControllerEdges(context); StorageActionEdges edges = num | storageActionEdges; StorageActionRequest request = StorageActionRouter.Select(edges); if (!request.IsPresent) { return; } StorageRouteDecision storageRouteDecision = StorageActionRouter.Route(request, context); if (PluginConfig.DebugTransfers.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("input-detected edges=" + StorageInputReader.DescribeEdges(edges) + " selected=" + StorageActionDiagnostics.ActionCode(request.Action) + " source=" + StorageActionDiagnostics.OriginCode(request.Origin) + " route=" + storageRouteDecision.Outcome.ToString().ToLowerInvariant() + " reason=" + StorageActionDiagnostics.RouteReasonCode(storageRouteDecision.Reason))); } if (storageRouteDecision.Outcome != StorageRouteOutcome.Execute) { string text = StorageActionDiagnostics.RouteFeedback(storageRouteDecision.Reason); if (text.Length != 0) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { ((Character)localPlayer).Message((MessageType)2, text, 0, (Sprite)null); } } return; } switch (request.Action) { case StorageActionKind.QuickStack: _actions.QuickStack(); break; case StorageActionKind.StoreAllOpenedContainer: _actions.StoreAllOpenedContainer(); break; case StorageActionKind.Restock: _actions.Restock(); break; case StorageActionKind.SortOpenedContainer: _actions.SortOpenedContainer(); break; case StorageActionKind.Consolidate: _actions.ConsolidateCarriedStacks(); break; case StorageActionKind.Search: _actions.Search(); break; } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Runic Storage action stopped after restoring the in-flight item move; earlier completed moves, if any, remain applied: " + ex)); Player localPlayer2 = Player.m_localPlayer; if ((Object)(object)localPlayer2 != (Object)null) { ((Character)localPlayer2).Message((MessageType)2, "Runic Storage stopped safely; completed moves may remain. Check BepInEx/LogOutput.log.", 0, (Sprite)null); } } } private void OnGUI() { try { _searchPanel?.Draw(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Storage search list closed after a UI error: " + ex.Message)); _searchPanel?.Close(); } } private void OnDestroy() { Shutdown(); } private void RegisterKeybindings() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) DisposeKeybindings(); RegisterKey("quick-stack", "Quick Stack", PluginConfig.QuickStackKey.Value); RegisterKey("restock", "Restock", PluginConfig.RestockKey.Value); RegisterKey("sort", "Sort Opened Container", PluginConfig.SortOpenedContainerKey.Value); RegisterKey("store-all", "Store All Into Opened Container", PluginConfig.StoreAllOpenedContainerKey.Value); RegisterKey("consolidate", "Consolidate Carried Stacks", PluginConfig.ConsolidateKey.Value); RegisterKey("search", "Search Nearby Storage", PluginConfig.SearchKey.Value); if (PluginConfig.ControllerShortcuts.Value) { RegisterControllerKey("controller-quick-stack", "Quick Stack (Controller)", PluginConfig.ControllerQuickStack.Value); RegisterControllerKey("controller-restock", "Restock (Controller)", PluginConfig.ControllerRestock.Value); RegisterControllerKey("controller-sort", "Sort Opened Container (Controller)", PluginConfig.ControllerSort.Value); RegisterControllerKey("controller-consolidate", "Consolidate (Controller)", PluginConfig.ControllerConsolidate.Value); RegisterControllerKey("controller-search", "Search (Controller)", PluginConfig.ControllerSearch.Value); } } private void RegisterKey(string bindingId, string displayName, KeyboardShortcut shortcut) { //IL_0016: 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) //IL_001d: 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) List<string> list = new List<string>(); foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers) { list.Add(((object)modifier).ToString()); } _keybindingRegistrations.Add(_keybindings.Register(new KeybindingDescriptor("runic.storage", bindingId, displayName, new InputChord("keyboard", ((object)((KeyboardShortcut)(ref shortcut)).MainKey).ToString(), list)))); } private void RegisterControllerKey(string bindingId, string displayName, string action) { string text = (action ?? string.Empty).Trim(); string text2 = (PluginConfig.ControllerModifier.Value ?? string.Empty).Trim(); if (text.Length == 0 || text2.Length == 0 || string.Equals(text, text2, StringComparison.Ordinal)) { return; } try { _keybindingRegistrations.Add(_keybindings.Register(new KeybindingDescriptor("runic.storage", bindingId, displayName, new InputChord("controller", text, new string[1] { text2 })))); } catch (ArgumentException ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Controller keybinding " + displayName + " was not registered: " + ex.Message)); } } private void OnSettingChanged(object sender, SettingChangedEventArgs arguments) { ContainerHoverContents.InvalidateConfiguration(); _inputReader.InvalidateControllerBindings(); StorageControllerCollisionGuard.Reset(); try { RegisterKeybindings(); LogConfigurationSummary("configuration-changed"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Storage keybinding refresh failed: " + ex.Message)); } } private void OnInputLayoutChanged() { _inputReader.InvalidateControllerBindings(); StorageControllerCollisionGuard.Reset(); ConfigEntry<bool> debugTransfers = PluginConfig.DebugTransfers; if (debugTransfers != null && debugTransfers.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Controller input layout changed; Storage controller bindings will be re-resolved once."); } } private void DisposeKeybindings() { for (int num = _keybindingRegistrations.Count - 1; num >= 0; num--) { try { _keybindingRegistrations[num].Dispose(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not unregister a storage keybinding cleanly: " + ex.Message)); } } _keybindingRegistrations.Clear(); } internal static StorageRouteContext CaptureRouteContext() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown //IL_001b: 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_002f: Expected O, but got Unknown //IL_002f: Expected O, but got Unknown //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown Player localPlayer = Player.m_localPlayer; bool flag = InventoryGui.IsVisible(); bool flag2 = (Object)localPlayer != (Object)null && (Object)localPlayer == (Object)Player.m_localPlayer && ((Character)localPlayer).IsOwner(); return new StorageRouteContext(PluginConfig.Enabled?.Value ?? false, (Object)localPlayer == (Object)null || InputBlocked(), HasDraggedItem(), flag, flag && HasOpenedContainer(), StoreGui.IsVisible(), Minimap.IsOpen(), flag2, flag2); } private static bool InputBlocked() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown if (Console.IsVisible() || Menu.IsVisible() || TextInput.IsVisible()) { return true; } if ((Object)Chat.instance != (Object)null && Chat.instance.HasFocus()) { return true; } if ((Object)TextViewer.instance != (Object)null && TextViewer.instance.IsVisible()) { return true; } if (Hud.InRadial() || Hud.IsPieceSelectionVisible() || GameCamera.InFreeFly() || PlayerCustomizaton.IsBarberGuiVisible()) { return true; } Player localPlayer = Player.m_localPlayer; if (!((Object)localPlayer == (Object)null) && !((Character)localPlayer).IsDead() && !((Character)localPlayer).InCutscene()) { return ((Character)localPlayer).IsTeleporting(); } return true; } private static bool HasDraggedItem() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown if ((Object)InventoryGui.instance != (Object)null) { return DragItemField?.GetValue(InventoryGui.instance) is ItemData; } return false; } private static bool HasOpenedContainer() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown if ((Object)InventoryGui.instance != (Object)null) { return CurrentContainerField?.GetValue(InventoryGui.instance) is Container; } return false; } private void ShowReadyMessage() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //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) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) if (!_readyMessageShown && PluginConfig.Enabled.Value && PluginConfig.ShowReadyMessage.Value && !((Object)Player.m_localPlayer == (Object)null) && !((Object)MessageHud.instance == (Object)null)) { _readyMessageShown = true; string text = (PluginConfig.ControllerShortcuts.Value ? (" Controller: hold " + PluginConfig.ControllerModifier.Value + "; see config for routes.") : string.Empty); ((Character)Player.m_localPlayer).Message((MessageType)1, "Runic Storage ready — " + ShortcutLabel(PluginConfig.QuickStackKey.Value) + " Quick Stack; " + ShortcutLabel(PluginConfig.RestockKey.Value) + " Restock; " + ShortcutLabel(PluginConfig.SearchKey.Value) + " Search; " + ShortcutLabel(PluginConfig.ConsolidateKey.Value) + " Consolidate; open a chest and use " + ShortcutLabel(PluginConfig.SortOpenedContainerKey.Value) + " to Sort or " + ShortcutLabel(PluginConfig.StoreAllOpenedContainerKey.Value) + " to Store All." + text, 0, (Sprite)null); } } private void LogConfigurationSummary(string reason) { //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) string text = (PluginConfig.ControllerShortcuts.Value ? ("hold " + PluginConfig.ControllerModifier.Value + ": quick=" + PluginConfig.ControllerQuickStack.Value + ", restock=" + PluginConfig.ControllerRestock.Value + ", sort=" + PluginConfig.ControllerSort.Value + ", consolidate=" + PluginConfig.ControllerConsolidate.Value + ", search=" + PluginConfig.ControllerSearch.Value) : "disabled"); ((BaseUnityPlugin)this).Logger.LogInfo((object)($"Storage configuration ({reason}): Enabled={PluginConfig.Enabled.Value}; " + $"Range={PluginConfig.RangeMeters.Value:0.##}m; MaxCandidates={PluginConfig.MaximumCandidates.Value}; " + $"ProtectHotbar={PluginConfig.ProtectHotbar.Value}; DebugTransfers={PluginConfig.DebugTransfers.Value}; " + $"Hover=[enabled={PluginConfig.ShowContentsOnHover.Value}, kinds={PluginConfig.HoverMaximumItemKinds.Value}, " + $"perLine={PluginConfig.HoverItemsPerLine.Value}, characters={PluginConfig.HoverMaximumCharacters.Value}, " + $"stacks={PluginConfig.HoverMaximumStacksExamined.Value}, " + $"snapshotCharacters={PluginConfig.HoverMaximumSnapshotCharacters.Value}, " + string.Format("retry={0:0.##}s, signatures={1}]; ", PluginConfig.HoverRefreshIntervalSeconds.Value, ContainerHoverContents.IsSupported ? "ready" : "unavailable") + "keyboard=[quick " + ShortcutLabel(PluginConfig.QuickStackKey.Value) + ", restock " + ShortcutLabel(PluginConfig.RestockKey.Value) + ", sort " + ShortcutLabel(PluginConfig.SortOpenedContainerKey.Value) + ", store-all " + ShortcutLabel(PluginConfig.StoreAllOpenedContainerKey.Value) + ", consolidate " + ShortcutLabel(PluginConfig.ConsolidateKey.Value) + ", search " + ShortcutLabel(PluginConfig.SearchKey.Value) + "]; controller=[" + text + "]; controller-validation=" + _inputReader.ControllerStatusSummary() + ".")); } private static string ShortcutLabel(KeyboardShortcut shortcut) { //IL_0016: 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) //IL_001d: 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) List<string> list = new List<string>(); foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers) { list.Add(((object)modifier).ToString()); } list.Add(((object)((KeyboardShortcut)(ref shortcut)).MainKey).ToString()); return string.Join("+", list); } private void Shutdown() { ((BaseUnityPlugin)this).Config.SettingChanged -= OnSettingChanged; ZInput.OnInputLayoutChanged -= OnInputLayoutChanged; Localization.OnLanguageChange = (Action)Delegate.Remove(Localization.OnLanguageChange, new Action(ContainerHoverContents.InvalidateConfiguration)); _inputReader.InvalidateControllerBindings(); StorageControllerCollisionGuard.Reset(); StorageSearchGameplayInputGuard.Reset(); ContainerHoverContents.Reset(); try { _harmony.UnpatchSelf(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not unpatch Runic Storage cleanly: " + ex.Message)); } DisposeKeybindings(); _actions = null; _searchPanel?.Dispose(); _searchPanel = null; ActiveSearchPanel = null; Index = null; Log = null; } } internal static class PluginConfig { internal static ConfigEntry<bool> Enabled { get; private set; } internal static ConfigEntry<bool> ShowReadyMessage { get; private set; } internal static ConfigEntry<float> RangeMeters { get; private set; } internal static ConfigEntry<int> MaximumCandidates { get; private set; } internal static ConfigEntry<bool> ProtectHotbar { get; private set; } internal static ConfigEntry<bool> ShowContentsOnHover { get; private set; } internal static ConfigEntry<int> HoverMaximumItemKinds { get; private set; } internal static ConfigEntry<int> HoverItemsPerLine { get; private set; } internal static ConfigEntry<int> HoverMaximumCharacters { get; private set; } internal static ConfigEntry<int> HoverMaximumStacksExamined { get; private set; } internal static ConfigEntry<int> HoverMaximumSnapshotCharacters { get; private set; } internal static ConfigEntry<float> HoverRefreshIntervalSeconds { get; private set; } internal static ConfigEntry<KeyboardShortcut> QuickStackKey { get; private set; } internal static ConfigEntry<KeyboardShortcut> RestockKey { get; private set; } internal static ConfigEntry<string> RestockTargets { get; private set; } internal static ConfigEntry<KeyboardShortcut> SortOpenedContainerKey { get; private set; } internal static ConfigEntry<KeyboardShortcut> StoreAllOpenedContainerKey { get; private set; } internal static ConfigEntry<string> LockedContainerSlots { get; private set; } internal static ConfigEntry<KeyboardShortcut> ConsolidateKey { get; private set; } internal static ConfigEntry<KeyboardShortcut> SearchKey { get; private set; } internal static ConfigEntry<string> SearchItem { get; private set; } internal static ConfigEntry<int> SearchMenuFontSize { get; private set; } internal static ConfigEntry<StorageSearchMenuFontColor> SearchMenuFontColor { get; private set; } internal static ConfigEntry<bool> ControllerShortcuts { get; private set; } internal static ConfigEntry<string> ControllerModifier { get; private set; } internal static ConfigEntry<string> ControllerQuickStack { get; private set; } internal static ConfigEntry<string> ControllerRestock { get; private set; } internal static ConfigEntry<string> ControllerSort { get; private set; } internal static ConfigEntry<string> ControllerConsolidate { get; private set; } internal static ConfigEntry<string> ControllerSearch { get; private set; } internal static ConfigEntry<bool> DebugTransfers { get; private set; } internal static void Bind(ConfigFile config) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Expected O, but got Unknown //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Expected O, but got Unknown //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Expected O, but got Unknown //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Expected O, but got Unknown //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Expected O, but got Unknown //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Expected O, but got Unknown //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: Unknown result type (might be due to invalid IL or missing references) //IL_031d: Unknown result type (might be due to invalid IL or missing references) //IL_034c: Unknown result type (might be due to invalid IL or missing references) //IL_039f: Unknown result type (might be due to invalid IL or missing references) //IL_03a9: Expected O, but got Unknown Enabled = config.Bind<bool>("General", "Enabled", true, "Enable Runic Storage gameplay actions."); ShowReadyMessage = config.Bind<bool>("General", "ShowReadyMessage", true, "Show a one-time in-world control reminder after the local player loads."); RangeMeters = config.Bind<float>("Discovery", "RangeMeters", 20f, new ConfigDescription("Nearby storage radius. Hard limited to 50 meters.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f), Array.Empty<object>())); MaximumCandidates = config.Bind<int>("Discovery", "MaximumCandidates", 64, new ConfigDescription("Maximum cached containers examined per action.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 256), Array.Empty<object>())); ProtectHotbar = config.Bind<bool>("Safety", "ProtectHotbar", true, "Never quick-stack, store-all, or consolidate items in the top-row hotbar."); ShowContentsOnHover = config.Bind<bool>("Hover", "ShowContents", true, "List a closed authorized container's synchronized contents in its hover text. Private, busy, unsynchronized, or Runic-reserved containers retain vanilla text only."); HoverMaximumItemKinds = config.Bind<int>("Hover", "MaximumItemKinds", 8, new ConfigDescription("Maximum distinct item kinds shown in one hover summary.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 24), Array.Empty<object>())); HoverItemsPerLine = config.Bind<int>("Hover", "ItemsPerLine", 3, new ConfigDescription("Maximum item kinds placed on each summary line.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 4), Array.Empty<object>())); HoverMaximumCharacters = config.Bind<int>("Hover", "MaximumCharacters", 320, new ConfigDescription("Hard character ceiling for the complete generated contents suffix, including formatting tags.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(64, 1024), Array.Empty<object>())); HoverMaximumStacksExamined = config.Bind<int>("Hover", "MaximumStacksExamined", 256, new ConfigDescription("Maximum inventory stacks examined when rebuilding one changed summary. Additional stacks are reported as unscanned.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(16, 1024), Array.Empty<object>())); HoverMaximumSnapshotCharacters = config.Bind<int>("Hover", "MaximumSnapshotCharacters", 262144, new ConfigDescription("Maximum persisted Base64 inventory characters accepted for an exact hover snapshot. Oversized third-party containers retain vanilla text.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(16384, 1048576), Array.Empty<object>())); HoverRefreshIntervalSeconds = config.Bind<float>("Hover", "FailedRefreshRetrySeconds", 0.5f, new ConfigDescription("Retry delay after a container cannot prove an exact synchronized inventory snapshot. Stable summaries remain cached by exact persisted item-payload evidence.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>())); QuickStackKey = config.Bind<KeyboardShortcut>("Keys", "QuickStack", new KeyboardShortcut((KeyCode)113, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Deposit eligible carried stacks into authorized nearby containers already holding that item."); RestockKey = config.Bind<KeyboardShortcut>("Keys", "Restock", new KeyboardShortcut((KeyCode)114, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Restock configured carried targets from authorized nearby storage."); RestockTargets = config.Bind<string>("Restock", "Targets", "Wood=50,Stone=50", "Comma-separated prefab/name targets, for example Wood=50,Stone=50."); SortOpenedContainerKey = config.Bind<KeyboardShortcut>("Keys", "SortOpenedContainer", new KeyboardShortcut((KeyCode)115, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Sort the currently opened authorized container by category, name, quality, then weight."); StoreAllOpenedContainerKey = config.Bind<KeyboardShortcut>("Keys", "StoreAllOpenedContainer", new KeyboardShortcut((KeyCode)97, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Store every eligible carried item in the currently opened authorized locally owned container."); LockedContainerSlots = config.Bind<string>("Sort", "LockedSlots", string.Empty, "Semicolon-separated zero-based slots to leave fixed, for example 0,0;1,0."); ConsolidateKey = config.Bind<KeyboardShortcut>("Keys", "ConsolidateCarriedStacks", new KeyboardShortcut((KeyCode)99, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Safely consolidate compatible carried stacks while respecting protected slots and equipment."); SearchKey = config.Bind<KeyboardShortcut>("Keys", "Search", new KeyboardShortcut((KeyCode)102, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Open a selectable list of item kinds in authorized nearby containers and highlight every matching chest."); SearchItem = config.Bind<string>("Search", "SearchItem", "Wood", "Legacy setting retained for configuration compatibility; Alt+F now opens the complete nearby-item list."); SearchMenuFontSize = config.Bind<int>("Search", "MenuFontSize", 14, new ConfigDescription("Font size for every title, label, text field, and button in the Alt+F nearby-item window.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 32), Array.Empty<object>())); StorageSearchMenuFontColor migrated; bool num = TryReadAndRemoveLegacySearchMenuFontColor(config, out migrated); SearchMenuFontColor = config.Bind<StorageSearchMenuFontColor>("Search", "MenuFontColor", StorageSearchMenuFontColor.LightGray, "Named font color for the complete Alt+F nearby-item window. Configuration Manager presents the available colors as a dropdown."); if (num) { SearchMenuFontColor.Value = migrated; } ControllerShortcuts = config.Bind<bool>("Controller", "Enabled", true, "Enable controller chords resolved through Valheim's current ZInput action map."); ControllerModifier = config.Bind<string>("Controller", "ModifierAction", "JoyAltKeys", "Valheim ZInput action held as the controller modifier. Change only to an existing action name."); ControllerQuickStack = config.Bind<string>("Controller", "QuickStackAction", "JoyDPadDown", "Valheim ZInput action pressed with ModifierAction to Quick Stack."); ControllerRestock = config.Bind<string>("Controller", "RestockAction", "JoyDPadUp", "Valheim ZInput action pressed with ModifierAction to Restock."); ControllerSort = config.Bind<string>("Controller", "SortOpenedContainerAction", "JoyRStick", "Valheim ZInput action pressed with ModifierAction to sort the opened container."); ControllerConsolidate = config.Bind<string>("Controller", "ConsolidateAction", "JoyDPadLeft", "Valheim ZInput action pressed with ModifierAction to consolidate carried stacks."); ControllerSearch = config.Bind<string>("Controller", "SearchAction", "JoyDPadRight", "Valheim ZInput action pressed with ModifierAction to search nearby storage."); DebugTransfers = config.Bind<bool>("Diagnostics", "DebugTransfers", false, "Log detected controls, routing decisions, discovery counts, action summaries, transfer legs, and stable no-op/denial codes."); } private static bool TryReadAndRemoveLegacySearchMenuFontColor(ConfigFile config, out StorageSearchMenuFontColor migrated) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown ConfigDefinition val = new ConfigDefinition("Search", "MenuFontColor"); bool result = HasOrphanedValue(config, val); ConfigEntry<string> val2 = config.Bind<string>(val, StorageSearchMenuFontColor.LightGray.ToString(), new ConfigDescription("Legacy Alt+F menu color migration entry.", (AcceptableValueBase)null, Array.Empty<object>())); migrated = StorageSearchMenuAppearance.NormalizeFontColorConfigValue(val2.Value); if (!config.Remove(val)) { throw new InvalidOperationException("Runic Storage could not replace its legacy MenuFontColor setting."); } return result; } private static bool HasOrphanedValue(ConfigFile config, ConfigDefinition definition) { object obj = typeof(ConfigFile).GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(config, null); if (obj == null) { obj = typeof(ConfigFile).GetField("<OrphanedEntries>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(config); } if (obj is IDictionary<ConfigDefinition, string> dictionary) { return dictionary.ContainsKey(definition); } return false; } } } namespace RunicStorage.Runtime { internal static class ContainerHoverContents { private delegate bool CheckAccessDelegate(Container container, long playerId); private delegate string TranslateDelegate(Localization localization, string key); private delegate bool WardStateDelegate(PrivateArea area); private delegate bool WardContainsDelegate(PrivateArea area, Vector3 point, float radius); private sealed class CacheEntry { private WeakReference<string> _persistedReference; internal HoverPersistedEvidence Evidence; internal bool HasEvidence; internal long ConfigurationGeneration; internal float NextRetryAt; internal bool Verified; internal string Suffix; internal string BaseHoverText; internal string CombinedHoverText; internal long LastAccess; internal bool TryGetPersistedReference(out string persisted) { persisted = null; if (_persistedReference != null) { return _persistedReference.TryGetTarget(out persisted); } return false; } internal void SetPersistedReference(string persisted) { if (persisted == null) { _persistedReference = null; } else if (_persistedReference == null) { _persistedReference = new WeakReference<string>(persisted); } else { _persistedReference.SetTarget(persisted); } } } private const int MaximumCacheEntries = 512; private const int MaximumWardAreasExamined = 4096; private static readonly CheckAccessDelegate CheckAccess = ResolveCheckAccess(); private static readonly TranslateDelegate Translate = ResolveTranslate(); private static readonly FieldRef<Container, bool> Loading = ResolveLoading(); private static readonly List<PrivateArea> WardAreas = ResolveWardAreas(); private static readonly WardStateDelegate WardEnabled = ResolveWardState("IsEnabled"); private static readonly WardStateDelegate WardLocalAccess = ResolveWardState("HaveLocalAccess"); private static readonly WardContainsDelegate WardContains = ResolveWardContains(); private static readonly Dictionary<Container, CacheEntry> Cache = new Dictionary<Container, CacheEntry>(); private static long _configurationGeneration = 1L; private static long _accessSequence; internal static bool IsSupported { get { if (CheckAccess != null && Translate != null && Loading != null && WardAreas != null && WardEnabled != null && WardLocalAccess != null) { return WardContains != null; } return false; } } internal static void Append(Container container, ref string hoverText) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: 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_00d5: 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) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) if (!IsSupported || (Object)container == (Object)null || !HoverBaseTextPolicy.Allows(hoverText)) { return; } Player localPlayer = Player.m_localPlayer; ConfigEntry<bool> enabled = PluginConfig.Enabled; bool flag = enabled != null && enabled.Value && (PluginConfig.ShowContentsOnHover?.Value ?? false); if (!flag || (Object)localPlayer == (Object)null || !((Behaviour)container).isActiveAndEnabled || (int)container.m_privacy == 0 || IsBusy(container, null)) { return; } ZNetView val = ValheimContainerIdentity.NetworkView(container); ZDO val2 = (((Object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null); if (val2 == null || Loading.Invoke(container) || IsBusy(container, val2)) { return; } Vector3 position = ((Component)container).transform.position; Vector3 val3 = position - ((Component)localPlayer).transform.position; bool flag2 = HoverRangePolicy.IsWithinPhysicalReach(((Vector3)(ref val3)).sqrMagnitude, localPlayer.m_maxInteractDistance); if (!flag2) { return; } bool flag3 = !container.m_checkGuardStone || HasStrictWardAccess(position); if (!flag3) { return; } long playerID = localPlayer.GetPlayerID(); bool flag4; try { flag4 = CheckAccess(container, playerID); } catch { return; } if (!flag4) { return; } bool durableClaimAbsent = true; if (!HoverDisclosurePolicy.AllowsBeforeSynchronization(new HoverDisclosureFacts(flag, localPlayerAvailable: true, containerActive: true, networkObjectValid: true, nonPrivate: true, flag3, flag4, flag2, closed: true, mutationIdle: true, durableClaimAbsent, synchronized: false))) { return; } CacheEntry orCreate = GetOrCreate(container); orCreate.LastAccess = ++_accessSequence; string text = val2.GetString(ZDOVars.s_items, string.Empty); bool samePersistedEvidence; HoverPersistedEvidence expectedEvidence = CaptureEvidence(orCreate, text, out samePersistedEvidence); float realtimeSinceStartup = Time.realtimeSinceStartup; switch (HoverCachePolicy.Decide(orCreate.Verified, samePersistedEvidence, orCreate.ConfigurationGeneration, _configurationGeneration, orCreate.NextRetryAt, realtimeSinceStartup)) { case HoverCacheDecision.SuppressUntilRetry: return; case HoverCacheDecision.Refresh: if (!TryRefresh(container, val, val2, text, expectedEvidence, realtimeSinceStartup, orCreate, localPlayer, playerID)) { return; } break; } if (HoverDisclosurePolicy.Allows(new HoverDisclosureFacts(flag, localPlayerAvailable: true, containerActive: true, networkObjectValid: true, nonPrivate: true, flag3, flag4, flag2, closed: true, mutationIdle: true, durableClaimAbsent, orCreate.Verified)) && !string.IsNullOrEmpty(orCreate.Suffix)) { if (!string.Equals(orCreate.BaseHoverText, hoverText, StringComparison.Ordinal)) { orCreate.BaseHoverText = hoverText; orCreate.CombinedHoverText = hoverText + orCreate.Suffix; } hoverText = orCreate.CombinedHoverText; } } internal static void Invalidate(Container container) { if ((Object)(object)container != (Object)null) { Cache.Remove(container); } } internal static void InvalidateConfiguration() { _configurationGeneration++; if (_configurationGeneration == 0L) { _configurationGeneration = 1L; } } internal static void Reset() { Cache.Clear(); _configurationGeneration = 1L; _accessSequence = 0L; } internal static bool TryGetSynchronizedReadSnapshot(Container container, out Inventory inventory, out long revision) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown inventory = null; revision = 0L; if ((Object)container == (Object)null || Loading == null) { return false; } try { ZNetView val = ValheimContainerIdentity.NetworkView(container); ZDO val2 = (((Object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null); if (val2 == null || Loading.Invoke(container) || IsBusy(container, val2)) { return false; } string text = val2.GetString(ZDOVars.s_items, string.Empty); HoverPersistedEvidence evidence = HoverPersistedEvidence.Capture(text, PluginConfig.HoverMaximumSnapshotCharacters.Value); if (!TryGetExactInventory(container, text, evidence, out var inventory2)) { return false; } ZNetView val3 = ValheimContainerIdentity.NetworkView(container); ZDO val4 = (((Object)val3 != (Object)null && val3.IsValid()) ? val3.GetZDO() : null); string a = ((val4 != null) ? val4.GetString(ZDOVars.s_items, string.Empty) : null); if ((Object)(object)val3 != (Object)(object)val || val4 != val2 || val4 == null || Loading.Invoke(container) || IsBusy(container, val4) || !string.Equals(a, text, StringComparison.Ordinal)) { return false; } inventory = inventory2; revision = val4.DataRevision; return true; } catch { inventory = null; revision = 0L; return false; } } private static bool TryRefresh(Container container, ZNetView expectedView, ZDO expectedZdo, string expectedPersistedItems, HoverPersistedEvidence expectedEvidence, float now, CacheEntry entry, Player expectedPlayer, long expectedPlayerId) { //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Expected O, but got Unknown //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Expected O, but got Unknown //IL_025a: Expected O, but got Unknown //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Expected O, but got Unknown //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02af: Unknown result type (might be due to invalid IL or missing references) //IL_02b8: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: 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) entry.Verified = false; entry.Evidence = expectedEvidence; entry.HasEvidence = true; entry.SetPersistedReference(expectedEvidence.IsAdmissible ? expectedPersistedItems : null); entry.ConfigurationGeneration = _configurationGeneration; entry.NextRetryAt = now + Mathf.Clamp(PluginConfig.HoverRefreshIntervalSeconds.Value, 0.1f, 5f); entry.Suffix = string.Empty; entry.BaseHoverText = null; entry.CombinedHoverText = null; try { if (!expectedEvidence.IsAdmissible || !TryGetExactInventory(container, expectedPersistedItems, expectedEvidence, out var inventory)) { return false; } List<ItemData> allItems = inventory.GetAllItems(); int num = Math.Min(allItems.Count, Mathf.Clamp(PluginConfig.HoverMaximumStacksExamined.Value, 16, 1024)); int num2 = allItems.Count - num; List<HoverContentEntry> list = new List<HoverContentEntry>(num); for (int i = 0; i < num; i++) { ItemData val = allItems[i]; if (val != null && val.m_stack > 0) { string text = ValheimContainerIdentity.ResourceId(val); if (text.Length == 0 || text.Length > 256) { num2++; } else { list.Add(new HoverContentEntry(text, DisplayName(val, text), val.m_stack)); } } } string suffix = ContainerHoverSummaryFormatter.Format(list, Mathf.Clamp(PluginConfig.HoverMaximumItemKinds.Value, 1, 24), Mathf.Clamp(PluginConfig.HoverItemsPerLine.Value, 1, 4), Mathf.Clamp(PluginConfig.HoverMaximumCharacters.Value, 64, 1024), num2); ZNetView val2 = ValheimContainerIdentity.NetworkView(container); ZDO val3 = (((Object)val2 != (Object)null && val2.IsValid()) ? val2.GetZDO() : null); string text2 = ((val3 != null) ? val3.GetString(ZDOVars.s_items, string.Empty) : null); HoverPersistedEvidence hoverPersistedEvidence = HoverPersistedEvidence.Capture(text2, PluginConfig.HoverMaximumSnapshotCharacters.Value); if ((Object)(object)val2 == (Object)(object)expectedView && val3 == expectedZdo && val3 != null && hoverPersistedEvidence.Equals(expectedEvidence) && string.Equals(text2, expectedPersistedItems, StringComparison.Ordinal)) { ConfigEntry<bool> enabled = PluginConfig.Enabled; if (enabled != null && enabled.Value) { ConfigEntry<bool> showContentsOnHover = PluginConfig.ShowContentsOnHover; if (showContentsOnHover != null && showContentsOnHover.Value && !((Object)Player.m_localPlayer != (Object)expectedPlayer) && !((Object)expectedPlayer == (Object)null) && ((Behaviour)container).isActiveAndEnabled && (int)container.m_privacy != 0 && !Loading.Invoke(container) && !IsBusy(container, val3)) { Vector3 position = ((Component)container).transform.position; Vector3 val4 = position - ((Component)expectedPlayer).transform.position; if (!HoverRangePolicy.IsWithinPhysicalReach(((Vector3)(ref val4)).sqrMagnitude, expectedPlayer.m_maxInteractDistance) || (container.m_checkGuardStone && !HasStrictWardAccess(position)) || !CheckAccess(container, expectedPlayerId)) { return false; } entry.Suffix = suffix; entry.Verified = true; entry.NextRetryAt = 0f; return true; } } } return false; } catch { return false; } } private static bool TryGetExactInventory(Container container, string persisted, HoverPersistedEvidence evidence, out Inventory inventory) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown inventory = null; if ((Object)container == (Object)null || !evidence.IsAdmissible || Loading.Invoke(container)) { return false; } inventory = container.GetInventory(); if (inventory == null) { return false; } List<ItemData> allItems = inventory.GetAllItems(); if (string.IsNullOrEmpty(persisted)) { return allItems.Count == 0; } if (!HoverSnapshotBounds.AllowsEnvelope(allItems.Count, persisted.Length, PluginConfig.HoverMaximumSnapshotCharacters.Value)) { return false; } int maximumSerializedBytes = HoverSnapshotBounds.SerializedByteCeiling(PluginConfig.HoverMaximumSnapshotCharacters.Value); if (!HasBoundedSerializedShape(allItems, maximumSerializedBytes)) { return false; } ZPackage val = new ZPackage(); inventory.Save(val); if (!HoverSnapshotBounds.MatchesExactSerializedSize(val.Size(), persisted.Length, PluginConfig.HoverMaximumSnapshotCharacters.Value)) { return false; } return string.Equals(val.GetBase64(), persisted, StringComparison.Ordinal); } private static bool IsBusy(Container container, ZDO zdo) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown if ((Object)container == (Object)null || container.IsInUse() || ((Object)container.m_wagon != (Object)null && container.m_wagon.InUse())) { return true; } if (zdo != null) { return zdo.GetBool(ZDOVars.s_inUse, false); } return false; } private static bool HasBoundedSerializedShape(List<ItemData> items, int maximumSerializedBytes) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown if (items == null || items.Count > 1024 || maximumSerializedBytes <= 0) { return false; } long nextBytes = 8L; int num = 0; for (int i = 0; i < items.Count; i++) { ItemData val = items[i]; if (val == null || (Object)val.m_dropPrefab == (Object)null) { return false; } nextBytes += 64; if (!HoverSnapshotBounds.TryAddStringEstimate(nextBytes, ((Object)val.m_dropPrefab).name?.Length ?? 0, maximumSerializedBytes, out nextBytes) || !HoverSnapshotBounds.TryAddStringEstimate(nextBytes, val.m_crafterName?.Length ?? 0, maximumSerializedBytes, out nextBytes)) { return false; } Dictionary<string, string> customData = val.m_customData; int num2 = customData?.Count ?? 0; if (!HoverSnapshotBounds.AllowsCustomDataAddition(num, num2)) { return false; } num += num2; if (customData == null) { continue; } foreach (KeyValuePair<string, string> item in customData) { if (!HoverSnapshotBounds.TryAddStringEstimate(nextBytes, item.Key?.Length ?? 0, maximumSerializedBytes, out nextBytes) || !HoverSnapshotBounds.TryAddStringEstimate(nextBytes, item.Value?.Length ?? 0, maximumSerializedBytes, out nextBytes)) { return false; } } } return nextBytes <= maximumSerializedBytes; } private static bool HasStrictWardAccess(Vector3 position) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_0049: Unknown result type (might be due to invalid IL or missing references) try { int count = WardAreas.Count; if (count > 4096) { return false; } for (int i = 0; i < count; i++) { PrivateArea val = WardAreas[i]; if (!((Object)val == (Object)null)) { bool num = WardEnabled(val); bool flag = num && WardContains(val, position, 0f); bool localAccess = !flag || WardLocalAccess(val); if (StrictWardDisclosurePolicy.IsHostileOverlap(num, flag, localAccess)) { return false; } } } return true; } catch { return false; } } private static string DisplayName(ItemData item, string fallback) { if (!HoverLabelPolicy.TryPrepareLocalizationToken(item?.m_shared?.m_name, out var token)) { return HoverLabelPolicy.NormalizeDisplayLabel(fallback, "Item"); } try { Localization instance = Localization.instance; string key; return HoverLabelPolicy.NormalizeDisplayLabel((instance != null && HoverLabelPolicy.TryGetTranslationKey(token, out key)) ? Translate(instance, key) : HoverLabelPolicy.WithoutLocalizationMarker(token), fallback); } catch { return HoverLabelPolicy.NormalizeDisplayLabel(HoverLabelPolicy.WithoutLocalizationMarker(token), fallback); } } private static HoverPersistedEvidence CaptureEvidence(CacheEntry entry, string persisted, out bool samePersistedEvidence) { if (persisted == null) { persisted = string.Empty; } string persisted2 = null; if (entry.HasEvidence && entry.TryGetPersistedReference(out persisted2) && (object)persisted2 == persisted) { samePersistedEvidence = true; return entry.Evidence; } HoverPersistedEvidence hoverPersistedEvidence = HoverPersistedEvidence.Capture(persisted, PluginConfig.HoverMaximumSnapshotCharacters.Value); samePersistedEvidence = entry.HasEvidence && entry.Evidence.Equals(hoverPersistedEvidence); if (samePersistedEvidence && hoverPersistedEvidence.IsAdmissible) { samePersistedEvidence = persisted2 != null && string.Equals(persisted2, persisted, StringComparison.Ordinal); } if (samePersistedEvidence && hoverPersistedEvidence.IsAdmissible) { entry.SetPersistedReference(persisted); } return hoverPersistedEvidence; } private static CacheEntry GetOrCreate(Container container) { if (Cache.TryGetValue(container, out var value)) { return value; } if (Cache.Count >= 512) { EvictOldest(); } value = new CacheEntry(); Cache.Add(container, value); return value; } private static void EvictOldest() { Container val = null; long num = long.MaxValue; foreach (KeyValuePair<Container, CacheEntry> item in Cache) { if (item.Value.LastAccess < num) { val = item.Key; num = item.Value.LastAccess; } } if ((Object)(object)val != (Object)null) { Cache.Remove(val); } } private static CheckAccessDelegate ResolveCheckAccess() { try { MethodInfo methodInfo = AccessTools.Method(typeof(Container), "CheckAccess", new Type[1] { typeof(long) }, (Type[])null); return (methodInfo == null) ? null : AccessTools.MethodDelegate<CheckAccessDelegate>(methodInfo, (object)null, true); } catch { return null; } } private static TranslateDelegate ResolveTranslate() { try { MethodInfo methodInfo = AccessTools.Method(typeof(Localization), "Translate", new Type[1] { typeof(string) }, (Type[])null); return (methodInfo == null || methodInfo.IsStatic || methodInfo.ReturnType != typeof(string)) ? null : AccessTools.MethodDelegate<TranslateDelegate>(methodInfo, (object)null, true); } catch { return null; } } private static FieldRef<Container, bool> ResolveLoading() { try { return AccessTools.FieldRefAccess<Container, bool>("m_loading"); } catch { return null; } } private static List<PrivateArea> ResolveWardAreas() { try { return AccessTools.Field(typeof(PrivateArea), "m_allAreas")?.GetValue(null) as List<PrivateArea>; } catch { return null; } } private static WardStateDelegate ResolveWardState(string methodName) { try { MethodInfo methodInfo = AccessTools.Method(typeof(PrivateArea), methodName, Type.EmptyTypes, (Type[])null); return (methodInfo == null) ? null : AccessTools.MethodDelegate<WardStateDelegate>(methodInfo, (object)null, true); } catch { return null; } } private static WardContainsDelegate ResolveWardContains() { try { MethodInfo methodInfo = AccessTools.Method(typeof(PrivateArea), "IsInside", new Type[2] { typeof(Vector3), typeof(float) }, (Type[])null); return (methodInfo == null) ? null : AccessTools.MethodDelegate<WardContainsDelegate>(methodInfo, (object)null, true); } catch { return null; } } } internal sealed class ContainerIndex { private readonly struct CellKey : IEquatable<CellKey> { internal int X { get; } internal int Z { get; } internal CellKey(int x, int z) { X = x; Z = z; } internal static CellKey From(Vector3 value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) return new CellKey(ContainerSpatialPolicy.CellCoordinate(value.x), ContainerSpatialPolicy.CellCoordinate(value.z)); } public bool Equals(CellKey other) { if (X == other.X) { return Z == other.Z; } return false; } public override bool Equals(object obj) { if (obj is CellKey other) { return Equals(other); } return false; } public override int GetHashCode() { return (X * 397) ^ Z; } } private readonly struct Entry { internal int InstanceId { get; } internal Container Container { get; } internal Entry(int instanceId, Container container) { InstanceId = instanceId; Container = container; } } private readonly struct Membership { internal CellKey Cell { get; } internal string EndpointId { get; } internal Container Container { get; } internal Membership(CellKey cell, string endpointId, Container container) { Cell = cell; EndpointId = endpointId ?? string.Empty; Container = container; } } private readonly struct Candidate { internal SpatialCandidateKey Key { get; } internal Container Container { get; } internal Candidate(SpatialCandidateKey key, Container container) { Key = key; Container = container; } } private sealed class CandidateComparer : IComparer<Candidate> { internal static CandidateComparer Instance { get; } = new CandidateComparer(); public int Compare(Candidate left, Candidate right) { return left.Key.CompareTo(right.Key); } } private readonly object _gate = new object(); private readonly Dictionary<CellKey, List<Entry>> _cells = new Dictionary<CellKey, List<Entry>>(); private readonly Dictionary<int, Membership> _membership = new Dictionary<int, Membership>(); private readonly Dictionary<string, HashSet<int>> _endpointMembers = new Dictionary<string, HashSet<int>>(StringComparer.Ordinal); internal void Add(Container container) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)container == (Object)null) { return; } int instanceID; Vector3 position; try { instanceID = ((Object)container).GetInstanceID(); position = ((Component)container).transform.position; } catch { return; } lock (_gate) { if (!Finite(position)) { RemoveLocked(instanceID, container); return; } CellKey cellKey = CellKey.From(position); Membership value; bool num = _membership.TryGetValue(instanceID, out value); bool flag = num && value.Cell.Equals(cellKey); if (ContainerSpatialPolicy.CanRetain(exactContainerPresent: flag && _cells.TryGetValue(value.Cell, out var value2) && ContainsExact(value2, instanceID, container), membershipExists: num, cellUnchanged: flag, endpointUnchanged: !string.IsNullOrEmpty(value.EndpointId))) { return; } string endpointId; try { endpointId = ValheimContainerIdentity.EndpointId(container); } catch { return; } RemoveLocked(instanceID, container); if (ContainerSpatialPolicy.CanIndexEndpoint(endpointId)) { if (!_cells.TryGetValue(cellKey, out value2)) { value2 = new List<Entry>(); _cells.Add(cellKey, value2); } value2.Add(new Entry(instanceID, container)); _membership.Add(instanceID, new Membership(cellKey, endpointId, container)); AddEndpoint(endpointId, instanceID); } } } internal int RefreshLoadedContainers() { Container[] array; try { array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0); } catch { return 0; } for (int i = 0; i < array.Length; i++) { Add(array[i]); } return array.Length; } internal void Remove(Container container) { if (container == null) { return; } int instanceID; try { instanceID = ((Object)container).GetInstanceID(); } catch { return; } lock (_gate) { RemoveLocked(instanceID, container); } } internal IReadOnlyList<Container> Nearest(Vector3 origin, float radius, int maximum, out bool truncated) { //IL_000f: 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_005f: 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_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0077: 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_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) if (maximum <= 0) { throw new ArgumentOutOfRangeException("maximum"); } if (!Finite(origin) || float.IsNaN(radius) || float.IsInfinity(radius) || radius <= 0f) { truncated = false; return Array.Empty<Container>(); } radius = Math.Min(50f, radius); maximum = Math.Min(256, maximum); float num = radius * radius; CellKey cellKey = CellKey.From(origin - new Vector3(radius, 0f, radius)); CellKey cellKey2 = CellKey.From(origin + new Vector3(radius, 0f, radius)); SortedSet<Candidate> sortedSet = new SortedSet<Candidate>(CandidateComparer.Instance); int num2 = 0; lock (_gate) { for (int i = cellKey.X; i <= cellKey2.X; i++) { for (int j = cellKey.Z; j <= cellKey2.Z; j++) { CellKey key = new CellKey(i, j); if (!_cells.TryGetValue(key, out var value)) { continue; } for (int num3 = value.Count - 1; num3 >= 0; num3--) { Entry entry = value[num3]; Container container = entry.Container; if ((Object)(object)container == (Object)null) { value.RemoveAt(num3); RemoveIndexesLocked(entry.InstanceId); } else { try { if (((Behaviour)container).isActiveAndEnabled) { Vector3 val = ((Component)container).transform.position - origin; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (!float.IsNaN(sqrMagnitude) && !float.IsInfinity(sqrMagnitude) && !(sqrMagnitude > num) && _membership.TryGetValue(entry.InstanceId, out var value2) && ContainerSpatialPolicy.CanIndexEndpoint(value2.EndpointId) && _endpointMembers.TryGetValue(value2.EndpointId, out var value3) && ContainerSpatialPolicy.IsUniqueEndpointMemberCount(value3.Count)) { if (num2 < int.MaxValue) { num2++; } string endpointId = value2.EndpointId; sortedSet.Add(new Candidate(new SpatialCandidateKey(sqrMagnitude, endpointId, entry.InstanceId), container)); if (sortedSet.Count > maximum) { sortedSet.Remove(sortedSet.Max); } } } } catch { } } } if (value.Count == 0) { _cells.Remove(key); } } } } List<Container> list = new List<Container>(sortedSet.Count); foreach (Candidate item in sortedSet) { list.Add(item.Container); } truncated = num2 > maximum; return list.AsReadOnly(); } internal bool TryGet(string endpointId, out Container container) { container = null; if (string.IsNullOrEmpty(endpointId)) { return false; } lock (_gate) { if (!_endpointMembers.TryGetValue(endpointId, out var value) || !ContainerSpatialPolicy.IsUniqueEndpointMemberCount(value.Count)) { return false; } using HashSet<int>.Enumerator enumerator = value.GetEnumerator(); if (enumerator.MoveNext()) { int current = enumerator.Current; if (!_membership.TryGetValue(current, out var value2) || !string.Equals(value2.EndpointId, endpointId, StringComparison.Ordinal) || (Object)(object)value2.Container == (Object)null) { RemoveIndexesLocked(current); return false; } container = value2.Container; return true; } } return false; } private void RemoveLocked(int instanceId, Container container) { if (!_membership.TryGetValue(instanceId, out var value)) { return; } if (_cells.TryGetValue(value.Cell, out var value2)) { for (int num = value2.Count - 1; num >= 0; num--) { if (value2[num].InstanceId == instanceId || value2[num].Container == container) { value2.RemoveAt(num); } } if (value2.Count == 0) { _cells.Remove(value.Cell); } } RemoveIndexesLocked(instanceId); } private void RemoveIndexesLocked(int instanceId) { if (!_membership.TryGetValue(instanceId, out var value)) { return; } _membership.Remove(instanceId); if (!string.IsNullOrEmpty(value.EndpointId) && _endpointMembers.TryGetValue(value.EndpointId, out var value2)) { value2.Remove(instanceId); if (value2.Count == 0) { _endpointMembers.Remove(value.EndpointId); } } } private void AddEndpoint(string endpointId, int instanceId) { if (!string.IsNullOrEmpty(endpointId)) { if (!_endpointMembers.TryGetValue(endpointId, out var value)) { value = new HashSet<int>(); _endpointMembers.Add(endpointId, value); } value.Add(instanceId); } } private static bool ContainsExact(List<Entry> entries, int instanceId, Container container) { for (int i = 0; i < entries.Count; i++) { if (entries[i].InstanceId == instanceId && entries[i].Container == container) { return true; } } return false; } private static bool Finite(Vector3 value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (!float.IsNaN(value.x) && !float.IsInfinity(value.x) && !float.IsNaN(value.y) && !float.IsInfinity(value.y) && !float.IsNaN(value.z)) { return !float.IsInfinity(value.z); } return false; } } internal static class ValheimContainerIdentity { internal unsafe static string EndpointId(Container container) { //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) if ((Object)(object)container == (Object)null) { return string.Empty; } ZNetView val = NetworkView(container); ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null); if (val2 != null) { ZDOID uid = val2.m_uid; return "valheim.zdo:" + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString(); } return string.Empty; } internal static ZNetView NetworkView(Container container) { if ((Object)(object)container == (Object)null) { return null; } if (!((Object)(object)container.m_rootObjectOverride != (Object)null)) { return ((Component)container).GetComponent<ZNetView>(); } return container.m_rootObjectOverride; } internal static string TypeId(Container container) { string text = (((Object)(object)container == (Object)null) ? "container" : ((Object)((Component)container).gameObject).name); if (text.EndsWith("(Clone)", StringComparison.Ordinal)) { text = text.Substring(0, text.Length - "(Clone)".Length); } if (!string.IsNullOrWhiteSpace(text)) { return text.Trim(); } return "container"; } internal static string ResourceId(ItemData item) { if (item == null) { return string.Empty; } if ((Object)(object)item.m_dropPrefab != (Object)null && !string.IsNullOrWhiteSpace(((Object)item.m_dropPrefab).name)) { string text = ((Object)item.m_dropPrefab).name; if (text.EndsWith("(Clone)", StringComparison.Ordinal)) { text = text.Substring(0, text.Length - "(Clone)".Length); } return text.Trim(); } if (item.m_shared != null && !string.IsNullOrWhiteSpace(item.m_shared.m_name)) { return item.m_shared.m_name.Trim(); } return string.Empty; } } [HarmonyPatch(typeof(GameCamera), "UpdateMouseCapture")] internal static class StorageSearchCursorLeasePatch { [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix() { StorageSearchPanel.RenewCursorLease(); } } [HarmonyPatch(typeof(Player), "TakeInput")] internal static class StorageSearchInputGatePatch { [HarmonyAfter(new string[] { "chazman.RunicBuildCamera" })] private static void Postfix(Player __instance, ref bool __result) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer && Plugin.SearchPanelOpen) { __result = false; } } } [HarmonyPatch(typeof(Container), "Awake")] internal static class ContainerAwakePatch { private static void Postfix(Container __instance) { ContainerHoverContents.Invalidate(__instance); Plugin.Index?.Add(__instance); } } [HarmonyPatch(typeof(Container), "OnDestroyed")] internal static class ContainerDestroyPatch { private static void Postfix(Container __instance, bool __runOriginal) { if (__runOriginal) { ContainerHoverContents.Invalidate(__instance); Plugin.Index?.Remove(__instance); } } } [HarmonyPatch(typeof(Container), "CheckForChanges")] internal static class ContainerSpatialRefreshPatch { private static void Postfix(Container __instance) { Plugin.Index?.Add(__instance); } } [HarmonyPatch(typeof(Container), "GetHoverText", new Type[] { })] internal static class ContainerHoverTextPatch { [HarmonyPriority(0)] private static void Postfix(Container __instance, ref string __result) { try { ContainerHoverContents.Append(__instance, ref __result); } catch { } } } internal sealed class ControllerBindingState { internal string Signature { get; } internal string ModifierName { get; } internal string QuickStackName { get; } internal string RestockName { get; } internal string SortName { get; } internal string ConsolidateName { get; } internal string SearchName { get; } internal ButtonDef Modifier { get; } internal ButtonDef QuickStack { get; } internal ButtonDef Restock { get; } internal ButtonDef Sort { get; } internal ButtonDef Consolidate { get; } internal ButtonDef Search { get; } internal bool ModifierValid => Modifier != null; internal bool QuickStackValid => QuickStack != null; internal bool RestockValid => Restock != null; internal bool SortValid => Sort != null; internal bool ConsolidateValid => Consolidate != null; internal bool SearchValid => Search != null; internal int ValidRouteCount => (QuickStackValid ? 1 : 0) + (RestockValid ? 1 : 0) + (SortValid ? 1 : 0) + (ConsolidateValid ? 1 : 0) + (SearchValid ? 1 : 0); internal ControllerBindingState(string signature, string modifierName, string quickStackName, string restockName, string sortName, string consolidateName, string searchName, ButtonDef modifier, ButtonDef quickStack, ButtonDef restock, ButtonDef sort, ButtonDef consolidate, ButtonDef search) { Signature = signature; ModifierName = modifierName; QuickStackName = quickStackName; RestockName = restockName; SortName = sortName; ConsolidateName = consolidateName; SearchName = searchName; Modifier = modifier; QuickStack = quickStack; Restock = restock; Sort = sort; Consolidate = consolidate; Search = search; } } internal static class StorageControllerCollisionGuard { private static StorageActionEdges _pendingEdge; private static ButtonDef _latchedModifier; private static ControllerBindingState _sessionBindings; private static ControllerSessionPaths _sessionPaths; private static int _releasedFrame = -1; private static int _capturedFrame = -1; private static int _lastProbeFrame = -1; internal static StorageActionEdges ObserveAndConsume(ControllerBindingState bindings, StorageRouteContext context) { Observe(bindings, context); return ConsumePendingEdge(); } internal static StorageActionEdges ConsumePendingEdge() { StorageActionEdges pendingEdge = _pendingEdge; _pendingEdge = StorageActionEdges.None; return pendingEdge; } internal static bool ShouldSuppress(string buttonName) { //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Invalid comparison between Unknown and I4 ConfigEntry<bool> enabled = PluginConfig.Enabled; if (enabled == null || !enabled.Value) { return false; } UpdateReleaseState(); ConfigEntry<bool> controllerShortcuts = PluginConfig.ControllerShortcuts; if (controllerShortcuts != null && controllerShortcuts.Value && ZInput.instance != null) { bool flag; if (_latchedModifier != null) { flag = _latchedModifier.Held; } else { string text = (PluginConfig.ControllerModifier?.Value ?? string.Empty).Trim(); ButtonDef val = ((text.Length == 0) ? null : ZInput.instance.GetButtonDef(text)); flag = val != null && val.Held; } if (flag && _lastProbeFrame != Time.frameCount) { _lastProbeFrame = Time.frameCount; Observe(_sessionBindings ?? StorageControllerBindings.Resolve(), Plugin.CaptureRouteContext()); } } if (_latchedModifier == null || ZInput.instance == null) { return false; } ButtonDef buttonDef = ZInput.instance.GetButtonDef(buttonName); if (buttonDef == null || (int)buttonDef.Source != 180) { return false; } string actionPath = buttonDef.GetActionPath(true); if (actionPath != null) { return _sessionPaths.Contains(actionPath); } return false; } internal static void Reset() { _pendingEdge = StorageActionEdges.None; _latchedModifier = null; _sessionBindings = null; _sessionPaths = default(ControllerSessionPaths); _releasedFrame = -1; _capturedFrame = -1; _lastProbeFrame = -1; } private static void Observe(ControllerBindingState bindings, StorageRouteContext context) { UpdateReleaseState(); if (bindings == null || !bindings.ModifierValid || !bindings.Modifier.Held || _capturedFrame == Time.frameCount) { return; } StorageActionEdges storageActionEdges = StorageActionEdges.None; if (bindings.SortValid && bindings.Sort.Pressed) { storageActionEdges = StorageActionEdges.ControllerSort; } else if (bindings.QuickStackValid && bindings.QuickStack.Pressed) { storageActionEdges = StorageActionEdges.ControllerQuickStack; } else if (bindings.RestockValid && bindings.Restock.Pressed) { storageActionEdges = StorageActionEdges.ControllerRestock; } else if (bindings.ConsolidateValid && bindings.Consolidate.Pressed) { storageActionEdges = StorageActionEdges.ControllerConsolidate; } else if (bindings.SearchValid && bindings.Search.Pressed) { storageActionEdges = StorageActionEdges.ControllerSearch; } if (storageActionEdges == StorageActionEdges.None) { return; } _capturedFrame = Time.frameCount; _pendingEdge = storageActionEdges; StorageActionRequest request = StorageActionRouter.Select(storageActionEdges); StorageRouteDecision decision = StorageActionRouter.Route(request, context); bool flag = _latchedModifier != null; if (ControllerChordSessionPolicy.Decide(flag, decision) == ControllerChordDisposition.ReportWithoutConsume) { return; } if (!flag) { StartSession(bindings); } _releasedFrame = -1; if (PluginConfig.DebugTransfers.Value) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("input-consumed source=controller action=" + StorageActionDiagnostics.ActionCode(request.Action) + " result=" + ((decision.Outcome == StorageRouteOutcome.Execute) ? "authorized" : "blocked-in-owned-session") + " modifier-session=active configured-button-aliases=suppressed-until-full-release")); } } } private static void UpdateReleaseState() { if (_latchedModifier != null) { if (_latchedModifier.Held || AnySessionPrimaryHeld()) { _releasedFrame = -1; } else if (_releasedFrame < 0) { _releasedFrame = Time.frameCount; } else if (_releasedFrame != Time.frameCount) { _latchedModifier = null; _sessionBindings = null; _sessionPaths = default(ControllerSessionPaths); _releasedFrame = -1; } } } private static void StartSession(ControllerBindingState bindings) { _latchedModifier = bindings.Modifier; _sessionBindings = bindings; _sessionPaths = new ControllerSessionPaths(Path(bindings.Modifier), Path(bindings.QuickStack), Path(bindings.Restock), Path(bindings.Sort), Path(bindings.Consolidate), Path(bindings.Search)); } private static bool AnySessionPrimaryHeld() { if (!IsHeld(_sessionBindings?.QuickStack) && !IsHeld(_sessionBindings?.Restock) && !IsHeld(_sessionBindings?.Sort) && !IsHeld(_sessionBindings?.Consolidate)) { return IsHeld(_sessionBindings?.Search); } return true; } private static bool IsHeld(ButtonDef definition) { if (definition != null) { return definition.Held; } return false; } private static string Path(ButtonDef definition) { return ((definition != null) ? definition.GetActionPath(true) : null) ?? string.Empty; } } [HarmonyPatch(typeof(ZInput), "GetButton", new Type[] { typeof(string) })] internal static class StorageControllerGetButtonPatch { [HarmonyPriority(800)] [HarmonyBefore(new string[] { "chazman.RunicAgriculture", "chazman.RunicInventory" })] private static bool Prefix(string name, ref bool __result) { return AllowOrConsume(name, ref __result); } private static bool AllowOrConsume(string name, ref bool result) { if (StorageSearchGameplayInputGuard.ShouldSuppressPrimaryAttack(name)) { result = false; return false; } if (!StorageControllerCollisionGuard.ShouldSuppress(name)) { return true; } result = false; return false; } } [HarmonyPatch(typeof(ZInput), "GetButtonDown", new Type[] { typeof(string) })] internal static class StorageControllerGetButtonDownPatch { [HarmonyPriority(800)] [HarmonyBefore(new string[] { "chazman.RunicAgriculture", "chazman.RunicInventory" })] private static bool Prefix(string name, ref bool __result) { if (StorageSearchGameplayInputGuard.ShouldSuppressPrimaryAttack(name)) { __result = false; return false; } if (!StorageControllerCollisionGuard.ShouldSuppress(name)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(ZInput), "GetButtonUp", new Type[] { typeof(string) })] internal static class StorageControllerGetButtonUpPatch { [HarmonyPriority(800)] [HarmonyBefore(new string[] { "chazman.RunicAgriculture", "chazman.RunicInventory" })] private static bool Prefix(string name, ref bool __result) { if (StorageSearchGameplayInputGuard.ShouldSuppressPrimaryAttack(name)) { __result = false; return false; } if (!StorageControllerCollisionGuard.ShouldSuppress(name)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(ZInput), "GetButtonPressedTimer", new Type[] { typeof(string) })] internal static class StorageControllerPressedTimerPatch { [HarmonyPriority(800)] [HarmonyBefore(new string[] { "chazman.RunicAgriculture", "chazman.RunicInventory" })] private static bool Prefix(string name, ref float __result) { if (StorageSearchGameplayInputGuard.ShouldSuppressPrimaryAttack(name)) { __result = 0f; return false; } if (!StorageControllerCollisionGuard.ShouldSuppress(name)) { return true; } __result = 0f; return false; } } [HarmonyPatch(typeof(ZInput), "GetButtonLastPressedTimer", new Type[] { typeof(string) })] internal static class StorageControllerLastPressedTimerPatch { [HarmonyPriority(800)] [HarmonyBefore(new string[] { "chazman.RunicAgriculture", "chazman.RunicInventory" })] private static bool Prefix(string name, ref float __result) { if (StorageSearchGameplayInputGuard.ShouldSuppressPrimaryAttack(name)) { __result = 0f; return false; } if (!StorageControllerCollisionGuard.ShouldSuppress(name)) { return true; } __result = 0f; return false; } } [HarmonyPatch(typeof(ZInput), "GetKeyDown", new Type[] { typeof(KeyCode), typeof(bool) })] internal static class StorageSearchEscapeKeyPatch { [HarmonyPriority(800)] private static bool Prefix(KeyCode key, ref bool __result) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (!StorageSearchGameplayInputGuard.ShouldSuppressEscape(key)) { return true; } __result = false; return false; } } internal sealed class RaycastHitDistanceComparer : IComparer<RaycastHit> { internal static readonly RaycastHitDistanceComparer Instance = new RaycastHitDistanceComparer(); public int Compare(RaycastHit left, RaycastHit right) { return ((RaycastHit)(ref left)).distance.CompareTo(((RaycastHit)(ref right)).distance); } } internal sealed class StorageActions { private sealed class SearchAccumulator { private readonly List<Container> _containers = new List<Container>(); internal string ResourceId { get; } internal string DisplayName { get; } internal int Quantity { get; set; } internal SearchAccumulator(string resourceId, string displayName) { ResourceId = resourceId; DisplayName = displayName; } internal void AddContainer(Container container) { if ((Object)(object)container != (Object)null && !_containers.Contains(container)) { _containers.Add(container); } } internal StorageSearchEntry ToEntry() { return new StorageSearchEntry(ResourceId, DisplayName, Quantity, _containers.AsReadOnly()); } } private static readonly FieldInfo CurrentContainerField = AccessTools.Field(typeof(InventoryGui), "m_currentContainer"); private static readonly MethodInfo InventoryChangedMethod = AccessTools.Method(typeof(Inventory), "Changed", (Type[])null, (Type[])null); private readonly ContainerIndex _index; private readonly StorageSearchPanel _searchPanel; internal StorageActions(ContainerIndex index, StorageSearchPanel searchPanel) { _index = index ?? throw new ArgumentNullException("index"); _searchPanel = searchPanel ?? throw new ArgumentNullException("searchPanel"); } internal void QuickStack() { if (!TryBeginMutation("Quick Stack", out var player, out var mutationLease)) { return; } using (mutationLease) { Inventory inventory = ((Humanoid)player).GetInventory(); IReadOnlyList<Container> readOnlyList = Nearby(player, requireWritable: true, "quick-stack"); int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = 0; int num6 = 0; int num7 = 0; int num8 = 0; Dictionary<Container, Inventory> dictionary = new Dictionary<Container, Inventory>(); HashSet<Container> hashSet = new HashSet<Container>(); List<ItemData> list = new List<ItemData>(inventory.GetAllItems()); list.Sort(CompareGridPosition); if (!TryCaptureProtection(list, out var snapshot, out var failureCode)) { Message(player, "Runic Storage: carried-item protection could not be proven; Quick Stack changed nothing."); LogAction("quick-stack", failureCode, $"stacks={list.Count} moved=0"); return; } int num9 = 0; for (int i = 0; i < list.Count; i++) { ItemData val = list[i]; StorageProtectionState storageProtectionState = snapshot.StateAt(i); if (IsProtected(player, val, storageProtectionState)) { num4++; if (storageProtectionState == StorageProtectionState.Locked) { num9++; } if (val != null && (val.m_equipped || ((Humanoid)player).IsItemEquiped(val))) { num6++; } else if (val != null && PluginConfig.ProtectHotbar.Value && val.m_gridPos.y == 0) { num5++; } continue; } string text = ValheimContainerIdentity.ResourceId(val); if (text.Length == 0) { continue; } num3++; int stack = val.m_stack; bool flag = false; foreach (Container item in readOnlyList) { if (val.m_stack <= 0) { break; } if (!dictionary.TryGetValue(item, out var value)) { if (hashSet.Contains(item)) { continue; } if (!StorageContainerAuthority.TryGetSynchronizedServerInventory(item, out value)) { hashSet.Add(item); num8++; LogTransfer(PlayerEndpointId(player), ValheimContainerIdentity.EndpointId(item), text, 0, "ownership.denied"); continue; } dictionary[item] = value; } if (ContainsResource(value, text)) { if (!flag) { num2 = AddSaturated(num2, stack); num7++; flag = true; } int stack2 = val.m_stack; int num10 = ValheimContainerService.MoveUpTo(inventory, value, val, stack2, player, mutationLease, null, item); num = AddSaturated(num, num10); LogTransfer(PlayerEndpointId(player), ValheimContainerIdentity.EndpointId(item), text, num10, (num10 > 0) ? "ok" : "destination.full"); if (num10 >= stack2) { break; } } } } QuickStackNoOpReason reason = QuickStackDiagnostics.Classify(new QuickStackObservation(list.Count, num3, num4, readOnlyList.Count, num7, num)); int num11 = Math.Max(0, num2 - num); string text2 = ((num > 0) ? $"Runic Storage: moved {num} item(s); {num11} matching item(s) remained." : QuickStackNoOpFeedback(reason, num5, num6, num9)); Message(player, text2); LogAction("quick-stack", (num > 0) ? "ok" : QuickStackDiagnostics.ReasonCode(reason), $"carriedStacks={list.Count} eligibleStacks={num3} protectedStacks={num4} " + $"hotbarProtected={num5} equippedProtected={num6} itemLockProtected={num9} " + $"authorizedContainers={readOnlyList.Count} matchedStacks={num7} " + $"ownershipDenied={num8} moved={num} remainder={num11}"); } } internal void StoreAllOpenedContainer() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown //IL_007d: Unknown result type (might be due to invalid IL or missing references) if (!TryBeginMutation("Store All", out var player, out var mutationLease)) { return; } using (mutationLease) { Container val = (Container)(((Object)InventoryGui.instance == (Object)null || CurrentContainerField == null) ? null : /*isinst with value type is only supported in some contexts*/); if ((Object)val == (Object)null) { Message(player, "Runic Storage: open a container before using Store All."); LogAction("store-all-opened-container", "ui.open-container-required", "container=false moved=0"); return; } if ((int)val.m_privacy == 0 || !ValheimContainerService.CanDiscover(val, player.GetPlayerID(), requireWritable: true, allowCurrentUse: true)) { Message(player, "Runic Storage: that container is personal, busy, or denied; Store All changed nothing."); LogAction("store-all-opened-container", "container.denied", "authorized=false moved=0"); return; } if (!StorageContainerAuthority.TryGetExactOpenedLocalOwnerInventory(val, player, out var inventory)) { Message(player, "Runic Storage: ownership changed before Store All; nothing was