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 RunicInventory v1.0.0
RunicInventory.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.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Runic.Foundation.Core; using RunicInventory.Api; using RunicInventory.Core; using RunicInventory.Integration; using UnityEngine; using UnityEngine.EventSystems; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Runic Inventory")] [assembly: AssemblyDescription("Labeled native-row equipment and quick slots with automatic empty-role equip, lossless armor swaps, locks, sort, and pickup controls for Valheim.")] [assembly: AssemblyCompany("Chazman")] [assembly: AssemblyProduct("Runic Inventory")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: InternalsVisibleTo("RunicInventory.Tests")] [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 RunicInventory { internal static class Diagnostics { private static ManualLogSource _log; internal static void Initialize(ManualLogSource log) { _log = log; } internal static void Info(string value) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)Bound(value)); } } internal static void Warn(string value) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)Bound(value)); } } internal static void Error(Exception exception, string context) { ManualLogSource log = _log; if (log != null) { log.LogError((object)(Bound(context) + " " + ((exception == null) ? "unknown" : (exception.GetType().Name + ": " + Bound(exception.Message))))); } } internal static void Trace(string value) { ConfigEntry<bool> verboseDiagnostics = InventoryConfig.VerboseDiagnostics; if (verboseDiagnostics != null && verboseDiagnostics.Value) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)Bound(value)); } } } private static string Bound(string value) { string text = value ?? string.Empty; if (text.Length > 512) { return text.Substring(0, 512); } return text; } } internal static class InventoryConfig { internal static ConfigEntry<bool> Enabled { get; private set; } internal static ConfigEntry<bool> ShowInventoryStatus { get; private set; } internal static ConfigEntry<bool> ShowRoleLabels { get; private set; } internal static ConfigEntry<bool> ShowPickupPreview { get; private set; } internal static ConfigEntry<string> FilteredPickupItems { get; private set; } internal static ConfigEntry<string> SortRows { get; private set; } internal static ConfigEntry<KeyboardShortcut> Quick1 { get; private set; } internal static ConfigEntry<KeyboardShortcut> Quick2 { get; private set; } internal static ConfigEntry<KeyboardShortcut> Quick3 { get; private set; } internal static ConfigEntry<KeyboardShortcut> Sort { get; private set; } internal static ConfigEntry<KeyboardShortcut> ToggleLock { get; private set; } internal static ConfigEntry<bool> ControllerEnabled { get; private set; } internal static ConfigEntry<string> ControllerModifier { get; private set; } internal static ConfigEntry<string> ControllerQuick1 { get; private set; } internal static ConfigEntry<string> ControllerQuick2 { get; private set; } internal static ConfigEntry<string> ControllerQuick3 { get; private set; } internal static ConfigEntry<string> ControllerSort { get; private set; } internal static ConfigEntry<string> ControllerToggleLock { get; private set; } internal static ConfigEntry<bool> VerboseDiagnostics { get; private set; } internal static void Bind(ConfigFile config) { //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) Enabled = config.Bind<bool>("General", "Enabled", true, "Enable the validated native-row topology. Disabling never removes or serializes an item."); ShowInventoryStatus = config.Bind<bool>("UI", "ShowInventoryStatus", false, "Show the bounded topology/authority panel while the player inventory is open."); ShowRoleLabels = config.Bind<bool>("UI", "ShowRoleLabels", true, "Label and subtly outline the eight native bottom-row equipment and quick slots while inventory is open."); ShowPickupPreview = config.Bind<bool>("UI", "ShowPickupPreview", true, "Append a bounded local capacity/weight preview to nearby world-item hover text."); FilteredPickupItems = config.Bind<string>("Pickup Filter", "Items", string.Empty, "Exact comma/semicolon/newline-separated prefab IDs or shared-name tokens to refuse before pickup mutation; maximum 128 safe entries. Quest items always bypass the filter."); SortRows = config.Bind<string>("Sort", "Rows", "1,2", "Zero-based general rows eligible for regional sort. Row 0 and the bottom special row are always rejected. Empty means every proven general row."); Quick1 = config.Bind<KeyboardShortcut>("Keyboard", "UseQuickSlot1", new KeyboardShortcut((KeyCode)49, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Manually use quick slot 1 for the owning local player, including a dedicated-server client."); Quick2 = config.Bind<KeyboardShortcut>("Keyboard", "UseQuickSlot2", new KeyboardShortcut((KeyCode)50, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Manually use quick slot 2 for the owning local player, including a dedicated-server client."); Quick3 = config.Bind<KeyboardShortcut>("Keyboard", "UseQuickSlot3", new KeyboardShortcut((KeyCode)51, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Manually use quick slot 3 for the owning local player, including a dedicated-server client."); Sort = config.Bind<KeyboardShortcut>("Keyboard", "SortSelectedRows", new KeyboardShortcut((KeyCode)105, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Sort only configured safe general rows while the inventory is open."); ToggleLock = config.Bind<KeyboardShortcut>("Keyboard", "ToggleFocusedSlotLock", new KeyboardShortcut((KeyCode)108, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Optional keyboard fallback for slot locking. The primary gesture is Left Alt + right-click on a player slot."); ControllerEnabled = config.Bind<bool>("Controller", "Enabled", true, "Enable raw, effective-path-validated controller chords."); ControllerModifier = config.Bind<string>("Controller", "ModifierAction", "JoyAltKeys", "Existing Valheim Joy* action held as the modifier."); ControllerQuick1 = config.Bind<string>("Controller", "UseQuickSlot1Action", "JoyMap", "Existing gamepad action pressed with ModifierAction to use quick slot 1. The exact untouched legacy 1.0.0 controller set is read as JoyMap without rewriting the file."); ControllerQuick2 = config.Bind<string>("Controller", "UseQuickSlot2Action", "JoyButtonY", "Existing gamepad action pressed with ModifierAction to use quick slot 2."); ControllerQuick3 = config.Bind<string>("Controller", "UseQuickSlot3Action", "JoyRBumper", "Existing gamepad action pressed with ModifierAction to use quick slot 3."); ControllerSort = config.Bind<string>("Controller", "SortSelectedRowsAction", "JoyButtonA", "Existing gamepad action pressed with ModifierAction to sort while inventory is open."); ControllerToggleLock = config.Bind<string>("Controller", "ToggleFocusedSlotLockAction", "JoyButtonB", "Existing gamepad action pressed with ModifierAction to toggle the focused slot lock."); VerboseDiagnostics = config.Bind<bool>("Diagnostics", "Verbose", false, "Log bounded reason codes and control routes; never log inventory contents or player metadata."); } } [BepInPlugin("chazman.RunicInventory", "Runic Inventory", "1.0.0")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "chazman.RunicInventory"; public const string Name = "Runic Inventory"; public const string Version = "1.0.0"; public const string ModuleId = "runic.inventory"; public const string ProtocolVersion = "1.0"; private readonly List<KeybindingRegistration> _bindings = new List<KeybindingRegistration>(); private readonly KeybindingConflictRegistry _keybindings = new KeybindingConflictRegistry(); private Harmony _harmony; private InventoryRuntime _runtime; private bool _configurationSubscribed; private bool _inputLayoutSubscribed; private bool _shuttingDown; internal static Plugin Instance { get; private set; } internal static bool RuntimeReady { get; private set; } internal InventoryRuntime Runtime => _runtime; private void Awake() { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown Instance = this; Diagnostics.Initialize(((BaseUnityPlugin)this).Logger); try { InventoryConfig.Bind(((BaseUnityPlugin)this).Config); ((BaseUnityPlugin)this).Config.SettingChanged += OnSettingChanged; _configurationSubscribed = true; ZInput.OnInputLayoutChanged += OnInputLayoutChanged; _inputLayoutSubscribed = true; if (!ValheimContracts.Initialize(out var problem)) { throw new MissingMethodException(problem); } _runtime = new InventoryRuntime(batch: false); _harmony = new Harmony("chazman.RunicInventory"); _harmony.PatchAll(typeof(Plugin).Assembly); RegisterBindings(); _runtime.Initialize(); InventoryIntegrationApi.Attach(_runtime); RuntimeReady = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Runic Inventory v1.0.0 ready. Native owner-local topology, locks, sort, quick use, equipment relocation, saves, and tombstones remain Valheim-owned."); if (Application.isBatchMode) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Batch transport detected: only an exact owning local Player can activate Inventory; a true dedicated server remains inert."); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Runic Inventory startup failed: " + ex)); ShutdownRuntime(); } } private void Update() { if (!RuntimeReady || _runtime == null) { return; } try { _runtime.Tick(); } catch (Exception exception) { Diagnostics.Error(exception, "Inventory runtime faulted and was disabled."); _runtime.FailClosed("runtime.exception"); } } private void OnGUI() { if (!RuntimeReady || _runtime == null) { return; } try { _runtime.Draw(); } catch (Exception exception) { Diagnostics.Error(exception, "Inventory status drawing failed closed."); } } private void OnSettingChanged(object sender, SettingChangedEventArgs arguments) { if (!RuntimeReady || _runtime == null) { return; } try { _runtime.OnConfigurationChanged(); } catch (Exception exception) { Diagnostics.Error(exception, "Inventory configuration refresh failed closed."); _runtime.FailClosed("config.refresh-failed"); } try { RefreshBindings(); } catch (Exception exception2) { Diagnostics.Error(exception2, "Inventory keybinding refresh failed."); } } private void OnInputLayoutChanged() { ControllerChordSession.Reset(); Diagnostics.Trace("Controller input layout changed; Inventory bindings will be re-resolved."); } private void OnDestroy() { ShutdownRuntime(); Diagnostics.Initialize(null); Instance = null; } private void RegisterBindings() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) ConfigEntry<bool> enabled = InventoryConfig.Enabled; if (enabled != null && enabled.Value) { RegisterKeyboard("quick-1", "Use quick slot 1", InventoryConfig.Quick1.Value, "gameplay"); RegisterKeyboard("quick-2", "Use quick slot 2", InventoryConfig.Quick2.Value, "gameplay"); RegisterKeyboard("quick-3", "Use quick slot 3", InventoryConfig.Quick3.Value, "gameplay"); RegisterKeyboard("sort", "Sort selected safe inventory rows", InventoryConfig.Sort.Value, "inventory"); RegisterKeyboard("lock", "Toggle focused inventory slot lock", InventoryConfig.ToggleLock.Value, "inventory"); ConfigEntry<bool> controllerEnabled = InventoryConfig.ControllerEnabled; if (controllerEnabled != null && controllerEnabled.Value) { string modifier = BoundControllerAction(InventoryConfig.ControllerModifier.Value); string quick = BoundControllerAction(InventoryConfig.ControllerQuick1.Value); string text = BoundControllerAction(InventoryConfig.ControllerQuick2.Value); string text2 = BoundControllerAction(InventoryConfig.ControllerQuick3.Value); string text3 = BoundControllerAction(InventoryConfig.ControllerSort.Value); string text4 = BoundControllerAction(InventoryConfig.ControllerToggleLock.Value); quick = ControllerBindingPolicy.EffectiveQuick1Action(modifier, quick, text, text2, text3, text4, out var _); RegisterController("controller-quick-1", "Use quick slot 1", quick, modifier, "gameplay"); RegisterController("controller-quick-2", "Use quick slot 2", text, modifier, "gameplay"); RegisterController("controller-quick-3", "Use quick slot 3", text2, modifier, "gameplay"); RegisterController("controller-sort", "Sort selected safe inventory rows", text3, modifier, "inventory"); RegisterController("controller-lock", "Toggle focused inventory slot lock", text4, modifier, "inventory"); } } } private void RefreshBindings() { DisposeBindings(); try { RegisterBindings(); } catch { DisposeBindings(); throw; } } private void DisposeBindings() { for (int num = _bindings.Count - 1; num >= 0; num--) { try { _bindings[num].Dispose(); } catch (Exception exception) { Diagnostics.Error(exception, "Inventory keybinding cleanup failed."); } } _bindings.Clear(); } private unsafe void RegisterKeyboard(string id, string display, KeyboardShortcut shortcut, string context) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0) { return; } List<string> list = new List<string>(); foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers) { if (list.Count >= 8) { throw new InvalidOperationException("A keyboard chord may contain at most eight modifiers."); } list.Add(((object)(*(KeyCode*)(&modifier))/*cast due to .constrained prefix*/).ToString()); } _bindings.Add(_keybindings.Register(new KeybindingDescriptor("runic.inventory", id, display, new InputChord("keyboard", ((object)((KeyboardShortcut)(ref shortcut)).MainKey/*cast due to .constrained prefix*/).ToString(), list), context))); } private void RegisterController(string id, string display, string action, string modifier, string context) { if (action.Length == 0 || modifier.Length == 0) { return; } try { _bindings.Add(_keybindings.Register(new KeybindingDescriptor("runic.inventory", id, display, new InputChord("controller", action, new string[1] { modifier }), context))); } catch (ArgumentException) { Diagnostics.Warn("Inventory skipped invalid controller route: " + id + "."); } } private static string BoundControllerAction(string value) { string text = value ?? string.Empty; if (text.Length == 0 || text.Length > 64) { return string.Empty; } string text2 = text.Trim(); if (!text2.StartsWith("Joy", StringComparison.Ordinal)) { return string.Empty; } for (int i = 0; i < text2.Length; i++) { if (!char.IsLetterOrDigit(text2[i]) && text2[i] != '_' && text2[i] != '-') { return string.Empty; } } return text2; } private void ShutdownRuntime() { if (_shuttingDown) { return; } _shuttingDown = true; RuntimeReady = false; InventoryRuntime runtime = _runtime; InventoryIntegrationApi.Detach(runtime); if (_configurationSubscribed) { ((BaseUnityPlugin)this).Config.SettingChanged -= OnSettingChanged; _configurationSubscribed = false; } if (_inputLayoutSubscribed) { ZInput.OnInputLayoutChanged -= OnInputLayoutChanged; _inputLayoutSubscribed = false; } ControllerChordSession.Reset(); DisposeBindings(); try { runtime?.Dispose(); } catch (Exception exception) { Diagnostics.Error(exception, "Inventory runtime cleanup was incomplete."); } _runtime = null; try { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch (Exception exception2) { Diagnostics.Error(exception2, "Inventory Harmony cleanup was incomplete."); } _harmony = null; } } } namespace RunicInventory.Integration { internal sealed class ControllerBindingState { internal const int DefinitionCount = 6; internal ButtonDef Modifier { get; } internal ButtonDef Quick1 { get; } internal ButtonDef Quick2 { get; } internal ButtonDef Quick3 { get; } internal ButtonDef Sort { get; } internal ButtonDef ToggleLock { get; } internal string ReasonCode { get; } internal bool LegacyQuick1Mapped { get; } internal int ValidRouteCount { get; } internal bool Ready { get { if (Modifier != null) { return ValidRouteCount > 0; } return false; } } internal bool AllValid { get { if (Modifier != null) { return ValidRouteCount == 5; } return false; } } internal ControllerBindingState(ButtonDef modifier, ButtonDef quick1, ButtonDef quick2, ButtonDef quick3, ButtonDef sort, ButtonDef toggleLock, string reasonCode, bool legacyQuick1Mapped) { Modifier = modifier; Quick1 = quick1; Quick2 = quick2; Quick3 = quick3; Sort = sort; ToggleLock = toggleLock; ReasonCode = reasonCode ?? "controller.invalid"; LegacyQuick1Mapped = legacyQuick1Mapped; ValidRouteCount = ((quick1 != null) ? 1 : 0) + ((quick2 != null) ? 1 : 0) + ((quick3 != null) ? 1 : 0) + ((sort != null) ? 1 : 0) + ((toggleLock != null) ? 1 : 0); } internal ButtonDef Definition(int index) { return (ButtonDef)(index switch { 0 => Modifier, 1 => Quick1, 2 => Quick2, 3 => Quick3, 4 => Sort, 5 => ToggleLock, _ => throw new ArgumentOutOfRangeException("index"), }); } } internal static class ControllerBindings { private static ZInput _instance; private static ControllerBindingState _cached; internal static ControllerBindingState Resolve() { if (_cached != null && _instance == ZInput.instance) { return _cached; } string text = Name(InventoryConfig.ControllerModifier?.Value); string quick = Name(InventoryConfig.ControllerQuick1?.Value); string text2 = Name(InventoryConfig.ControllerQuick2?.Value); string text3 = Name(InventoryConfig.ControllerQuick3?.Value); string text4 = Name(InventoryConfig.ControllerSort?.Value); string text5 = Name(InventoryConfig.ControllerToggleLock?.Value); quick = ControllerBindingPolicy.EffectiveQuick1Action(text, quick, text2, text3, text4, text5, out var legacyMapped); _instance = ZInput.instance; if (_instance == null) { return _cached = new ControllerBindingState(null, null, null, null, null, null, "controller.zinput-unavailable", legacyMapped); } string path; ButtonDef val = ResolveAction(text, out path); string[] array = new string[5] { quick, text2, text3, text4, text5 }; string[] array2 = new string[5]; ButtonDef[] array3 = (ButtonDef[])(object)new ButtonDef[5]; for (int i = 0; i < 5; i++) { array3[i] = ResolveAction(array[i], out array2[i]); } int num = ControllerBindingPolicy.ValidRouteMask(text, path, array, array2); for (int j = 0; j < 5; j++) { if (!ControllerBindingPolicy.RouteIsValid(num, j)) { array3[j] = null; } } string reasonCode = ((val == null) ? "controller.modifier-invalid" : (num switch { 0 => "controller.no-valid-routes", 31 => "ok", _ => "controller.one-or-more-routes-invalid", })); return _cached = new ControllerBindingState(val, array3[0], array3[1], array3[2], array3[3], array3[4], reasonCode, legacyMapped); } internal static void Invalidate() { _instance = null; _cached = null; } internal static bool ShouldReserveAction(string action) { //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Invalid comparison between Unknown and I4 ConfigEntry<bool> controllerEnabled = InventoryConfig.ControllerEnabled; if (controllerEnabled == null || !controllerEnabled.Value || string.IsNullOrEmpty(action) || ZInput.instance == null) { return false; } ControllerBindingState controllerBindingState = Resolve(); if (controllerBindingState.Ready && controllerBindingState.Modifier.Held) { ButtonDef quick = controllerBindingState.Quick1; if (quick == null || !quick.Pressed) { ButtonDef quick2 = controllerBindingState.Quick2; if (quick2 == null || !quick2.Pressed) { ButtonDef quick3 = controllerBindingState.Quick3; if (quick3 == null || !quick3.Pressed) { ButtonDef sort = controllerBindingState.Sort; if (sort == null || !sort.Pressed) { ButtonDef toggleLock = controllerBindingState.ToggleLock; if (toggleLock == null || !toggleLock.Pressed) { goto IL_00a3; } } } } } ButtonDef buttonDef; try { buttonDef = ZInput.instance.GetButtonDef(action); } catch (Exception) { return false; } if (buttonDef == null || (int)buttonDef.Source != 180) { return false; } string actionPath; try { actionPath = buttonDef.GetActionPath(true); } catch (Exception) { return false; } if (!SamePath(actionPath, controllerBindingState.Modifier) && !SamePath(actionPath, controllerBindingState.Quick1) && !SamePath(actionPath, controllerBindingState.Quick2) && !SamePath(actionPath, controllerBindingState.Quick3) && !SamePath(actionPath, controllerBindingState.Sort)) { return SamePath(actionPath, controllerBindingState.ToggleLock); } return true; } goto IL_00a3; IL_00a3: return false; } private static ButtonDef ResolveAction(string action, out string path) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Invalid comparison between Unknown and I4 path = string.Empty; if (action.Length == 0 || action.Length > 64 || !action.StartsWith("Joy", StringComparison.Ordinal)) { return null; } ButtonDef buttonDef; try { buttonDef = ZInput.instance.GetButtonDef(action); } catch (Exception) { return null; } if (buttonDef == null || (int)buttonDef.Source != 180) { return null; } try { path = buttonDef.GetActionPath(true)?.Trim() ?? string.Empty; } catch (Exception) { return null; } if (path.Length != 0) { return buttonDef; } return null; } private static string Name(string value) { string text = value ?? string.Empty; if (text.Length == 0 || text.Length > 64) { return string.Empty; } string text2 = text.Trim(); for (int i = 0; i < text2.Length; i++) { if (!char.IsLetterOrDigit(text2[i]) && text2[i] != '_' && text2[i] != '-') { return string.Empty; } } return text2; } private static bool SamePath(string path, ButtonDef definition) { if (string.IsNullOrEmpty(path) || definition == null) { return false; } try { return string.Equals(path, definition.GetActionPath(true), StringComparison.Ordinal); } catch (Exception) { return false; } } } internal static class KeyboardInput { private static readonly KeyCode[] ModifierKeys; internal static bool ShortcutDown(KeyboardShortcut shortcut) { //IL_0002: 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) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: 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) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0 || !ZInput.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey, false)) { return false; } int num = 0; int num2 = 0; foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers) { if (++num2 > 8) { return false; } if ((int)modifier == 0 || !ZInput.GetKey(modifier, false)) { return false; } int num3 = ModifierIndex(modifier); if (num3 >= 0) { num |= 1 << num3; } } for (int i = 0; i < ModifierKeys.Length; i++) { if ((num & (1 << i)) == 0 && ZInput.GetKey(ModifierKeys[i], false)) { return false; } } return true; } internal static bool ShouldReserveVanillaAction(string action) { //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) KeyCode val = (KeyCode)(action switch { "Hotbar1" => 49, "Hotbar2" => 50, "Hotbar3" => 51, "Hotbar4" => 52, "Hotbar5" => 53, "Hotbar6" => 54, "Hotbar7" => 55, "Hotbar8" => 56, _ => 0, }); if ((int)val == 0) { return false; } if (!IsChordFor(val, InventoryConfig.Quick1.Value) && !IsChordFor(val, InventoryConfig.Quick2.Value)) { return IsChordFor(val, InventoryConfig.Quick3.Value); } return true; } private static bool IsChordFor(KeyCode key, KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (((KeyboardShortcut)(ref shortcut)).MainKey == key) { return ShortcutDown(shortcut); } return false; } private static int ModifierIndex(KeyCode value) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between I4 and Unknown for (int i = 0; i < ModifierKeys.Length; i++) { if ((int)ModifierKeys[i] == (int)value) { return i; } } return -1; } static KeyboardInput() { KeyCode[] array = new KeyCode[8]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); ModifierKeys = (KeyCode[])(object)array; } } internal static class InputReservation { internal static bool ShouldSuppress(string action) { InventoryRuntime inventoryRuntime = Plugin.Instance?.Runtime; if (!Plugin.RuntimeReady || inventoryRuntime == null || !inventoryRuntime.AcceptsInput) { return false; } if (!ControllerChordSession.ShouldSuppress(action) && !ControllerBindings.ShouldReserveAction(action)) { return KeyboardInput.ShouldReserveVanillaAction(action); } return true; } } internal static class ControllerInput { private static ControllerBindingState _loggedBindings; internal static void Tick(InventoryRuntime runtime, bool inventoryVisible, bool gameplayInput) { if (runtime == null) { return; } ConfigEntry<bool> controllerEnabled = InventoryConfig.ControllerEnabled; if (controllerEnabled == null || !controllerEnabled.Value || ZInput.instance == null) { return; } ControllerBindingState controllerBindingState = ControllerBindings.Resolve(); if (_loggedBindings != controllerBindingState) { _loggedBindings = controllerBindingState; if (controllerBindingState.AllValid) { Diagnostics.Info(controllerBindingState.LegacyQuick1Mapped ? "Inventory controller chords validated by six unique effective gamepad paths; the exact legacy default Quick 1 route is using JoyMap." : "Inventory controller chords validated by six unique effective gamepad paths."); } else if (controllerBindingState.Ready) { Diagnostics.Warn("Inventory controller chords partially available (" + controllerBindingState.ValidRouteCount + "/5 routes): " + controllerBindingState.ReasonCode + "."); } else { Diagnostics.Warn("Inventory controller chords disabled: " + controllerBindingState.ReasonCode + "."); } } bool active = ControllerChordSession.Active; if (!controllerBindingState.Ready || active || !controllerBindingState.Modifier.Held) { return; } if (inventoryVisible) { ButtonDef toggleLock = controllerBindingState.ToggleLock; if (toggleLock != null && toggleLock.Pressed) { ControllerChordSession.Begin(controllerBindingState); runtime.ToggleFocusedLock("controller"); return; } } if (inventoryVisible) { ButtonDef sort = controllerBindingState.Sort; if (sort != null && sort.Pressed) { ControllerChordSession.Begin(controllerBindingState); runtime.SortSelectedRows("controller"); return; } } if (!gameplayInput) { return; } ButtonDef quick = controllerBindingState.Quick1; if (quick != null && quick.Pressed) { ControllerChordSession.Begin(controllerBindingState); runtime.UseQuick(InventoryRoleKind.Quick1, "controller"); return; } ButtonDef quick2 = controllerBindingState.Quick2; if (quick2 != null && quick2.Pressed) { ControllerChordSession.Begin(controllerBindingState); runtime.UseQuick(InventoryRoleKind.Quick2, "controller"); return; } ButtonDef quick3 = controllerBindingState.Quick3; if (quick3 != null && quick3.Pressed) { ControllerChordSession.Begin(controllerBindingState); runtime.UseQuick(InventoryRoleKind.Quick3, "controller"); } } internal static void ResetLog() { _loggedBindings = null; } } internal static class ControllerChordSession { private static ControllerBindingState _bindings; private static readonly HashSet<string> Paths = new HashSet<string>(StringComparer.Ordinal); private static int _releasedFrame = -1; internal static bool Active { get { UpdateRelease(); return _bindings != null; } } internal static void Begin(ControllerBindingState bindings) { if (bindings == null || !bindings.Ready || _bindings != null) { return; } _bindings = bindings; Paths.Clear(); for (int i = 0; i < 6; i++) { ButtonDef obj = bindings.Definition(i); string text = ((obj != null) ? obj.GetActionPath(true) : null); if (!string.IsNullOrEmpty(text)) { Paths.Add(text); } } _releasedFrame = -1; } internal static bool ShouldSuppress(string action) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Invalid comparison between Unknown and I4 UpdateRelease(); if (_bindings == null || ZInput.instance == null || string.IsNullOrEmpty(action)) { return false; } ButtonDef buttonDef; try { buttonDef = ZInput.instance.GetButtonDef(action); } catch (Exception) { return false; } if (buttonDef == null || (int)buttonDef.Source != 180) { return false; } string actionPath; try { actionPath = buttonDef.GetActionPath(true); } catch (Exception) { return false; } if (!string.IsNullOrEmpty(actionPath)) { return Paths.Contains(actionPath); } return false; } internal static void Reset() { _bindings = null; Paths.Clear(); _releasedFrame = -1; ControllerBindings.Invalidate(); ControllerInput.ResetLog(); } private static void UpdateRelease() { if (_bindings == null) { return; } bool flag = false; for (int i = 0; i < 6; i++) { ButtonDef obj = _bindings.Definition(i); if (obj != null && obj.Held) { flag = true; break; } } if (flag) { _releasedFrame = -1; } else if (_releasedFrame < 0) { _releasedFrame = Time.frameCount; } else if (_releasedFrame != Time.frameCount) { _bindings = null; Paths.Clear(); _releasedFrame = -1; } } } internal static class HarmonyOrderIds { internal const string Interaction = "chazman.RunicInteraction"; internal const string Storage = "chazman.RunicStorage"; internal const string Agriculture = "chazman.RunicAgriculture"; } [HarmonyPatch(typeof(Player), "SetLocalPlayer", new Type[] { })] internal static class LocalPlayerPatch { private static void Postfix(Player __instance) { try { Plugin.Instance?.Runtime?.OnLocalPlayerChanged(__instance); } catch (Exception exception) { Diagnostics.Error(exception, "Local-player topology bind failed closed."); } } } [HarmonyPatch(typeof(Player), "Load", new Type[] { typeof(ZPackage) })] internal static class PlayerLoadPatch { private static void Prefix(Player __instance) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } try { Plugin.Instance?.Runtime?.OnPlayerLoadStarted(__instance); } catch (Exception exception) { Diagnostics.Error(exception, "Player-load topology scope failed closed."); } } private static void Postfix(Player __instance) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } try { Plugin.Instance?.Runtime?.OnPlayerLoadCompleted(__instance); } catch (Exception exception) { Diagnostics.Error(exception, "Loaded player topology bind failed closed."); } } private static Exception Finalizer(Player __instance, Exception __exception) { if (__exception == null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return __exception; } try { Plugin.Instance?.Runtime?.OnPlayerLoadFaulted(__instance); } catch (Exception exception) { Diagnostics.Error(exception, "Faulted player-load topology cleanup failed closed."); } return __exception; } } [HarmonyPatch(typeof(Inventory), "FindEmptySlot", new Type[] { typeof(bool) })] internal static class FindEmptySlotPatch { private static bool Prefix(Inventory __instance, bool __0, ref Vector2i __result) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) InventoryRuntime inventoryRuntime = Plugin.Instance?.Runtime; if (inventoryRuntime == null) { return true; } try { if (!inventoryRuntime.TryFindEmptySlot(__instance, __0, out var result)) { return true; } __result = result; return false; } catch (Exception exception) { Diagnostics.Error(exception, "Special-row empty-slot routing faulted; vanilla routing won for this call."); return true; } } } [HarmonyPatch(typeof(Inventory), "FindFreeStackItem", new Type[] { typeof(string), typeof(int), typeof(float) })] internal static class FindFreeStackPatch { private static void Postfix(Inventory __instance, string __0, int __1, float __2, ref ItemData __result) { try { Plugin.Instance?.Runtime?.ReplaceLockedFreeStack(__instance, __0, __1, __2, ref __result); } catch (Exception exception) { Diagnostics.Error(exception, "Locked stack routing faulted; automatic stacking was declined for this call."); __result = null; } } } [HarmonyPatch(typeof(Inventory), "CanAddItem", new Type[] { typeof(ItemData), typeof(int) })] internal static class CanAddItemPatch { private static void Postfix(Inventory __instance, ItemData __0, int __1, ref bool __result) { try { Plugin.Instance?.Runtime?.AdjustCanAddItem(__instance, __0, __1, ref __result); } catch (Exception exception) { Diagnostics.Error(exception, "Safe carrying-capacity validation faulted; capacity was declined."); __result = false; } } } [HarmonyPatch(typeof(InventoryGrid), "DropItem", new Type[] { typeof(Inventory), typeof(ItemData), typeof(int), typeof(Vector2i) })] internal static class InventoryGridDropPatch { private static bool Prefix(InventoryGrid __instance, Inventory __0, ItemData __1, int __2, Vector2i __3, ref bool __result) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) InventoryRuntime inventoryRuntime = Plugin.Instance?.Runtime; if (inventoryRuntime == null) { return true; } try { if (inventoryRuntime.AllowGridDrop(__instance.GetInventory(), __0, __1, __2, __3)) { return true; } __result = false; return false; } catch (Exception exception) { Diagnostics.Error(exception, "Inventory grid validation faulted; the requested move was declined."); __result = false; return false; } } private static void Postfix(InventoryGrid __instance, Vector2i __3, bool __result) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) try { Plugin.Instance?.Runtime?.AfterGridDrop(__instance.GetInventory(), __3, __result); } catch (Exception exception) { Diagnostics.Error(exception, "Equipment-role post-drop handling failed closed."); } } } [HarmonyPatch(typeof(InventoryGui), "OnSelectedItem", new Type[] { typeof(InventoryGrid), typeof(ItemData), typeof(Vector2i), typeof(Modifier) })] internal static class InventorySelectedActionPatch { [HarmonyPriority(800)] [HarmonyBefore(new string[] { "chazman.RunicInteraction" })] private static bool Prefix(InventoryGrid __0, ItemData __1, Modifier __3) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) try { return Plugin.Instance?.Runtime?.AllowSelectedAction(__0, __1, __3) ?? true; } catch (Exception exception) { Diagnostics.Error(exception, "Locked-slot selection validation faulted; the destructive/move action was declined."); return false; } } } [HarmonyPatch(typeof(InventoryGrid), "OnRightClick", new Type[] { typeof(UIInputHandler) })] internal static class InventoryGridRightClickLockPatch { [HarmonyPriority(800)] private static bool Prefix(InventoryGrid __instance) { try { return Plugin.Instance?.Runtime?.TryTogglePointerLock(__instance) != true; } catch (Exception exception) { Diagnostics.Error(exception, "Alt-right-click slot lock failed closed."); return false; } } } [HarmonyPatch(typeof(Humanoid), "DropItem", new Type[] { typeof(Inventory), typeof(ItemData), typeof(int) })] internal static class HumanoidDropItemPatch { private static bool Prefix(Humanoid __instance, Inventory __0, ItemData __1, ref bool __result) { try { if (Plugin.Instance?.Runtime?.AllowItemAction(__instance, __0, __1, "dropping it") ?? true) { return true; } __result = false; return false; } catch (Exception exception) { Diagnostics.Error(exception, "Locked-item drop validation faulted; the drop was declined."); __result = false; return false; } } } [HarmonyPatch(typeof(Humanoid), "UseItem", new Type[] { typeof(Inventory), typeof(ItemData), typeof(bool) })] internal static class HumanoidUseItemPatch { private static bool Prefix(Humanoid __instance, Inventory __0, ItemData __1) { try { return Plugin.Instance?.Runtime?.AllowItemAction(__instance, __0, __1, "using it") ?? true; } catch (Exception exception) { Diagnostics.Error(exception, "Locked-item use validation faulted; the use was declined."); return false; } } } [HarmonyPatch(typeof(Humanoid), "EquipItem", new Type[] { typeof(ItemData), typeof(bool) })] internal static class HumanoidEquipItemPatch { [HarmonyPriority(800)] [HarmonyBefore(new string[] { "chazman.RunicInteraction" })] private static bool Prefix(Humanoid __instance, ItemData __0, ref bool __result, ref bool __state) { try { InventoryRuntime inventoryRuntime = Plugin.Instance?.Runtime; if (inventoryRuntime == null || inventoryRuntime.AllowEquip(__instance, __0)) { __state = inventoryRuntime?.BeginEquipmentTransition(__instance, __0) ?? false; return true; } __result = false; return false; } catch (Exception exception) { Diagnostics.Error(exception, "Locked equipment-target validation faulted; replacement was declined."); __result = false; return false; } } [HarmonyPriority(0)] [HarmonyAfter(new string[] { "chazman.RunicInteraction" })] private static void Postfix(Humanoid __instance, ItemData __0, bool __result) { try { Plugin.Instance?.Runtime?.OnEquipped(__instance, __0, __result); } catch (Exception exception) { Diagnostics.Error(exception, "Equipment-role relocation failed closed."); } } private static Exception Finalizer(bool __state, Exception __exception) { try { Plugin.Instance?.Runtime?.EndEquipmentTransition(__state, __exception); } catch (Exception exception) { Diagnostics.Error(exception, "Equipment transition cleanup failed closed."); try { Plugin.Instance?.Runtime?.FailClosed("equipment.transition-cleanup-faulted"); } catch (Exception exception2) { Diagnostics.Error(exception2, "Equipment transition fail-closed publication faulted."); } } return __exception; } } [HarmonyPatch(typeof(Humanoid), "Pickup", new Type[] { typeof(GameObject), typeof(bool), typeof(bool) })] internal static class PickupFilterPatch { [HarmonyPriority(800)] [HarmonyBefore(new string[] { "chazman.RunicInteraction" })] private static bool Prefix(Humanoid __instance, GameObject __0, ref bool __result, ref EquipmentAdditionState __state) { try { InventoryRuntime inventoryRuntime = Plugin.Instance?.Runtime; if (inventoryRuntime == null || inventoryRuntime.AllowPickup(__instance, __0)) { __state = inventoryRuntime?.BeginEquipmentAddition(__instance, (!Object.op_Implicit((Object)(object)__0)) ? null : __0.GetComponent<ItemDrop>()?.m_itemData); return true; } __result = false; return false; } catch (Exception exception) { Diagnostics.Error(exception, "Pickup filter faulted; vanilla pickup handling won."); return true; } } private static void Postfix(bool __result, EquipmentAdditionState __state) { try { Plugin.Instance?.Runtime?.CompleteEquipmentAddition(__state, __result); } catch (Exception exception) { Diagnostics.Error(exception, "Picked-up equipment placement failed closed."); } } } [HarmonyPatch(typeof(ItemDrop), "GetHoverText", new Type[] { })] internal static class ItemDropHoverPatch { private static void Postfix(ItemDrop __instance, ref string __result) { try { Plugin.Instance?.Runtime?.AppendPickupPreview(__instance, ref __result); } catch (Exception exception) { Diagnostics.Error(exception, "Pickup preview failed closed; vanilla hover text was retained."); } } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting", new Type[] { typeof(Player) })] internal static class InventoryCraftingPatch { [HarmonyPriority(800)] [HarmonyBefore(new string[] { "chazman.RunicCrafting" })] private static bool Prefix(InventoryGui __instance, ref EquipmentAdditionState __state) { try { InventoryRuntime inventoryRuntime = Plugin.Instance?.Runtime; int num; if (inventoryRuntime == null) { num = 1; } else { num = (inventoryRuntime.AllowCrafting(__instance) ? 1 : 0); if (num == 0) { goto IL_0036; } } __state = inventoryRuntime?.BeginEquipmentAddition((Humanoid)(object)Player.m_localPlayer); goto IL_0036; IL_0036: return (byte)num != 0; } catch (Exception exception) { Diagnostics.Error(exception, "Locked upgrade/crafting selection validation faulted; the action was declined."); return false; } } private static void Postfix(EquipmentAdditionState __state) { try { Plugin.Instance?.Runtime?.CompleteEquipmentAddition(__state, succeeded: true); } catch (Exception exception) { Diagnostics.Error(exception, "Crafted equipment placement failed closed."); } } } [HarmonyPatch(typeof(InventoryGui), "RepairOneItem")] internal static class InventoryRepairAllowancePatch { [HarmonyPriority(800)] [HarmonyBefore(new string[] { "chazman.RunicCrafting" })] private static void Prefix(ref bool __state) { try { __state = Plugin.Instance?.Runtime?.BeginRepairAllowance() == true; } catch (Exception exception) { __state = false; Diagnostics.Error(exception, "Repair protection allowance could not start; vanilla repair remains available."); } } private static Exception Finalizer(bool __state, Exception __exception) { try { Plugin.Instance?.Runtime?.EndRepairAllowance(__state); } catch (Exception exception) { Diagnostics.Error(exception, "Repair protection allowance cleanup faulted."); } return __exception; } } internal static class StationLockGuard { internal static bool Allow(Humanoid actor, ItemData item, string action, ref bool result, string nativeBoundary = null) { try { if (Plugin.Instance?.Runtime?.AllowStationItem(actor, item, action) ?? true) { return true; } result = false; return false; } catch (Exception exception) { Diagnostics.Error(exception, "Locked-item station validation faulted; the station action was declined."); result = false; return false; } } } [HarmonyPatch(typeof(Smelter), "OnAddOre", new Type[] { typeof(Switch), typeof(Humanoid), typeof(ItemData) })] internal static class SmelterOreLockPatch { [HarmonyPriority(0)] [HarmonyAfter(new string[] { "chazman.RunicSafety" })] private static bool Prefix(Humanoid __1, ItemData __2, ref bool __result) { return StationLockGuard.Allow(__1, __2, "processing it", ref __result, "Smelter.OnAddOre"); } } [HarmonyPatch(typeof(Smelter), "OnAddFuel", new Type[] { typeof(Switch), typeof(Humanoid), typeof(ItemData) })] internal static class SmelterFuelLockPatch { [HarmonyPriority(0)] [HarmonyAfter(new string[] { "chazman.RunicSafety" })] private static bool Prefix(Humanoid __1, ItemData __2, ref bool __result) { return StationLockGuard.Allow(__1, __2, "processing it", ref __result, "Smelter.OnAddFuel"); } } [HarmonyPatch(typeof(CookingStation), "CookItem", new Type[] { typeof(Humanoid), typeof(ItemData) })] internal static class CookingItemLockPatch { private static bool Prefix(Humanoid __0, ItemData __1, ref bool __result) { return StationLockGuard.Allow(__0, __1, "cooking it", ref __result, "CookingStation.CookItem"); } } [HarmonyPatch(typeof(CookingStation), "OnAddFuelSwitch", new Type[] { typeof(Switch), typeof(Humanoid), typeof(ItemData) })] internal static class CookingFuelLockPatch { [HarmonyPriority(400)] [HarmonyAfter(new string[] { "chazman.RunicSafety" })] [HarmonyBefore(new string[] { "chazman.RunicProduction" })] private static bool Prefix(Humanoid __1, ItemData __2, ref bool __result) { return StationLockGuard.Allow(__1, __2, "processing it", ref __result, "CookingStation.OnAddFuelSwitch"); } } [HarmonyPatch(typeof(Fermenter), "AddItem", new Type[] { typeof(Humanoid), typeof(ItemData) })] internal static class FermenterLockPatch { [HarmonyPriority(400)] [HarmonyAfter(new string[] { "chazman.RunicSafety" })] [HarmonyBefore(new string[] { "chazman.RunicProduction" })] private static bool Prefix(Humanoid __0, ItemData __1, ref bool __result) { return StationLockGuard.Allow(__0, __1, "fermenting it", ref __result); } } [HarmonyPatch(typeof(Incinerator), "OnIncinerate", new Type[] { typeof(Switch), typeof(Humanoid), typeof(ItemData) })] internal static class IncineratorLockPatch { [HarmonyPriority(0)] [HarmonyAfter(new string[] { "chazman.RunicSafety" })] private static bool Prefix(Humanoid __1, ItemData __2, ref bool __result) { return StationLockGuard.Allow(__1, __2, "incinerating it", ref __result); } } [HarmonyPatch(typeof(ItemStand), "UseItem", new Type[] { typeof(Humanoid), typeof(ItemData) })] internal static class ItemStandLockPatch { [HarmonyPriority(0)] [HarmonyAfter(new string[] { "chazman.RunicSafety" })] private static bool Prefix(Humanoid __0, ItemData __1, ref bool __result) { return StationLockGuard.Allow(__0, __1, "displaying it", ref __result, "ItemStand.UseItem"); } } [HarmonyPatch(typeof(OfferingBowl), "UseItem", new Type[] { typeof(Humanoid), typeof(ItemData) })] internal static class OfferingLockPatch { private static bool Prefix(Humanoid __0, ItemData __1, ref bool __result) { return StationLockGuard.Allow(__0, __1, "sacrificing it", ref __result, "OfferingBowl.UseItem"); } } [HarmonyPatch(typeof(ShieldGenerator), "OnAddFuel", new Type[] { typeof(Switch), typeof(Humanoid), typeof(ItemData) })] internal static class ShieldFuelLockPatch { private static bool Prefix(Humanoid __1, ItemData __2, ref bool __result) { return StationLockGuard.Allow(__1, __2, "processing it", ref __result, "ShieldGenerator.OnAddFuel"); } } [HarmonyPatch(typeof(ZInput), "GetButton", new Type[] { typeof(string) })] internal static class ControllerGetButtonPatch { [HarmonyPriority(0)] [HarmonyAfter(new string[] { "chazman.RunicStorage", "chazman.RunicAgriculture" })] private static bool Prefix(string name, ref bool __result) { return Suppress(name, ref __result); } private static bool Suppress(string name, ref bool result) { if (!InputReservation.ShouldSuppress(name)) { return true; } result = false; return false; } } [HarmonyPatch(typeof(ZInput), "GetButtonDown", new Type[] { typeof(string) })] internal static class ControllerGetButtonDownPatch { [HarmonyPriority(0)] [HarmonyAfter(new string[] { "chazman.RunicStorage", "chazman.RunicAgriculture" })] private static bool Prefix(string name, ref bool __result) { if (!InputReservation.ShouldSuppress(name)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(ZInput), "GetButtonUp", new Type[] { typeof(string) })] internal static class ControllerGetButtonUpPatch { [HarmonyPriority(0)] [HarmonyAfter(new string[] { "chazman.RunicStorage", "chazman.RunicAgriculture" })] private static bool Prefix(string name, ref bool __result) { if (!InputReservation.ShouldSuppress(name)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(ZInput), "GetButtonPressedTimer", new Type[] { typeof(string) })] internal static class ControllerPressedTimerPatch { [HarmonyPriority(0)] [HarmonyAfter(new string[] { "chazman.RunicStorage", "chazman.RunicAgriculture" })] private static bool Prefix(string name, ref float __result) { if (!InputReservation.ShouldSuppress(name)) { return true; } __result = 0f; return false; } } [HarmonyPatch(typeof(ZInput), "GetButtonLastPressedTimer", new Type[] { typeof(string) })] internal static class ControllerLastPressedTimerPatch { [HarmonyPriority(0)] [HarmonyAfter(new string[] { "chazman.RunicStorage", "chazman.RunicAgriculture" })] private static bool Prefix(string name, ref float __result) { if (!InputReservation.ShouldSuppress(name)) { return true; } __result = 0f; return false; } } internal sealed class ItemMutationEvidence { internal ItemData Item { get; } internal Vector2i Coordinate { get; } internal string Fingerprint { get; } internal ItemMutationEvidence(ItemData item, Vector2i coordinate, string fingerprint) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) Item = item; Coordinate = coordinate; Fingerprint = fingerprint; } } internal static class InventoryEvidence { private sealed class ReferenceComparer<T> : IEqualityComparer<T> where T : class { internal static readonly ReferenceComparer<T> Instance = new ReferenceComparer<T>(); public bool Equals(T x, T y) { return x == y; } public int GetHashCode(T obj) { return RuntimeHelpers.GetHashCode(obj); } } internal const int MaximumCustomEntriesPerItem = 64; internal const int MaximumCustomStringCharacters = 4096; internal const int MaximumAggregateCustomCharacters = 65536; internal const int MaximumSerializedBytes = 1048576; internal static bool TryCaptureMutation(Inventory inventory, out IReadOnlyList<ItemMutationEvidence> evidence, out string reasonCode) { //IL_00ee: Unknown result type (might be due to invalid IL or missing references) evidence = Array.Empty<ItemMutationEvidence>(); if (inventory == null) { reasonCode = "evidence.inventory-null"; return false; } List<ItemData> allItems = inventory.GetAllItems(); if (allItems == null || allItems.Count > 128) { reasonCode = "evidence.item-bound-exceeded"; return false; } HashSet<int> hashSet = new HashSet<int>(); List<ItemMutationEvidence> list = new List<ItemMutationEvidence>(allItems.Count); int aggregateCustom = 0; foreach (ItemData item in allItems) { if (item == null || item.m_gridPos.x < 0 || item.m_gridPos.x >= inventory.GetWidth() || item.m_gridPos.y < 0 || item.m_gridPos.y >= inventory.GetHeight() || !hashSet.Add(item.m_gridPos.y * inventory.GetWidth() + item.m_gridPos.x) || !TryFingerprint(item, includePosition: false, ref aggregateCustom, out var fingerprint)) { reasonCode = "evidence.item-invalid"; return false; } list.Add(new ItemMutationEvidence(item, item.m_gridPos, fingerprint)); } evidence = list.AsReadOnly(); reasonCode = "ok"; return true; } internal static bool VerifyUnchangedExceptPosition(Inventory inventory, IReadOnlyList<ItemMutationEvidence> before, out string reasonCode) { if (inventory == null || before == null || inventory.GetAllItems().Count != before.Count) { reasonCode = "evidence.item-count-changed"; return false; } HashSet<ItemData> hashSet = new HashSet<ItemData>(ReferenceComparer<ItemData>.Instance); foreach (ItemData allItem in inventory.GetAllItems()) { hashSet.Add(allItem); } HashSet<int> hashSet2 = new HashSet<int>(); int aggregateCustom = 0; foreach (ItemMutationEvidence item2 in before) { ItemData item = item2.Item; if (!hashSet.Contains(item) || item.m_gridPos.x < 0 || item.m_gridPos.x >= inventory.GetWidth() || item.m_gridPos.y < 0 || item.m_gridPos.y >= inventory.GetHeight() || !hashSet2.Add(item.m_gridPos.y * inventory.GetWidth() + item.m_gridPos.x) || !TryFingerprint(item, includePosition: false, ref aggregateCustom, out var fingerprint) || !string.Equals(fingerprint, item2.Fingerprint, StringComparison.Ordinal)) { reasonCode = "evidence.metadata-or-membership-changed"; return false; } } reasonCode = "ok"; return true; } internal static void RestorePositions(IReadOnlyList<ItemMutationEvidence> evidence) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (evidence == null) { return; } foreach (ItemMutationEvidence item in evidence) { if (item?.Item != null) { item.Item.m_gridPos = item.Coordinate; } } } internal static bool TryFingerprint(ItemData item, bool includePosition, out string fingerprint) { int aggregateCustom = 0; return TryFingerprint(item, includePosition, ref aggregateCustom, out fingerprint); } internal static bool TryDeterministicSave(Inventory inventory, out string reasonCode) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown try { ZPackage val = new ZPackage(); inventory.Save(val); byte[] array = val.GetArray(); if (array == null || array.Length > 1048576) { reasonCode = "serialization.payload-bound"; return false; } ZPackage val2 = new ZPackage(); inventory.Save(val2); byte[] array2 = val2.GetArray(); if (array2 == null || array2.Length != array.Length) { reasonCode = "serialization.nondeterministic"; return false; } int num = 0; for (int i = 0; i < array.Length; i++) { num |= array[i] ^ array2[i]; } reasonCode = ((num == 0) ? "ok" : "serialization.nondeterministic"); return num == 0; } catch (Exception) { reasonCode = "serialization.failed"; return false; } } internal static string HashTopology(string value) { using SHA256 sHA = SHA256.Create(); byte[] array = sHA.ComputeHash(Encoding.UTF8.GetBytes(value ?? string.Empty)); StringBuilder stringBuilder = new StringBuilder(64); byte[] array2 = array; foreach (byte b in array2) { stringBuilder.Append(b.ToString("x2", CultureInfo.InvariantCulture)); } return stringBuilder.ToString(); } private static bool TryFingerprint(ItemData item, bool includePosition, ref int aggregateCustom, out string fingerprint) { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected I4, but got Unknown fingerprint = string.Empty; if (item == null || item.m_shared == null || item.m_stack <= 0) { return false; } string text = ValheimContracts.PrefabId(item); string text2 = item.m_shared.m_name ?? string.Empty; string text3 = item.m_crafterName ?? string.Empty; if (text.Length == 0 || text2.Length > 256 || text3.Length > 256) { return false; } StringBuilder stringBuilder = new StringBuilder(512); Append(stringBuilder, text); Append(stringBuilder, text2); stringBuilder.Append('|').Append((int)item.m_shared.m_itemType).Append('|') .Append(item.m_stack) .Append('|') .Append(BitConverter.SingleToInt32Bits(item.m_durability)) .Append('|') .Append(item.m_equipped ? 1 : 0) .Append('|') .Append(item.m_quality) .Append('|') .Append(item.m_variant) .Append('|') .Append(item.m_crafterID) .Append('|') .Append(item.m_worldLevel) .Append('|') .Append(item.m_pickedUp ? 1 : 0); Append(stringBuilder, text3); if (includePosition) { stringBuilder.Append('|').Append(item.m_gridPos.x).Append('|') .Append(item.m_gridPos.y); } int num = item.m_customData?.Count ?? 0; if (num > 64) { return false; } List<string> list = new List<string>(num); if (item.m_customData != null) { foreach (KeyValuePair<string, string> customDatum in item.m_customData) { string text4 = customDatum.Key ?? string.Empty; string text5 = customDatum.Value ?? string.Empty; if (text4.Length > 4096 || text5.Length > 4096) { return false; } aggregateCustom += text4.Length + text5.Length; if (aggregateCustom > 65536) { return false; } list.Add(text4); } list.Sort(StringComparer.Ordinal); foreach (string item2 in list) { Append(stringBuilder, item2); Append(stringBuilder, item.m_customData[item2] ?? string.Empty); } } fingerprint = HashTopology(stringBuilder.ToString()); return true; } private static void Append(StringBuilder builder, string value) { builder.Append('|').Append(value.Length).Append(':') .Append(value); } } internal sealed class EquipmentAdditionState { internal Player Player { get; } internal Inventory Inventory { get; } internal InventoryRoleKind? ExpectedRole { get; } internal IReadOnlyList<ItemData> Before { get; } internal EquipmentAdditionState(Player player, Inventory inventory, InventoryRoleKind? expectedRole, IReadOnlyList<ItemData> before) { Player = player; Inventory = inventory; ExpectedRole = expectedRole; Before = before; } } internal readonly struct StackCapacityKey : IEquatable<StackCapacityKey> { internal string SharedName { get; } internal int Quality { get; } internal int WorldLevel { get; } internal StackCapacityKey(string sharedName, int quality, int worldLevel) { SharedName = sharedName ?? string.Empty; Quality = quality; WorldLevel = worldLevel; } public bool Equals(StackCapacityKey other) { if (Quality == other.Quality && WorldLevel == other.WorldLevel) { return string.Equals(SharedName, other.SharedName, StringComparison.Ordinal); } return false; } public override bool Equals(object obj) { if (obj is StackCapacityKey other) { return Equals(other); } return false; } public override int GetHashCode() { return (((StringComparer.Ordinal.GetHashCode(SharedName) * 397) ^ Quality) * 397) ^ WorldLevel; } } internal sealed class InventoryRuntime : IInventoryTopologyService, IInventoryProtectionService, IInventoryStatusService, IItemProtectionQuery, IDisposable { private sealed class MutationScope : IDisposable { private InventoryRuntime _owner; internal MutationScope(InventoryRuntime owner) { _owner = owner; } public void Dispose() { InventoryRuntime inventoryRuntime = Interlocked.Exchange(ref _owner, null); if (inventoryRuntime != null) { Interlocked.Exchange(ref inventoryRuntime._mutationActive, 0); } } } private const int MaximumProtectionDiagnostics = 32; private readonly bool _batch; private readonly int _mainThreadId; private readonly Dictionary<StackCapacityKey, int> _stackCapacity = new Dictionary<StackCapacityKey, int>(); private readonly HashSet<string> _protectionDiagnostics = new HashSet<string>(StringComparer.Ordinal); private readonly object _protectionDiagnosticGate = new object(); private Player _player; private Inventory _inventory; private TopologyLayout _layout; private PersistedTopologyState _persisted; private InventoryTopologySnapshot _snapshot; private PickupFilterSet _filters; private InventoryAuthorityMode _mode; private string _reasonCode = "runtime.not-initialized"; private string _statusText = "Runic Inventory: waiting for the local player."; private long _generation; private int _freePickupSlots; private float _cachedWeight; private bool _topologyActive; private bool _playerLoadInProgress; private bool _loadMetadataRefreshed; private int _equipmentTransitionDepth; private int _repairAllowanceDepth; private int _mutationActive; private bool _equipmentRefreshPending; private bool _equipmentTransitionFaulted; private bool _disableCleanupPending; private bool _rebuilding; private bool _disposed; private float _nextMessageTime; private readonly Rect[] _roleSlotRects = (Rect[])(object)new Rect[8]; private static readonly string[] RoleLabels = new string[8] { "Helmet", "Chest", "Legs", "Cape", "Utility", "Quick 1", "Quick 2", "Quick 3" }; public string ProviderId => "runic.inventory"; internal bool TopologyActive { get { if (_topologyActive && !_disposed && _mode == InventoryAuthorityMode.AuthoritativeLocal && IsAuthoritativeLocal(_player)) { return LiveDimensionsMatch(); } return false; } } internal InventoryAuthorityMode Mode => _mode; internal bool AcceptsInput => CanEnforceLocks(); internal InventoryRuntime(bool batch) { _batch = batch; _mainThreadId = Thread.CurrentThread.ManagedThreadId; _filters = PickupFilterSet.Parse(InventoryConfig.FilteredPickupItems?.Value ?? string.Empty); _mode = (batch ? InventoryAuthorityMode.BatchInert : InventoryAuthorityMode.Unavailable); _reasonCode = (batch ? "authority.batch-inert" : "player.not-loaded"); } internal void Initialize() { if (_batch) { RebuildStatusOnly(); } else { Rebind(Player.m_localPlayer, "startup"); } } internal void Tick() { //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) if (_disposed || _batch) { return; } if (_player != Player.m_localPlayer) { Rebind(Player.m_localPlayer, "local-player-changed"); } if (!Object.op_Implicit((Object)(object)_player) || _inventory == null) { return; } if (_disableCleanupPending) { ConfigEntry<bool> enabled = InventoryConfig.Enabled; if (enabled != null && enabled.Value) { _disableCleanupPending = false; Rebind(Player.m_localPlayer, "disable-cleanup-cancelled"); } else { if (!TryCompleteDisableCleanup()) { return; } Rebind(Player.m_localPlayer, "disable-cleanup-completed"); } if (!Object.op_Implicit((Object)(object)_player) || _inventory == null) { return; } } if (_layout != null && !LiveDimensionsMatch()) { FailClosed("topology.runtime-dimension-changed"); return; } bool flag = IsAuthoritativeLocal(_player); if ((_mode == InventoryAuthorityMode.AuthoritativeLocal && !flag) || (_mode == InventoryAuthorityMode.RemoteDedicatedCompatibility && flag)) { Rebind(_player, "authority-changed"); if (!Object.op_Implicit((Object)(object)_player) || _inventory == null) { return; } } if (!AcceptsInput) { return; } bool flag2 = (Object)(object)InventoryGui.instance != (Object)null && InventoryGui.IsVisible(); if (flag2) { if (KeyboardInput.ShortcutDown(InventoryConfig.Sort.Value)) { SortSelectedRows("keyboard"); } if (KeyboardInput.ShortcutDown(InventoryConfig.ToggleLock.Value)) { ToggleFocusedLock("keyboard"); } } else if (ValheimContracts.PlayerMayTakeInput(_player)) { if (KeyboardInput.ShortcutDown(InventoryConfig.Quick1.Value)) { UseQuick(InventoryRoleKind.Quick1, "keyboard"); } else if (KeyboardInput.ShortcutDown(InventoryConfig.Quick2.Value)) { UseQuick(InventoryRoleKind.Quick2, "keyboard"); } else if (KeyboardInput.ShortcutDown(InventoryConfig.Quick3.Value)) { UseQuick(InventoryRoleKind.Quick3, "keyboard"); } } ControllerInput.Tick(this, flag2, !flag2 && ValheimContracts.PlayerMayTakeInput(_player)); } internal void Draw() { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) if (_disposed || _batch || (Object)(object)InventoryGui.instance == (Object)null || !InventoryGui.IsVisible() || ValheimContracts.InventoryModalVisible()) { return; } if (TopologyActive) { ConfigEntry<bool> showRoleLabels = InventoryConfig.ShowRoleLabels; if (showRoleLabels == null || showRoleLabels.Value) { DrawRoleOverlay(); } DrawLockedSlotOverlay(); } ConfigEntry<bool> showInventoryStatus = InventoryConfig.ShowInventoryStatus; if (showInventoryStatus != null && showInventoryStatus.Value) { float num = Math.Min(500f, Math.Max(300f, (float)Screen.width - 20f)); GUI.Box(new Rect(Math.Max(10f, (float)Screen.width - num - 10f), 72f, num, 190f), _statusText); } } private void DrawRoleOverlay() { //IL_035c: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Expected O, but got Unknown //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Unknown result type (might be due to invalid IL or missing references) //IL_0334: Unknown result type (might be due to invalid IL or missing references) InventoryGrid val = InventoryGui.instance?.m_playerGrid; if (!Object.op_Implicit((Object)(object)val) || _layout == null || !ValheimContracts.TryBottomRowScreenRects(val, _layout.SpecialRow, _roleSlotRects)) { return; } Color color = GUI.color; int depth = GUI.depth; try { GUI.depth = -650; Rect val2 = _roleSlotRects[0]; Rect val3 = _roleSlotRects[_roleSlotRects.Length - 1]; Rect val4 = default(Rect); ((Rect)(ref val4))..ctor(((Rect)(ref val2)).xMin - 2f, Math.Min(((Rect)(ref val2)).yMin, ((Rect)(ref val3)).yMin) - 2f, ((Rect)(ref val3)).xMax - ((Rect)(ref val2)).xMin + 4f, Math.Max(((Rect)(ref val2)).yMax, ((Rect)(ref val3)).yMax) - Math.Min(((Rect)(ref val2)).yMin, ((Rect)(ref val3)).yMin) + 4f); GUI.color = new Color(0.7f, 0.43f, 0.13f, 0.48f); GUI.DrawTexture(new Rect(((Rect)(ref val4)).xMin, ((Rect)(ref val4)).yMin, ((Rect)(ref val4)).width, 1f), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref val4)).xMin, ((Rect)(ref val4)).yMax - 1f, ((Rect)(ref val4)).width, 1f), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref val4)).xMin, ((Rect)(ref val4)).yMin, 1f, ((Rect)(ref val4)).height), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref val4)).xMax - 1f, ((Rect)(ref val4)).yMin, 1f, ((Rect)(ref val4)).height), (Texture)(object)Texture2D.whiteTexture); GUIStyle val5 = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)7, fontStyle = (FontStyle)1, fontSize = Math.Max(8, Math.Min(11, (int)(((Rect)(ref _roleSlotRects[0])).width / 7f))), clipping = (TextClipping)1, wordWrap = false }; Rect val7 = default(Rect); for (int i = 0; i < _roleSlotRects.Length; i++) { Rect val6 = _roleSlotRects[i]; GUI.color = new Color(0f, 0f, 0f, 0.7f); GUI.DrawTexture(new Rect(((Rect)(ref val6)).x + 1f, ((Rect)(ref val6)).y + 1f, ((Rect)(ref val6)).width - 2f, 17f), (Texture)(object)Texture2D.whiteTexture); ((Rect)(ref val7))..ctor(((Rect)(ref val6)).x + 2f, ((Rect)(ref val6)).y + 1f, ((Rect)(ref val6)).width - 4f, 16f); val5.normal.textColor = new Color(0.05f, 0.03f, 0.01f, 0.92f); GUI.Label(new Rect(((Rect)(ref val7)).x + 1f, ((Rect)(ref val7)).y + 1f, ((Rect)(ref val7)).width, ((Rect)(ref val7)).height), RoleLabels[i], val5); val5.normal.textColor = new Color(1f, 0.78f, 0.34f, 0.98f); GUI.Label(val7, RoleLabels[i], val5); } } finally { GUI.color = color; GUI.depth = depth; } } private void DrawLockedSlotOverlay() { //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0055: 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) InventoryGrid val = InventoryGui.instance?.m_playerGrid; if (!Object.op_Implicit((Object)(object)val) || _layout == null || _persisted == null) { return; } Color color = GUI.color; int depth = GUI.depth; try { GUI.depth = -660; GUI.color = new Color(1f, 0.84f, 0.08f, 1f); foreach (InventorySlotCoordinate item in _persisted.LockedSlots()) { if (ValheimContracts.TrySlotScreenRect(val, item.X, item.Y, out var slot)) { DrawOutline(new Rect(((Rect)(ref slot)).xMin - 2f, ((Rect)(ref slot)).yMin - 2f, ((Rect)(ref slot)).width + 4f, ((Rect)(ref slot)).height + 4f), 4f); } } } finally { GUI.color = color; GUI.depth = depth; } } private static void DrawOutline(Rect rect, float thickness) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) GUI.DrawTexture(new Rect(((Rect)(ref rect)).xMin, ((Rect)(ref rect)).yMin, ((Rect)(ref rect)).width, thickness), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref rect)).xMin, ((Rect)(ref rect)).yMax - thickness, ((Rect)(ref rect)).width, thickness), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref rect)).xMin, ((Rect)(ref rect)).yMin, thickness, ((Rect)(ref rect)).height), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref rect)).xMax - thickness, ((Rect)(ref rect)).yMin, thickness, ((Rect)(ref rect)).height), (Texture)(object)Texture2D.whiteTexture); } internal void OnConfigurationChanged() { if (!_disposed) { if ((_filters = PickupFilterSet.Parse(InventoryConfig.FilteredPickupItems.Value)).Truncated) { Diagnostics.Warn("Pickup filter exceeded 128 safe unique rules; additional entries were ignored."); } ControllerBindings.Invalidate(); ControllerChordSession.Reset(); ConfigEntry<bool> enabled = InventoryConfig.Enabled; if (enabled != null && enabled.Value) { _disableCleanupPending = false; } Rebind(Player.m_localPlayer, "configuration-changed"); } } internal void OnLocalPlayerChanged(Player player) { Rebind(player, "set-local-player"); } internal void OnPlayerLoadStarted(Player player) { if (!_disposed && !((Object)(object)player != (Object)(object)_player) && !((Object)(object)player != (Object)(object)Player.m_localPlayer)) { _playerLoadInProgress = true; _loadMetadataRefreshed = false; } } internal void OnPlayerLoadCompleted(Player player) { if (!_disposed && !((Object)(object)player != (Object)(object)Player.m_localPlayer)) { _playerLoadInProgress = false; _loadMetadataRefreshed = false; Rebind(player, "player-load-completed"); } } internal void OnPlayerLoadFaulted(Player player) { if (!_disposed && !((Object)(object)player != (Object)(object)_player)) { _playerLoadInProgress = false; _loadMetadataRefreshed = false; FailClosed("player.load-faulted"); } } internal void FailClosed(string reasonCode) { _repairAllowanceDepth = 0; _topologyActive = false; _snapshot = null; _mode = (_batch ? InventoryAuthorityMode.BatchInert : InventoryAuthorityMode.MigrationSafeCompatibility); _reasonCode = BoundReason(reasonCode, "runtime.fail-closed"); RebuildStatusOnly(); } public bool TryCapture(long playerId, out InventoryTopologySnapshot snapshot, out string failureCode) { snapshot = null; if (_disposed) { failureCode = "provider.disposed"; return false; } if (Thread.CurrentThread.ManagedThreadId != _mainThreadId) { failureCode = "thread.main-required"; return false; } if (!Object.op_Implicit((Object)(object)_player) || playerId <= 0 || playerId != _player.GetPlayerID()) { failureCode = "player.not-local"; return false; } if (_layout != null && !LiveDimensionsMatch()) { FailClosed("topology.runtime-dimension-changed"); failureCode = "topology.runtime-dimension-changed"; return false; } bool flag = IsAuthoritativeLocal(_player); if ((_mode == InventoryAuthorityMode.AuthoritativeLocal && !flag) || (_mode == InventoryAuthorityMode.RemoteDedicatedCompatibility && flag)) { failureCode = "authority.transition-pending"; return false; } if (_snapshot == null || !_snapshot.SerializationVerified) { RebuildCache("api-capture", verifySerialization: true); if (_snapshot == null || !_snapshot.SerializationVerified) { failureCode = ((_snapshot == null) ? _reasonCode : "serialization.unverified"); return false; } } snapshot = _snapshot; failureCode = "ok"; return true; } public bool TryIsLocked(long playerId, InventorySlotCoordinate coordinate, out bool locked, out string failureCode) { locked = false; if (_disposed || Thread.CurrentThread.ManagedThreadId != _mainThreadId) { failureCode = (_disposed ? "provider.disposed" : "thread.main-required"); return false; } if (!Object.op_Implicit((Object)(object)_player) || playerId <= 0 || playerId != _player.GetPlayerID()) { failureCode = "player.not-local"; return false; } if (!CanEnforceLocks()) { failureCode = "authority.local-required"; return false; } if (_persisted == null || _layout == null || !_layout.InBounds(coordinate) || _persisted.Width != _layout.Width || _persisted.Height != _layout.Height) { failureCode = "locks.topology-unavailable"; return false; } locked = _persisted.IsLocked(coordinate.X, coordinate.Y); failureCode = "ok"; return true; } public bool TryGetProtection(object nativeItem, out ItemProtectionState state) { state = ItemProtectionState.Unknown; ItemData val = (ItemData)((nativeItem is ItemData) ? nativeItem : null); if (val == null) { TraceProtectionDecision("not-applicable.not-native-item"); return false; } if ((InventoryConfig.Enabled != null && !InventoryConfig.Enabled.Value) || _mode == InventoryAuthorityMode.Disabled) { TraceProtectionDecision("not-applicable.feature-disabled"); return false; } if (_disposed) { TraceProtectionDecision("unknown.provider-disposed"); return true; } if (_inventory == null) { TraceProtectionDecision("unknown.inventory-unavailable"); return true; } if (!Object.op_Implicit((Object)(object)_player)) { TraceProtectionDecision("unknown.player-unavailable"); return true; } if (Thread.CurrentThread.ManagedThreadId != _mainThreadId) { TraceProtectionDecision("unknown.thread-main-required"); return true; } try { List<ItemData> allItems = _inventory.GetAllItems(); switch (ItemProtectionDomain.Evaluate(allItems, val, 128)) { case ItemProtectionDomainEvidence.NotApplicable: TraceProtectionDecision("not-applicable.foreign-inventory-item"); return false; default: TraceProtectionDecision("unknown.domain-membership-indeterminate"); return true; case ItemProtectionDomainEvidence.ExactCurrentMember: { if (val.m_shared == null || val.m_stack <= 0) { TraceProtectionDecision("unknown.domain-item-shape-invalid"); return true; } if (!CanEnforceLocks()) { TraceProtectionDecision("unknown.topology-or-authority-inactive"); return true; } if (_layout == null || _persisted == null) { TraceProtectionDecision("unknown.topology-unavailable"); return true; } if (!LiveDimensionsMatch()) { TraceProtectionDecision("unknown.dimensions-live-mismatch"); return true; } if (_persisted.Width != _layout.Width || _persisted.Height != _layout.Height) { TraceProtectionDecision("unknown.dimensions-persisted-mismatch"); return true; } int x = val.m_gridPos.x; int y = val.m_gridPos.y; if (x < 0 || x >= _layout.Width || y < 0 || y >= _layout.Height) { TraceProtectionDecision("unknown.domain-item-coordinate-invalid"); return true; } ulong num = 0uL; ulong num2 = 0uL; for (int i = 0; i < allItems.Count; i++) { ItemData val2 = allItems[i]; if (val2 == null || val2.m_shared == null || val2.m_stack <= 0 || val2.m_gridPos.x < 0 || val2.m_gridPos.x >= _layout.Width || val2.m_gridPos.y < 0 || val2.m_gridPos.y >= _layout.Height) { TraceProtectionDecision("unknown.domain-member-shape-invalid"); return true; } int num3 = val2.m_gridPos.y * _layout.Width + val2.m_gridPos.x; if (num3 < 64) { ulong num4 = (ulong)(1L << num3); if ((num & num4) != 0L) { TraceProtectionDecision("unknown.domain-duplicate-coordinate"); return true; } num |= num4; } else { ulong num5 = (ulong)(1L << num3 - 64); if ((num2 & num5) != 0L) { TraceProtectionDecision("unknown.domain-duplicate-coordinate"); return true; } num2 |= num5; } } if (val.m_gridPos.x != x || val.m_gridPos.y != y) { TraceProtectionDecision("unknown.reference-item-coordinate-mutated"); return true; } List<ItemData> allItems2 = _inventory.GetAllItems(); if (!SameItemReferences(allItems, allItems2)) { TraceProtectionDecision("unknown.reference-membership-list-replaced"); return true; } if (_inventory.GetItemAt(x, y) != val) { TraceProtectionDecision("unknown.reference-coordinate-occupant-mutated"); return true; } if (!CanEnforceLocks()) { TraceProtectionDecision("unknown.topology-transition-during-proof"); return true; } if (!LiveDimensionsMatch()) { TraceProtectionDecision("unknown.dimensions-transition-during-proof"); return true; } state = ((_repairAllowanceDepth > 0) ? ItemProtectionState.Unlocked : ItemProtectionDomain.ClassifyExactMember(y == _layout.SpecialRow, _persisted.IsLocked(x, y))); return true; } } } catch (Exception exception) { state = ItemProtectionState.Unknown; TraceProtectionDecision("unknown.exception", exception); return true; } } public InventoryFeatureStatus Snapshot() { if (_disposed) { return new InventoryFeatureStatus(InventoryAuthorityMode.Unavailable, topologyActive: false, "provider.disposed"); } if (Thread.CurrentThread.ManagedThreadId != _mainThreadId) { return new InventoryFeatureStatus(InventoryAuthorityMode.Unavailable, topologyActive: false, "thread.main-required"); } if (_layout != null && !LiveDimensionsMatch()) { return new InventoryFeatureStatus(InventoryAuthorityMode.MigrationSafeCompatibility, topologyActive: false, "topology.runtime-dimension-changed"); } bool flag = IsAuthoritativeLocal(_player); if ((_mode == InventoryAuthorityMode.AuthoritativeLocal && !flag) || (_mode == InventoryAuthorityMode.RemoteDedicatedCompatibility && flag)) { return new InventoryFeatureStatus(InventoryAuthorityMode.MigrationSafeCompatibility, topologyActive: false, "authority.transition-pending"); } return new InventoryFeatureStatus(_mode, TopologyActive, _reasonCode); } internal bool TryFindEmptySlot(Inventory inventory, bool topFirst, out Vector2i result) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0