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 Stackmaster v1.1.5
plugins/Stackmaster/Stackmaster.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Stackmaster.Core; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("JStack424")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Inventory sorting and nearby-storage planning for Valheim.")] [assembly: AssemblyFileVersion("1.1.5.0")] [assembly: AssemblyInformationalVersion("1.1.5+0412f1733398506fbf6f5d1c8daf54ef92a9cdfd")] [assembly: AssemblyProduct("Stackmaster")] [assembly: AssemblyTitle("Stackmaster")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/JStack424/Stackmaster")] [assembly: AssemblyVersion("1.1.5.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [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 Stackmaster { internal static class ChestSortPreferences { private static readonly FieldInfo NetworkViewField = AccessTools.Field(typeof(Container), "m_nview"); private static readonly HashSet<string> SessionDisabled = new HashSet<string>(StringComparer.Ordinal); private static bool _storageHealthy; private static bool _failureLogged; internal static void Initialize() { SessionDisabled.Clear(); _storageHealthy = true; _failureLogged = false; } internal static bool TryGet(Container container, out string key, out bool enabled) { key = string.Empty; enabled = false; if (!TryCreateKey(container, out key)) { LogFailureOnce("Opened chest has no stable local player/world/ZDO identity; chest auto-sort was skipped."); return false; } if (SessionDisabled.Contains(key)) { return true; } if (!_storageHealthy) { return false; } try { bool num = PlayerPrefs.HasKey(key); int storedValue = ((!num) ? 1 : PlayerPrefs.GetInt(key, -1)); if (!ChestSortPreferencePolicy.TryInterpretStoredValue(num, storedValue, out enabled)) { DisableStorage("A saved chest auto-sort preference had an invalid value; chest auto-sort was disabled safely."); return false; } return true; } catch (Exception ex) { DisableStorage("Local chest auto-sort preferences could not be read; chest auto-sort was disabled safely: " + ex.GetType().Name); return false; } } internal static bool TrySet(string key, bool enabled) { if (string.IsNullOrEmpty(key) || !_storageHealthy) { return false; } if (enabled) { SessionDisabled.Remove(key); } else { SessionDisabled.Add(key); } try { if (enabled) { PlayerPrefs.DeleteKey(key); } else { PlayerPrefs.SetInt(key, 0); } PlayerPrefs.Save(); return true; } catch (Exception ex) { SessionDisabled.Add(key); DisableStorage("Local chest auto-sort preferences could not be saved; chest auto-sort was disabled safely: " + ex.GetType().Name); return false; } } internal static void Shutdown() { SessionDisabled.Clear(); _storageHealthy = false; } private static bool TryCreateKey(Container container, out string key) { key = string.Empty; if ((Object)(object)container == (Object)null || ((object)container).GetType() != typeof(Container) || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNet.instance == (Object)null || NetworkViewField == null) { return false; } object? value = NetworkViewField.GetValue(container); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); if ((Object)(object)val == (Object)null || !val.IsValid()) { return false; } ZDO zDO = val.GetZDO(); if (zDO == null || ((ZDOID)(ref zDO.m_uid)).IsNone()) { return false; } return ChestSortPreferencePolicy.TryCreateKey(Player.m_localPlayer.GetPlayerID(), ZNet.instance.GetWorldUID(), ((object)Unsafe.As<ZDOID, ZDOID>(ref zDO.m_uid)/*cast due to .constrained prefix*/).ToString(), out key); } private static void DisableStorage(string message) { _storageHealthy = false; LogFailureOnce(message); } private static void LogFailureOnce(string message) { if (!_failureLogged) { _failureLogged = true; Plugin plugin = RuntimeContext.Plugin; if (plugin != null) { plugin.Log.LogWarning((object)message); } } } } internal sealed class CompatibilityResult { internal bool IsCompatible { get; } internal string Reason { get; } internal string Diagnostics { get; } internal CompatibilityResult(bool isCompatible, string reason, string diagnostics = "") { IsCompatible = isCompatible; Reason = reason; Diagnostics = diagnostics; } } internal static class CompatibilityGate { internal static CompatibilityResult Evaluate() { List<string> list = new List<string>(); string diagnostics = DescribeRuntimeIdentity(); Type[] array = new Type[35] { typeof(InventoryGui), typeof(Container), typeof(InventoryGrid), typeof(ItemData), typeof(Vector2i), typeof(Modifier), typeof(GameObject), typeof(Transform), typeof(Requirement), typeof(Player), typeof(Hud), typeof(BuildUi), typeof(ZDOID), typeof(ZDO), typeof(Inventory), typeof(ZPackage), typeof(ZNetView), typeof(Humanoid), typeof(Game), typeof(ZNet), typeof(ZDOMan), typeof(Character), typeof(TextViewer), typeof(GameCamera), typeof(PlayerPrefs), typeof(Recipe), typeof(RequirementMode), typeof(ZInput), typeof(SplitDialog), typeof(CraftingStation), typeof(ZNetScene), typeof(PrivateArea), typeof(Vector3), typeof(ZDOVars), typeof(TextInput) }; foreach (Type type in array) { RuntimeContractValidator.RequireType(list, type); } HarmonyTargetManifest.Validate(list); RequireMethod(list, typeof(InventoryGui), "IsContainerOpen"); RequireMethod(list, typeof(InventoryGui), "UpdateRecipe", typeof(Player), typeof(float)); RequireStaticMethod(list, typeof(InventoryGui), "get_instance"); RequireMethod(list, typeof(Container), "CheckAccess", typeof(long)); RequireMethod(list, typeof(Container), "CheckForChanges"); RequireMethod(list, typeof(Container), "GetInventory"); RequireMethod(list, typeof(ZDOID), "IsNone"); RequireMethod(list, typeof(ZDO), "GetByteArray", typeof(int), typeof(byte[])); RequireMethod(list, typeof(ZDO), "get_DataRevision"); RequireMethod(list, typeof(ZDO), "get_OwnerRevision"); RequireMethod(list, typeof(Inventory), "Load", typeof(ZPackage)); RequireConstructor(list, typeof(Inventory), typeof(bool)); RequireConstructor(list, typeof(ZPackage), typeof(byte[])); RequireMethod(list, typeof(ZNetView), "HasOwner"); RequireMethod(list, typeof(Container), "IsOwner"); RequireMethod(list, typeof(Container), "IsInUse"); RequireMethod(list, typeof(Container), "SetInUse", typeof(bool)); RequireMethod(list, typeof(Container), "StackAll"); RequireMethod(list, typeof(Container), "RPC_RequestStack", typeof(long), typeof(long)); RequireMethod(list, typeof(ZDO), "GetOwner"); RequireMethod(list, typeof(ZDO), "SetOwner", typeof(long)); RequireStaticMethod(list, typeof(ZDOMan), "GetSessionID"); RequireMethod(list, typeof(ZDOMan), "GetZDO", typeof(ZDOID)); RequireMethod(list, typeof(ZDOMan), "ForceSendZDO", typeof(ZDOID)); RequireMethod(list, typeof(Character), "IsDead"); RequireMethod(list, typeof(Character), "InCutscene"); RequireMethod(list, typeof(Character), "IsTeleporting"); RequireStaticMethod(list, typeof(TextViewer), "get_instance"); RequireMethod(list, typeof(TextViewer), "IsVisible"); RequireStaticMethod(list, typeof(GameCamera), "InFreeFly"); RequireMethod(list, typeof(Inventory), "MoveItemToThis", typeof(Inventory), typeof(ItemData), typeof(int), typeof(int), typeof(int)); RequireMethod(list, typeof(Inventory), "GetWidth"); RequireMethod(list, typeof(Inventory), "GetHeight"); RequireMethod(list, typeof(Inventory), "GetTotalWeight"); RequireMethod(list, typeof(Inventory), "ContainsItem", typeof(ItemData)); RequireMethod(list, typeof(Inventory), "GetAllItems"); RequireMethod(list, typeof(Inventory), "GetItemAt", typeof(int), typeof(int)); RequireMethod(list, typeof(Inventory), "RemoveAll"); RequireMethod(list, typeof(Inventory), "CountItems", typeof(string), typeof(int), typeof(bool)); RequireMethod(list, typeof(Inventory), "RemoveItem", typeof(ItemData), typeof(int)); RequireMethod(list, typeof(Inventory), "RemoveItem", typeof(string), typeof(int), typeof(int), typeof(bool)); RequireMethod(list, typeof(Inventory), "AddItem", typeof(ItemData), typeof(int), typeof(int), typeof(int), typeof(bool)); RequireMethod(list, typeof(Player), "ConsumeResources", typeof(Requirement[]), typeof(int), typeof(int), typeof(int)); RequireMethod(list, typeof(ItemData), "Clone"); RequireMethod(list, typeof(ItemData), "GetWeight", typeof(int)); RequireMethod(list, typeof(Requirement), "GetAmount", typeof(int)); RequireMethod(list, typeof(Recipe), "GetAmount", typeof(int), typeof(int).MakeByRefType(), typeof(ItemData).MakeByRefType(), typeof(int)); RequireMethod(list, typeof(Player), "GetHoverObject"); RequireMethod(list, typeof(Player), "GetMaxCarryWeight"); RequireMethod(list, typeof(Player), "GetPlayerID"); RequireMethod(list, typeof(ZNet), "GetWorldUID"); RequireStaticMethod(list, typeof(PlayerPrefs), "HasKey", typeof(string)); RequireStaticMethod(list, typeof(PlayerPrefs), "GetInt", typeof(string), typeof(int)); RequireStaticMethod(list, typeof(PlayerPrefs), "SetInt", typeof(string), typeof(int)); RequireStaticMethod(list, typeof(PlayerPrefs), "DeleteKey", typeof(string)); RequireStaticMethod(list, typeof(PlayerPrefs), "Save"); RequireStaticMethod(list, typeof(ZInput), "ResetButtonStatus", typeof(string)); RequireMethod(list, typeof(SplitDialog), "get_IsActive"); RequireMethod(list, typeof(ZNetView), "IsOwner"); RequireMethod(list, typeof(ZNetView), "IsValid"); RequireMethod(list, typeof(ZNetView), "GetZDO"); RequireStaticMethod(list, typeof(CraftingStation), "get_Instances"); RequireMethod(list, typeof(CraftingStation), "GetStationBuildRange"); RequireMethod(list, typeof(CraftingStation), "GetLevel", typeof(bool)); RequireMethod(list, typeof(CraftingStation), "CheckUsable", typeof(Player), typeof(bool)); RequireStaticMethod(list, typeof(ZNetScene), "get_instance"); RequireMethod(list, typeof(ZNetScene), "GetPrefab", typeof(string)); RequireMethod(list, typeof(ZNetScene), "GetPrefabHash", typeof(GameObject)); RequireMethod(list, typeof(ZDO), "GetPrefab"); RequireMethod(list, typeof(ZNetView), "InvokeRPC", typeof(string), typeof(object[])); RequireStaticMethod(list, typeof(PrivateArea), "CheckAccess", typeof(Vector3), typeof(float), typeof(bool), typeof(bool)); RequireField(list, typeof(InventoryGui), "m_pvp"); RequireField(list, typeof(InventoryGui), "m_container"); RequireField(list, typeof(InventoryGui), "m_currentContainer"); RequireField(list, typeof(InventoryGui), "m_craftTimer"); RequireField(list, typeof(InventoryGui), "m_craftRecipe"); RequireField(list, typeof(InventoryGui), "m_selectedRecipe"); RequireField(list, typeof(InventoryGui), "m_reqList"); RequireField(list, typeof(InventoryGui), "m_craftUpgradeItem"); RequireField(list, typeof(InventoryGui), "m_selectedVariant"); RequireField(list, typeof(InventoryGui), "m_craftVariant"); RequireField(list, typeof(InventoryGui), "m_touchMultiCrafting"); Type type2 = AccessTools.Inner(typeof(InventoryGui), "RecipeDataPair"); if (type2 == null) { list.Add("InventoryGui.RecipeDataPair missing"); } else { RequireProperty(list, type2, "Recipe"); RequireProperty(list, type2, "ItemData"); } RequireField(list, typeof(InventoryGui), "m_multiCrafting"); RequireField(list, typeof(InventoryGui), "m_multiCraftAmount"); RequireField(list, typeof(InventoryGui), "m_dragItem"); RequireField(list, typeof(InventoryGui), "m_dragInventory"); RequireField(list, typeof(InventoryGui), "m_trophiesPanel"); RequireField(list, typeof(InventoryGui), "m_achievementsPanel"); RequireField(list, typeof(InventoryGui), "m_skillsDialog"); RequireField(list, typeof(InventoryGui), "m_textsDialog"); RequireField(list, typeof(InventoryGui), "m_splitDialog"); RequireField(list, typeof(InventoryGui), "m_variantDialog"); RequireField(list, typeof(Hud), "m_requirementItems"); RequireField(list, typeof(Container), "m_nview"); RequireField(list, typeof(Container), "m_wagon"); RequireField(list, typeof(ZDO), "m_uid"); RequireField(list, typeof(ZDOVars), "s_items"); RequireField(list, typeof(Inventory), "m_onChanged"); RequireField(list, typeof(Inventory), "m_inventory"); RequireField(list, typeof(Player), "m_customData"); RequireField(list, typeof(Player), "m_noPlacementCost"); RequireField(list, typeof(TextInput), "m_inputField"); RequireField(list, typeof(ItemData), "m_gridPos"); RequireField(list, typeof(ItemData), "m_stack"); RequireField(list, typeof(ItemData), "m_quality"); RequireField(list, typeof(ItemData), "m_worldLevel"); RequireField(list, typeof(ItemData), "m_equipped"); RequireField(list, typeof(Piece), "m_resources"); RequireField(list, typeof(Piece), "m_craftingStation"); RequireField(list, typeof(Recipe), "m_resources"); RequireField(list, typeof(Recipe), "m_requireOnlyOneIngredient"); if (list.Count != 0) { return new CompatibilityResult(isCompatible: false, string.Join("; ", list), diagnostics); } return new CompatibilityResult(isCompatible: true, "verified runtime contract", diagnostics); } private static string DescribeRuntimeIdentity() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) Assembly assembly = typeof(Player).Assembly; string text = ((object)Version.CurrentVersion/*cast due to .constrained prefix*/).ToString(); string unityVersion = Application.unityVersion; string text2 = typeof(BaseUnityPlugin).Assembly.GetName().Version?.ToString() ?? "unknown"; string text3 = typeof(Harmony).Assembly.GetName().Version?.ToString() ?? "unknown"; Guid moduleVersionId = assembly.ManifestModule.ModuleVersionId; string[] obj = new string[12] { "Valheim label ", Application.version, ", API ", text, ", Unity ", unityVersion, ", BepInEx ", text2, ", Harmony ", text3, ", assembly_valheim MVID ", null }; Guid guid = moduleVersionId; obj[11] = guid.ToString(); return string.Concat(obj); } private static void RequireMethod(ICollection<string> failures, Type type, string name, params Type[] parameters) { RuntimeContractValidator.RequireMethod(failures, type, name, mustBeStatic: false, declaredOnly: false, parameters); } private static void RequireStaticMethod(ICollection<string> failures, Type type, string name, params Type[] parameters) { RuntimeContractValidator.RequireMethod(failures, type, name, mustBeStatic: true, declaredOnly: false, parameters); } private static void RequireConstructor(ICollection<string> failures, Type type, params Type[] parameters) { RuntimeContractValidator.RequireConstructor(failures, type, parameters); } private static void RequireProperty(ICollection<string> failures, Type type, string name) { RuntimeContractValidator.RequireProperty(failures, type, name); } private static void RequireField(ICollection<string> failures, Type type, string name) { RuntimeContractValidator.RequireField(failures, type, name); } } internal static class ConfigMigration { internal static ConfigEntry<bool> BindRenamedDefaultEnabledBoolean(ConfigFile config, string section, string legacyKey, string currentKey, string currentDescription) { if (config == null) { throw new ArgumentNullException("config"); } bool saveOnConfigSet = config.SaveOnConfigSet; config.SaveOnConfigSet = false; try { ConfigEntry<bool> val = config.Bind<bool>(section, legacyKey, true, "Legacy Stackmaster setting; migrated automatically and removed from the saved configuration."); ConfigEntry<bool> val2 = config.Bind<bool>(section, currentKey, true, currentDescription); val2.Value = val.Value && val2.Value; if (!config.Remove(((ConfigEntryBase)val).Definition)) { throw new InvalidOperationException("Could not retire legacy Stackmaster configuration key: " + legacyKey); } config.Save(); return val2; } finally { config.SaveOnConfigSet = saveOnConfigSet; } } } internal sealed class ContainerHandle { internal string Id { get; } internal Container Container { get; } internal ZNetView NetworkView { get; } internal ContainerSnapshot Snapshot { get; } internal Inventory ResourceInventory { get; } internal uint ResourceDataRevision { get; } internal ZDOID ResourceZdoId { get; } internal ushort ResourceOwnerRevision { get; } internal long ResourceOwner { get; } internal bool ResourceReadable { get; } internal ContainerHandle(string id, Container container, ZNetView networkView, ContainerSnapshot snapshot, Inventory resourceInventory, uint resourceDataRevision, ZDOID resourceZdoId, ushort resourceOwnerRevision, long resourceOwner, bool resourceReadable) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) Id = id; Container = container; NetworkView = networkView; Snapshot = snapshot; ResourceInventory = resourceInventory; ResourceDataRevision = resourceDataRevision; ResourceZdoId = resourceZdoId; ResourceOwnerRevision = resourceOwnerRevision; ResourceOwner = resourceOwner; ResourceReadable = resourceReadable; } } internal sealed class TargetDiscoveryDiagnostic { internal bool TargetPresent { get; } internal double Distance { get; } internal string Scope { get; } internal bool WithinScope { get; } internal bool Discovered { get; set; } internal string ObservedType { get; set; } internal bool? IsVanilla { get; set; } internal bool? HasNetworkView { get; set; } internal bool? NetworkViewValid { get; set; } internal bool? HasZdo { get; set; } internal bool? Refreshed { get; set; } internal bool? HasInventory { get; set; } internal bool? InUse { get; set; } internal bool? AccessGranted { get; set; } internal string RefreshFailure { get; set; } internal string AccessFailure { get; set; } internal TargetDiscoveryDiagnostic(bool targetPresent, double distance, StorageScope scope, Vector3 targetPosition) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) TargetPresent = targetPresent; Distance = distance; Scope = ((scope != null) ? scope.Description : "unavailable"); WithinScope = targetPresent && scope != null && scope.Contains(targetPosition); } internal string Format(DiscoveryResult discovery) { return string.Format(CultureInfo.InvariantCulture, "targetPresent={0} discovered={1} distance={2} scope={3} withinScope={4} searchTruncated={5} truncationReason={6} searchMs={7:0.00} objectScanMs={8:0.00} inspectionMs={9:0.00} budgetMs={10:0.00} candidates={11} inspected={12} minimumBeforeBudget={13} maximumNearby={14} type={15} vanilla={16} nview={17} nviewValid={18} zdo={19} refresh={20} inventory={21} inUse={22} access={23} refreshError={24} accessError={25}", TargetPresent, Discovered, double.IsPositiveInfinity(Distance) ? "n/a" : Distance.ToString("0.0", CultureInfo.InvariantCulture), Scope, WithinScope, discovery.Truncated, discovery.TruncationReason ?? "none", discovery.SearchMilliseconds, discovery.ObjectScanMilliseconds, discovery.InspectionMilliseconds, 25.0, discovery.NearbyCandidates, discovery.InspectedNearby, 8, 128, ObservedType ?? "n/a", FormatNullable(IsVanilla), FormatNullable(HasNetworkView), FormatNullable(NetworkViewValid), FormatNullable(HasZdo), FormatNullable(Refreshed), FormatNullable(HasInventory), FormatNullable(InUse), FormatNullable(AccessGranted), Sanitize(RefreshFailure), Sanitize(AccessFailure)); } private static string FormatNullable(bool? value) { if (!value.HasValue) { return "n/a"; } return value.Value.ToString(); } private static string Sanitize(string value) { if (string.IsNullOrEmpty(value)) { return "none"; } string text = value.Replace('\r', ' ').Replace('\n', ' ').Trim(); if (text.Length > 160) { return text.Substring(0, 160) + "…"; } return text; } } internal sealed class DiscoveryResult { internal IReadOnlyList<ContainerHandle> Containers { get; } internal bool Truncated { get; } internal double SearchMilliseconds { get; } internal double ObjectScanMilliseconds { get; } internal double InspectionMilliseconds { get; } internal int NearbyCandidates { get; } internal int InspectedNearby { get; } internal string TruncationReason { get; } internal TargetDiscoveryDiagnostic TargetDiagnostic { get; } internal StorageScope Scope { get; } internal DiscoveryResult(IReadOnlyList<ContainerHandle> containers, bool truncated, double searchMilliseconds, double objectScanMilliseconds, double inspectionMilliseconds, int nearbyCandidates, int inspectedNearby, string truncationReason, TargetDiscoveryDiagnostic targetDiagnostic, StorageScope scope) { Containers = containers; Truncated = truncated; SearchMilliseconds = searchMilliseconds; ObjectScanMilliseconds = objectScanMilliseconds; InspectionMilliseconds = inspectionMilliseconds; NearbyCandidates = nearbyCandidates; InspectedNearby = inspectedNearby; TruncationReason = truncationReason; TargetDiagnostic = targetDiagnostic; Scope = scope; } } internal static class ContainerDiscovery { internal const double SearchBudgetMilliseconds = 25.0; internal const int MinimumNearbyContainersBeforeBudget = 8; internal const int MaximumNearbyContainers = 128; private static readonly NearbyInspectionPolicy NearbyPolicy = new NearbyInspectionPolicy(8, 128, 25.0); private static readonly FieldInfo NetworkViewField = AccessTools.Field(typeof(Container), "m_nview"); private static readonly MethodInfo CheckAccessMethod = AccessTools.Method(typeof(Container), "CheckAccess", new Type[1] { typeof(long) }, (Type[])null); private static readonly MethodInfo CheckForChangesMethod = AccessTools.Method(typeof(Container), "CheckForChanges", (Type[])null, (Type[])null); internal static DiscoveryResult Discover(Player player, Container target, CompatibilityCatalog catalog, StorageScope scope, bool requireComplete = false, bool resourceReadOnly = false) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) List<ContainerHandle> list = new List<ContainerHandle>(); scope = scope ?? StorageScopeProvider.Resolve(player); double distance = (((Object)(object)target != (Object)null) ? ((double)Vector3.Distance(((Component)player).transform.position, ((Component)target).transform.position)) : double.PositiveInfinity); TargetDiscoveryDiagnostic targetDiscoveryDiagnostic = new TargetDiscoveryDiagnostic((Object)(object)target != (Object)null, distance, scope, (Vector3)(((Object)(object)target != (Object)null) ? ((Component)target).transform.position : default(Vector3))); if ((Object)(object)target != (Object)null && scope.Contains(((Component)target).transform.position)) { list.Add(Inspect(player, target, catalog, distance, isTarget: true, targetDiscoveryDiagnostic, resourceReadOnly)); } Stopwatch stopwatch = Stopwatch.StartNew(); Stopwatch stopwatch2 = Stopwatch.StartNew(); var array = (from container in Object.FindObjectsByType<Container>((FindObjectsInactive)0, (FindObjectsSortMode)0) where (Object)(object)container != (Object)null && (Object)(object)container != (Object)(object)target select new { Container = container, Distance = (double)Vector3.Distance(((Component)player).transform.position, ((Component)container).transform.position) } into candidate where scope.Contains(((Component)candidate.Container).transform.position) orderby candidate.Distance, ((Object)candidate.Container).GetInstanceID() select candidate).ToArray(); stopwatch2.Stop(); Stopwatch stopwatch3 = Stopwatch.StartNew(); int num = 0; string text = null; var array2 = array; foreach (var anon in array2) { double totalMilliseconds = stopwatch3.Elapsed.TotalMilliseconds; if (!requireComplete && !scope.RequiresCompleteDiscovery && !NearbyPolicy.CanInspectNext(num, totalMilliseconds)) { text = NearbyPolicy.StopReason(num, totalMilliseconds); break; } list.Add(Inspect(player, anon.Container, catalog, anon.Distance, isTarget: false, null, resourceReadOnly)); num++; } stopwatch3.Stop(); stopwatch.Stop(); bool flag = num < array.Length; if (flag && string.IsNullOrEmpty(text)) { text = "responsiveness limit reached"; } return new DiscoveryResult(list, flag, stopwatch.Elapsed.TotalMilliseconds, stopwatch2.Elapsed.TotalMilliseconds, stopwatch3.Elapsed.TotalMilliseconds, array.Length, num, text, targetDiscoveryDiagnostic, scope); } private static ContainerHandle Inspect(Player player, Container container, CompatibilityCatalog catalog, double distance, bool isTarget, TargetDiscoveryDiagnostic diagnostic, bool resourceReadOnly) { //IL_02d7: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: Unknown result type (might be due to invalid IL or missing references) object? value = NetworkViewField.GetValue(container); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); bool flag = (Object)(object)val != (Object)null && val.IsValid(); ZDO val2 = (flag ? val.GetZDO() : null); string text = ((val2 != null) ? ((object)Unsafe.As<ZDOID, ZDOID>(ref val2.m_uid)/*cast due to .constrained prefix*/).ToString() : ("instance:" + ((Object)container).GetInstanceID())); Type type = ((object)container).GetType(); bool flag2 = type == typeof(Container) && type.Assembly == typeof(Container).Assembly; string failure = null; bool flag3 = !resourceReadOnly && flag2 && flag && val2 != null && TryRefreshFromNetwork(container, out failure); Inventory val3 = (flag3 ? container.GetInventory() : null); bool flag4 = flag3 && val3 != null; bool flag5 = isTarget && StorageAction.IsLocalOpenTarget(container); bool flag6 = flag4 && ((!flag5 && container.IsInUse()) || ((Object)(object)container.m_wagon != (Object)null && container.m_wagon.InUse())); string failure2 = null; bool num; if (!resourceReadOnly) { num = flag4; } else { if (!(flag2 && flag)) { goto IL_0140; } num = val2 != null; } if (!num) { goto IL_0140; } int num2 = (TryCheckAccess(player, container, out failure2) ? 1 : 0); goto IL_0141; IL_0140: num2 = 0; goto IL_0141; IL_0141: bool flag7 = (byte)num2 != 0; Inventory inventory = null; uint dataRevision = 0u; string failure3 = null; bool flag8 = resourceReadOnly && flag2 && flag && val2 != null && TryReadSerializedInventory(container, val2, out inventory, out dataRevision, out failure3); ResourceSnapshotReadPlan resourceSnapshotReadPlan = ResourceSnapshotPolicy.Evaluate(resourceReadOnly, flag4, flag7, flag6, flag8); bool captureLiveInventory = resourceSnapshotReadPlan.CaptureLiveInventory; int capacity = ((val3 != null) ? (val3.GetWidth() * val3.GetHeight()) : 0); IReadOnlyList<ItemStackSnapshot> readOnlyList2; if (!captureLiveInventory || val3 == null) { IReadOnlyList<ItemStackSnapshot> readOnlyList = Array.Empty<ItemStackSnapshot>(); readOnlyList2 = readOnlyList; } else { readOnlyList2 = InventorySnapshots.CaptureInventory(text, val3, catalog).Items; } IReadOnlyList<ItemStackSnapshot> items = readOnlyList2; bool useDetachedInventory = resourceSnapshotReadPlan.UseDetachedInventory; if (!flag8 && string.IsNullOrEmpty(failure)) { failure = failure3; } if (diagnostic != null) { diagnostic.Discovered = true; diagnostic.ObservedType = type.FullName; diagnostic.IsVanilla = flag2; diagnostic.HasNetworkView = (Object)(object)val != (Object)null; diagnostic.NetworkViewValid = flag; diagnostic.HasZdo = val2 != null; diagnostic.Refreshed = flag3; diagnostic.HasInventory = val3 != null; diagnostic.InUse = (flag4 ? new bool?(flag6) : ((bool?)null)); diagnostic.AccessGranted = (flag4 ? new bool?(flag7) : ((bool?)null)); diagnostic.RefreshFailure = failure; diagnostic.AccessFailure = failure2; } ContainerSnapshot snapshot = new ContainerSnapshot(text, distance, isTarget, flag4, captureLiveInventory, flag2, flag6, capacity, items); return new ContainerHandle(text, container, val, snapshot, useDetachedInventory ? inventory : null, dataRevision, (ZDOID)(((??)val2?.m_uid) ?? default(ZDOID)), (ushort)((val2 != null) ? val2.OwnerRevision : 0), (val2 != null) ? val2.GetOwner() : 0, useDetachedInventory); } private static bool TryReadSerializedInventory(Container container, ZDO zdo, out Inventory inventory, out uint dataRevision, out string failure) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown inventory = null; dataRevision = 0u; failure = null; try { uint dataRevision2 = zdo.DataRevision; byte[] byteArray = zdo.GetByteArray(ZDOVars.s_items, (byte[])null); uint dataRevision3 = zdo.DataRevision; if (dataRevision2 != dataRevision3) { failure = "container data revision changed while reading its serialized inventory"; return false; } Inventory val = new Inventory(true); if (byteArray != null) { val.Load(new ZPackage(byteArray)); } if (zdo.DataRevision != dataRevision3) { failure = "container data revision changed while decoding its serialized inventory"; return false; } if (!DetachedItemHydrator.TryHydrate(val.GetAllItems(), delegate(ItemData item) { ItemDrop val2 = (((Object)(object)item.m_dropPrefab != (Object)null) ? item.m_dropPrefab.GetComponent<ItemDrop>() : null); return (!((Object)(object)val2 != (Object)null) || val2.m_itemData == null) ? null : val2.m_itemData.m_shared; }, delegate(ItemData item, SharedData shared) { item.m_shared = shared; })) { failure = "detached inventory item metadata could not be resolved"; return false; } inventory = val; dataRevision = dataRevision3; return true; } catch (Exception ex) { failure = ex.Message; return false; } } internal static bool RefreshFromNetwork(Container container) { string failure; bool num = TryRefreshFromNetwork(container, out failure); if (!num && (Object)(object)RuntimeContext.Plugin != (Object)null) { RuntimeContext.Plugin.Log.LogWarning((object)("Container refresh failed safely: " + failure)); } return num; } private static bool TryRefreshFromNetwork(Container container, out string failure) { failure = null; try { CheckForChangesMethod.Invoke(container, null); return true; } catch (Exception ex) { TargetInvocationException ex2 = ex as TargetInvocationException; failure = ((ex2 == null) ? ex.Message : (ex2.InnerException?.Message ?? ex2.Message)); return false; } } internal static bool CheckAccess(Player player, Container container) { string failure; bool num = TryCheckAccess(player, container, out failure); if (!num && !string.IsNullOrEmpty(failure) && (Object)(object)RuntimeContext.Plugin != (Object)null) { RuntimeContext.Plugin.Log.LogWarning((object)("Container access check failed safely: " + failure)); } return num; } private static bool TryCheckAccess(Player player, Container container, out string failure) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) failure = null; try { if (container.m_checkGuardStone && !PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, false)) { return false; } return (bool)CheckAccessMethod.Invoke(container, new object[1] { player.GetPlayerID() }); } catch (Exception ex) { TargetInvocationException ex2 = ex as TargetInvocationException; failure = ((ex2 == null) ? ex.Message : (ex2.InnerException?.Message ?? ex2.Message)); return false; } } } internal sealed class SessionToken { internal ZNet Network { get; } internal long LocalSession { get; } internal SessionToken(ZNet network, long localSession) { Network = network; LocalSession = localSession; } internal static bool TryCapture(out SessionToken token) { token = null; if (!RuntimeContext.Compatibility.IsCompatible || (Object)(object)ZNet.instance == (Object)null || ZDOMan.instance == null) { return false; } token = new SessionToken(ZNet.instance, ZDOMan.GetSessionID()); return true; } internal bool IsCurrent() { if (RuntimeContext.Compatibility.IsCompatible && Network == ZNet.instance && ZDOMan.instance != null) { return ZDOMan.GetSessionID() == LocalSession; } return false; } } internal sealed class CraftingIntent { internal InventoryGui Gui { get; } internal Player Player { get; } internal Recipe Recipe { get; } internal ItemData UpgradeItem { get; } internal int Quality { get; } internal int Variant { get; } internal bool MultiCrafting { get; } internal int Multiplier { get; } internal CraftingStation Station { get; } internal int StationLevel { get; } internal Vector3 PlayerPosition { get; } internal string ScopeSignature { get; } internal SessionToken Session { get; } internal CraftingIntent(InventoryGui gui, Player player, Recipe recipe, ItemData upgradeItem, int quality, int variant, bool multiCrafting, int multiplier, CraftingStation station, StorageScope scope, SessionToken session) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) Gui = gui; Player = player; Recipe = recipe; UpgradeItem = upgradeItem; Quality = quality; Variant = variant; MultiCrafting = multiCrafting; Multiplier = multiplier; Station = station; StationLevel = ((!((Object)(object)station == (Object)null)) ? station.GetLevel(true) : 0); PlayerPosition = ((Component)player).transform.position; ScopeSignature = scope?.Signature; Session = session; } } internal static class CraftingPreflightAction { private const float OwnershipTimeoutSeconds = 2f; private static readonly CraftingActionLifecycle Lifecycle = new CraftingActionLifecycle(); private static readonly MethodInfo OnCraftPressedMethod = AccessTools.Method(typeof(InventoryGui), "OnCraftPressed", Type.EmptyTypes, (Type[])null); private static readonly FieldInfo SelectedRecipeField = AccessTools.Field(typeof(InventoryGui), "m_selectedRecipe"); private static readonly Type RecipeDataPairType = AccessTools.Inner(typeof(InventoryGui), "RecipeDataPair"); private static readonly PropertyInfo SelectedRecipeProperty = AccessTools.Property(RecipeDataPairType, "Recipe"); private static readonly PropertyInfo SelectedUpgradeItemProperty = AccessTools.Property(RecipeDataPairType, "ItemData"); private static readonly FieldInfo SelectedVariantField = AccessTools.Field(typeof(InventoryGui), "m_selectedVariant"); private static readonly FieldInfo CraftRecipeField = AccessTools.Field(typeof(InventoryGui), "m_craftRecipe"); private static readonly FieldInfo CraftUpgradeItemField = AccessTools.Field(typeof(InventoryGui), "m_craftUpgradeItem"); private static readonly FieldInfo CraftVariantField = AccessTools.Field(typeof(InventoryGui), "m_craftVariant"); private static readonly FieldInfo MultiCraftingField = AccessTools.Field(typeof(InventoryGui), "m_multiCrafting"); private static readonly FieldInfo MultiCraftAmountField = AccessTools.Field(typeof(InventoryGui), "m_multiCraftAmount"); private static readonly FieldInfo TouchMultiCraftingField = AccessTools.Field(typeof(InventoryGui), "m_touchMultiCrafting"); private static readonly FieldInfo CraftTimerField = AccessTools.Field(typeof(InventoryGui), "m_craftTimer"); private static CraftingIntent _intent; private static CraftingResourcePlan _plan; private static PreparedCraftingResources _prepared; private static OwnershipBatch _ownership; private static int _generation; private static bool _resuming; internal static bool Prefix(InventoryGui gui) { if (_resuming) { return true; } if (!RuntimeContext.Compatibility.IsCompatible || (Object)(object)RuntimeContext.Plugin == (Object)null || !RuntimeContext.Plugin.CraftingFromNearbyChestsEnabled.Value) { return true; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || localPlayer.NoCostCheat() || ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey((GlobalKeys)25))) { return true; } if (Lifecycle.IsActive) { if (_intent != null && IsSelectionCurrent(_intent, requireCraftFields: false)) { Show("Stackmaster: securing the required nearby materials."); return false; } Cancel("a newer crafting intent replaced the pending craft", notify: false); } if (!TryCaptureIntent(gui, out var intent)) { return true; } if (!NearbyResourceService.TryPlanCraftingResources(intent.Player, intent.Recipe, intent.Quality, intent.Multiplier, out var planned, out var failure)) { return true; } if (planned.Plan.RequiredUnits == 0) { return true; } _intent = intent; _plan = planned; ContainerHandle[] array = planned.RequiredHandles.Where((ContainerHandle handle) => !handle.NetworkView.IsOwner() || !handle.Container.IsOwner()).ToArray(); _generation = Lifecycle.Begin(array.Length != 0); if (array.Length == 0) { if (!TryPrepare(out failure)) { Cancel(failure, notify: true); return false; } return true; } Show("Stackmaster: securing the required nearby materials."); ((MonoBehaviour)RuntimeContext.Plugin).StartCoroutine(AcquireAndResume(_generation, intent.Session, array)); return false; } internal static void AfterVanillaStart(InventoryGui gui) { if (!Lifecycle.Matches(_generation) || Lifecycle.Phase != CraftingActionPhase.Reserved || _intent == null || gui != _intent.Gui) { return; } if (_resuming) { MultiCraftingField.SetValue(gui, _intent.MultiCrafting); if (_intent.MultiCrafting) { MultiCraftAmountField.SetValue(gui, _intent.Multiplier); } } if (!IsSelectionCurrent(_intent, requireCraftFields: true) || GetCraftTimer(gui) < 0f || !Lifecycle.MarkCrafting(_generation)) { Cancel("vanilla did not start the reserved craft", notify: true); } } internal static Exception Finalizer(Exception exception) { if (exception != null && Lifecycle.IsActive) { Cancel("craft start threw " + exception.GetType().Name, notify: true); } return exception; } internal static void Update(InventoryGui gui) { if (!Lifecycle.IsActive || _intent == null || gui != _intent.Gui) { return; } if (!RuntimeContext.Compatibility.IsCompatible || !_intent.Session.IsCurrent()) { Cancel("the network session changed during crafting", notify: false); return; } bool flag = Lifecycle.Phase == CraftingActionPhase.Crafting; if (!IsSelectionCurrent(_intent, flag)) { Cancel("the player, crafting selection, station, or storage scope changed", notify: true); } else if (flag && GetCraftTimer(gui) < 0f) { Cancel("the craft was canceled before completion", notify: false); } } internal static bool TryBeginPreparedTransaction(InventoryGui gui, Player player, Recipe recipe, int quality, int multiplier, out bool handled, out string failure) { handled = Lifecycle.IsActive; failure = null; if (!handled) { return false; } if (_intent == null || _prepared == null || gui != _intent.Gui || player != _intent.Player || recipe != _intent.Recipe || quality != _intent.Quality || multiplier != _intent.Multiplier || !IsSelectionCurrent(_intent, requireCraftFields: true) || !Lifecycle.TransferToTransaction(_generation)) { failure = "the prepared crafting intent no longer matches the finishing craft"; Cancel(failure, notify: true); return false; } PreparedCraftingResources prepared = _prepared; _prepared = null; ClearReferences(); Lifecycle.FinishTransferred(_generation); if (!NearbyResourceService.TryBeginPreparedCraftingTransaction(player, prepared, out failure)) { NearbyResourceService.ReleasePreparedCraftingResources(prepared); return false; } return true; } internal static void Cancel(string reason, bool notify) { if (Lifecycle.IsActive || _intent != null || _prepared != null || _ownership != null) { if (_intent != null && (Object)(object)_intent.Gui != (Object)null) { CraftTimerField.SetValue(_intent.Gui, -1f); } Lifecycle.Cancel(); if (_prepared != null) { NearbyResourceService.ReleasePreparedCraftingResources(_prepared); } if (_ownership != null) { OwnershipCoordinator.Cancel(_ownership, reason); } ClearReferences(); if (notify && !string.IsNullOrWhiteSpace(reason)) { Show("Stackmaster: craft canceled — " + reason + "."); } } } internal static void Shutdown(string reason) { Cancel(reason, notify: false); } private static IEnumerator AcquireAndResume(int generation, SessionToken session, ContainerHandle[] unowned) { if (!Lifecycle.Matches(generation) || !session.IsCurrent()) { yield break; } OwnershipBatch ownership; try { ownership = (_ownership = OwnershipCoordinator.Begin(unowned)); } catch (Exception ex) { Cancel("ownership request failed: " + ex.GetType().Name, notify: true); yield break; } float deadline = Time.realtimeSinceStartup + 2f; while (Lifecycle.Matches(generation) && session.IsCurrent()) { try { ownership.Refresh(); } catch (Exception ex2) { Cancel("ownership refresh failed: " + ex2.GetType().Name, notify: true); yield break; } if (ownership.IsComplete) { break; } if (Time.realtimeSinceStartup >= deadline) { ownership.Timeout(); break; } yield return null; } if (!Lifecycle.Matches(generation) || !session.IsCurrent()) { if (_ownership == ownership) { OwnershipCoordinator.Cancel(ownership, "craft acquisition became stale"); _ownership = null; } yield break; } try { ownership.Refresh(); if (!ownership.IsComplete) { ownership.Timeout(); } if (ownership.FailedContainerIds.Count != 0) { Cancel((ownership.OwnerRejectedContainerIds.Count != 0) ? "The required materials are currently in use" : "Stackmaster could not safely acquire the required materials", notify: true); yield break; } OwnershipLeaseManager.HoldForCrafting(ownership); OwnershipCoordinator.End(ownership); _ownership = null; if (!TryPrepare(out var failure)) { OwnershipLeaseManager.ReleaseBatch(ownership, "craft reservation failed"); Cancel(failure, notify: true); yield break; } if (!Lifecycle.MarkReserved(generation)) { Cancel("the ownership callback was stale", notify: true); yield break; } TouchMultiCraftingField.SetValue(_intent.Gui, _intent.MultiCrafting); SelectedVariantField.SetValue(_intent.Gui, _intent.Variant); try { _resuming = true; OnCraftPressedMethod.Invoke(_intent.Gui, null); } finally { _resuming = false; } } catch (TargetInvocationException ex3) { Cancel("vanilla craft start threw " + (ex3.InnerException ?? ex3).GetType().Name, notify: true); } catch (Exception ex4) { Cancel("ownership preparation threw " + ex4.GetType().Name, notify: true); } finally { if (_ownership == ownership) { OwnershipCoordinator.Cancel(ownership, "craft acquisition ended before reservation"); _ownership = null; } } } private static bool TryPrepare(out string failure) { failure = null; if (_intent == null || _plan == null || !IsSelectionCurrent(_intent, requireCraftFields: false)) { failure = "the crafting selection changed before reservation"; return false; } return NearbyResourceService.TryPrepareCraftingResources(_intent.Player, _plan, out _prepared, out failure); } private static bool TryCaptureIntent(InventoryGui gui, out CraftingIntent intent) { intent = null; Player localPlayer = Player.m_localPlayer; object value = SelectedRecipeField.GetValue(gui); Recipe val = (Recipe)((value == null) ? null : /*isinst with value type is only supported in some contexts*/); if ((Object)(object)localPlayer == (Object)null || value == null || (Object)(object)val == (Object)null) { return false; } object? value2 = SelectedUpgradeItemProperty.GetValue(value, null); ItemData val2 = (ItemData)((value2 is ItemData) ? value2 : null); int quality = ((val2 == null) ? 1 : (val2.m_quality + 1)); bool flag = val2 == null && (ZInput.GetButton("AltPlace") || ZInput.GetButton("JoyLStick") || (bool)TouchMultiCraftingField.GetValue(gui)); int multiplier = ((!flag) ? 1 : Math.Max(1, (int)MultiCraftAmountField.GetValue(gui))); StorageScope storageScope = StorageScopeProvider.Resolve(localPlayer); if (storageScope == null || !SessionToken.TryCapture(out var token)) { return false; } intent = new CraftingIntent(gui, localPlayer, val, val2, quality, (int)SelectedVariantField.GetValue(gui), flag, multiplier, localPlayer.GetCurrentCraftingStation(), storageScope, token); return true; } private static bool IsSelectionCurrent(CraftingIntent intent, bool requireCraftFields) { //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) if (intent != null && !((Object)(object)intent.Player == (Object)null) && !((Object)(object)intent.Gui == (Object)null) && Player.m_localPlayer == intent.Player && !((Character)intent.Player).IsDead() && !((Character)intent.Player).IsTeleporting() && !((Character)intent.Player).InCutscene() && intent.Player.GetCurrentCraftingStation() == intent.Station && (!((Object)(object)intent.Station != (Object)null) || (intent.Station.CheckUsable(intent.Player, false) && intent.Station.GetLevel(true) == intent.StationLevel))) { Vector3 val = ((Component)intent.Player).transform.position - intent.PlayerPosition; if (!(((Vector3)(ref val)).sqrMagnitude > 0.0025f)) { object value = SelectedRecipeField.GetValue(intent.Gui); if (value == null || SelectedRecipeProperty.GetValue(value, null) != intent.Recipe || SelectedUpgradeItemProperty.GetValue(value, null) != intent.UpgradeItem || (int)SelectedVariantField.GetValue(intent.Gui) != intent.Variant) { return false; } StorageScope storageScope = StorageScopeProvider.Resolve(intent.Player); if (storageScope == null || !string.Equals(storageScope.Signature, intent.ScopeSignature, StringComparison.Ordinal)) { return false; } if (!requireCraftFields) { return true; } if (CraftRecipeField.GetValue(intent.Gui) == intent.Recipe && CraftUpgradeItemField.GetValue(intent.Gui) == intent.UpgradeItem && (int)CraftVariantField.GetValue(intent.Gui) == intent.Variant && (bool)MultiCraftingField.GetValue(intent.Gui) == intent.MultiCrafting) { if (intent.MultiCrafting) { return (int)MultiCraftAmountField.GetValue(intent.Gui) == intent.Multiplier; } return true; } return false; } } return false; } private static float GetCraftTimer(InventoryGui gui) { return (float)CraftTimerField.GetValue(gui); } private static void ClearReferences() { _intent = null; _plan = null; _prepared = null; _ownership = null; } private static void Show(string message) { MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, message, 0, (Sprite)null, false, true); } } } internal static class ExpeditionKitClickPatch { internal static bool Prefix([HarmonyArgument(0)] Piece piece) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) bool flag = false; try { Plugin plugin = RuntimeContext.Plugin; Player localPlayer = Player.m_localPlayer; KeyCode[] source; if (!((Object)(object)plugin != (Object)null)) { source = Array.Empty<KeyCode>(); } else { KeyboardShortcut value = plugin.StorageActionShortcut.Value; source = ((KeyboardShortcut)(ref value)).Modifiers.ToArray(); } bool[] configuredModifierStates = ((IEnumerable<KeyCode>)source).Select((Func<KeyCode, bool>)Input.GetKey).ToArray(); bool hasClickedPiece = (Object)(object)piece != (Object)null && (piece.m_resources ?? Array.Empty<Requirement>()).Any((Requirement requirement) => requirement != null && (Object)(object)requirement.m_resItem != (Object)null && requirement.m_amount > 0); if (!ExpeditionClickPolicy.ShouldIntercept(RuntimeContext.Compatibility.IsCompatible, (Object)(object)localPlayer != (Object)null, hasClickedPiece, configuredModifierStates)) { return true; } flag = true; ExpeditionKitAction.Begin(localPlayer, piece); return false; } catch (Exception exception) { NearbyHudFailOpen.ReportOnce("expedition-kit click", exception); return !flag; } } } internal sealed class ExpeditionInventoryBackup { private readonly IdentityPreservingInventoryBackup<ItemData, ItemData> _items; internal Inventory Inventory { get; } internal int TotalUnits { get; } internal ExpeditionInventoryBackup(Inventory inventory, bool preserveItemIdentity) { Inventory = inventory ?? throw new ArgumentNullException("inventory"); ItemData[] array = (from item in inventory.GetAllItems() orderby item.m_gridPos.y, item.m_gridPos.x select item).ToArray(); _items = IdentityPreservingInventoryBackup<ItemData, ItemData>.Capture(preserveItemIdentity ? ((IEnumerable<ItemData>)array) : ((IEnumerable<ItemData>)array.Select((ItemData item) => item.Clone()).ToArray()), (ItemData item) => item.Clone()); TotalUnits = array.Sum((ItemData item) => item.m_stack); } internal ExpeditionSnapshotRestoreTarget PrepareRestore() { return new ExpeditionSnapshotRestoreTarget(Inventory, _items.PrepareRestore((ItemData snapshot) => snapshot.Clone()), TotalUnits); } } internal sealed class ExpeditionSnapshotRestoreTarget { internal Inventory Inventory { get; } internal IdentityPreservingInventoryRestorePlan<ItemData, ItemData> Items { get; } internal int TotalUnits { get; } internal ExpeditionSnapshotRestoreTarget(Inventory inventory, IdentityPreservingInventoryRestorePlan<ItemData, ItemData> items, int totalUnits) { Inventory = inventory; Items = items; TotalUnits = totalUnits; } } internal sealed class CompletedExpeditionMove { internal RuntimeResourceStack Source { get; } internal ItemData SourceClone { get; } internal Vector2i SourcePosition { get; } internal Vector2i DestinationPosition { get; } internal string CompatibilityKey { get; } internal int Quantity { get; } internal ContainerReservation Reservation { get; } internal CompletedExpeditionMove(RuntimeResourceStack source, ItemData sourceClone, Vector2i sourcePosition, Vector2i destinationPosition, string compatibilityKey, int quantity, ContainerReservation reservation) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) Source = source; SourceClone = sourceClone; SourcePosition = sourcePosition; DestinationPosition = destinationPosition; CompatibilityKey = compatibilityKey; Quantity = quantity; Reservation = reservation; } } internal static class ExpeditionKitAction { private const float OwnershipTimeoutSeconds = 2f; private static readonly ExpeditionKitWithdrawalPlanner WithdrawalPlanner = new ExpeditionKitWithdrawalPlanner(); private static readonly ExpeditionCapacityPlanner CapacityPlanner = new ExpeditionCapacityPlanner(); private static readonly MethodInfo AddItemAtMethod = AccessTools.DeclaredMethod(typeof(Inventory), "AddItem", new Type[5] { typeof(ItemData), typeof(int), typeof(int), typeof(int), typeof(bool) }, (Type[])null); private static readonly FieldInfo InventoryItemsField = AccessTools.Field(typeof(Inventory), "m_inventory"); private static readonly Queue<Tuple<Player, Piece>> PendingRequests = new Queue<Tuple<Player, Piece>>(); private static bool _running; private static int _generation; internal static void Begin(Player player, Piece piece) { if (!((Object)(object)player == (Object)null) && !((Object)(object)piece == (Object)null)) { PendingRequests.Enqueue(Tuple.Create<Player, Piece>(player, piece)); StartNext(); } } private static void StartNext() { if (_running || !RuntimeContext.Compatibility.IsCompatible) { return; } Tuple<Player, Piece> tuple = null; while (PendingRequests.Count > 0) { Tuple<Player, Piece> tuple2 = PendingRequests.Dequeue(); if (tuple2.Item1 == Player.m_localPlayer) { tuple = tuple2; break; } } if (tuple == null) { return; } Player item = tuple.Item1; Piece item2 = tuple.Item2; int generation = _generation; _running = true; try { if (!TryPrepare(item, item2, out var _, out var capture, out var plan, out var handles, out var failure)) { ShowFailure(failure); Complete(generation); return; } if (!TryPlanCapacity(item, capture, plan, out var _, out failure)) { ShowFailure(failure); Complete(generation); return; } if (handles.Any((ContainerHandle handle) => OwnershipLeaseManager.HasPotentialAcquisition(handle.Id))) { ShowFailure("a previous ownership transition is still pending"); Complete(generation); return; } ContainerHandle[] array = handles.Where((ContainerHandle handle) => !handle.NetworkView.IsOwner() || !handle.Container.IsOwner()).ToArray(); if (array.Length == 0) { Finish(item, item2, plan, null); Complete(generation); } else { RuntimeContext.ShowTopLeft("Stackmaster: checking expedition-kit storage…"); ((MonoBehaviour)RuntimeContext.Plugin).StartCoroutine(FinishAfterOwnership(item, item2, plan, array, generation)); } } catch (Exception ex) { Plugin plugin = RuntimeContext.Plugin; if (plugin != null) { plugin.Log.LogError((object)("Expedition-kit action stopped safely: " + ex)); } RuntimeContext.ShowCenter("Stackmaster stopped safely; no expedition kit was added."); Complete(generation); } } private static void Complete(int generation) { if (generation == _generation) { _running = false; StartNext(); } } internal static void Shutdown() { _generation++; PendingRequests.Clear(); _running = false; } internal static void RearmSession() { PendingRequests.Clear(); _running = false; } private static IEnumerator FinishAfterOwnership(Player player, Piece piece, ResourceWithdrawalPlan expectedPlan, ContainerHandle[] unownedHandles, int generation) { if (generation != _generation || !RuntimeContext.Compatibility.IsCompatible) { yield break; } OwnershipBatch ownership; try { ownership = OwnershipCoordinator.Begin(unownedHandles); } catch (Exception ex) { Plugin plugin = RuntimeContext.Plugin; if (plugin != null) { plugin.Log.LogError((object)("Expedition-kit ownership setup failed safely: " + ex)); } RuntimeContext.ShowCenter("Stackmaster could not safely acquire the expedition kit; nothing was moved."); Complete(generation); yield break; } try { bool refreshFailed = false; float deadline = Time.realtimeSinceStartup + 2f; while (generation == _generation && player == Player.m_localPlayer && !ownership.IsComplete && Time.realtimeSinceStartup < deadline) { try { ownership.Refresh(); } catch (Exception ex2) { refreshFailed = true; Plugin plugin2 = RuntimeContext.Plugin; if (plugin2 != null) { plugin2.Log.LogError((object)("Expedition-kit ownership refresh failed safely: " + ex2)); } RuntimeContext.ShowCenter("Stackmaster could not safely acquire the expedition kit; nothing was moved."); } if (refreshFailed) { yield break; } yield return null; } try { if (generation != _generation || !RuntimeContext.Compatibility.IsCompatible || player != Player.m_localPlayer) { yield break; } ownership.Refresh(); if (!ownership.IsComplete) { ownership.Timeout(); } if (ownership.FailedContainerIds.Count > 0) { ShowFailure((ownership.OwnerRejectedContainerIds.Count > 0 && unownedHandles.Where((ContainerHandle handle) => ownership.OwnerRejectedContainerIds.Contains(handle.Id)).All((ContainerHandle handle) => (Object)(object)handle.NetworkView != (Object)null && handle.NetworkView.IsValid() && handle.NetworkView.HasOwner() && ContainerDiscovery.CheckAccess(player, handle.Container))) ? "The required materials are currently in use" : "required storage ownership could not be acquired"); } else { Finish(player, piece, expectedPlan, ownership); } } catch (Exception ex3) { Plugin plugin3 = RuntimeContext.Plugin; if (plugin3 != null) { plugin3.Log.LogError((object)("Expedition-kit ownership failed safely: " + ex3)); } RuntimeContext.ShowCenter("Stackmaster could not safely acquire the expedition kit; nothing was moved."); } } finally { if (generation == _generation) { try { if (!ownership.IsComplete) { ownership.Timeout(); } } catch (Exception ex4) { Plugin plugin4 = RuntimeContext.Plugin; if (plugin4 != null) { plugin4.Log.LogError((object)("Expedition-kit ownership timeout cleanup failed: " + ex4)); } DisableAfterFatalFailure("Stackmaster could not finish expedition-kit ownership timeout cleanup."); } try { OwnershipLeaseManager.ReleaseBatch(ownership, "expedition-kit action ended"); } catch (Exception ex5) { Plugin plugin5 = RuntimeContext.Plugin; if (plugin5 != null) { plugin5.Log.LogError((object)("Expedition-kit ownership release failed safely: " + ex5)); } DisableAfterFatalFailure("Stackmaster could not hand expedition-kit ownership to cleanup."); } finally { try { OwnershipCoordinator.End(ownership); } catch (Exception ex) { Plugin plugin6 = RuntimeContext.Plugin; if (plugin6 != null) { plugin6.Log.LogError((object)("Expedition-kit ownership teardown failed safely: " + ex)); } DisableAfterFatalFailure("Stackmaster could not finish expedition-kit ownership teardown."); } finally { Complete(generation); } } } } } private static void Finish(Player player, Piece piece, ResourceWithdrawalPlan expectedPlan, OwnershipBatch ownership) { if (!TryPrepare(player, piece, out var requirements, out var capture, out var plan, out var handles, out var failure) || !ExpeditionKitWithdrawalPlanner.PlansAreIdentical(expectedPlan, plan)) { ShowFailure(failure ?? "nearby materials changed before transfer"); return; } if (handles.Any((ContainerHandle handle) => !handle.NetworkView.IsOwner() || !handle.Container.IsOwner())) { ShowFailure("required storage ownership changed before transfer"); return; } if (!NearbyResourceService.RevalidateContainers(player, plan, capture, out failure)) { ShowFailure(failure); return; } if (!TryPrepare(player, piece, out requirements, out capture, out plan, out handles, out failure) || !ExpeditionKitWithdrawalPlanner.PlansAreIdentical(expectedPlan, plan) || handles.Any((ContainerHandle handle) => !handle.NetworkView.IsOwner() || !handle.Container.IsOwner())) { ShowFailure(failure ?? "nearby materials changed during transfer preparation"); return; } if (!NearbyResourceService.TryReserveContainers(player, capture.Scope, handles, releaseMatchingOwnershipOnFailure: false, out var reservations, out failure)) { ShowFailure(failure); return; } try { if (!NearbyResourceService.RevalidateReservedContainers(player, capture.Scope, reservations, out failure) || !NearbyResourceService.RevalidateStacks(plan, capture, matchWorldLevel: true, out failure) || !TryPlanCapacity(player, capture, plan, out var plan2, out failure)) { ShowFailure(failure); return; } if (!ExecuteAtomic(player, capture, plan, plan2, reservations, out failure)) { ShowFailure(failure); return; } try { NearbyResourceService.ResetCaches(); NearbyBuildHudPatch.ResetCache(); } catch (Exception ex) { Plugin plugin = RuntimeContext.Plugin; if (plugin != null) { plugin.Log.LogError((object)("Expedition-kit cache refresh failed after commit: " + ex)); } } try { RuntimeContext.ShowTopLeft("Stackmaster: expedition kit added (" + plan.RequiredUnits.ToString(CultureInfo.InvariantCulture) + " items)."); } catch (Exception ex2) { Plugin plugin2 = RuntimeContext.Plugin; if (plugin2 != null) { plugin2.Log.LogError((object)("Expedition-kit success notification failed after commit: " + ex2)); } } } finally { bool flag = false; try { flag = NearbyResourceService.ReleaseReservations(reservations, releaseMatchingOwnership: false); } catch (Exception ex3) { Plugin plugin3 = RuntimeContext.Plugin; if (plugin3 != null) { plugin3.Log.LogError((object)("Expedition-kit reservation cleanup threw: " + ex3)); } } if (!flag) { DisableAfterFatalFailure("Stackmaster could not fully release an expedition-kit reservation."); } } } private static bool TryPrepare(Player player, Piece piece, out IReadOnlyList<ResourceRequirement> requirements, out NearbyResourceCapture capture, out ResourceWithdrawalPlan plan, out ContainerHandle[] handles, out string failure) { requirements = Array.Empty<ResourceRequirement>(); capture = null; plan = null; handles = Array.Empty<ContainerHandle>(); failure = null; if ((Object)(object)player == (Object)null || (Object)(object)piece == (Object)null) { failure = "invalid build piece"; return false; } requirements = NearbyResourceService.PieceRequirements(piece); if (requirements.Count == 0) { failure = "this build piece has no material recipe"; return false; } capture = NearbyResourceService.CaptureForExpedition(player, matchWorldLevel: true, fresh: true); plan = WithdrawalPlanner.Plan(requirements, capture.Stacks.Where((ResourceStack stack) => !string.Equals(stack.InventoryId, "player", StringComparison.Ordinal))); if (!plan.IsSatisfiable || plan.PlannedUnits != plan.RequiredUnits) { failure = "nearby storage does not contain one complete additional kit"; return false; } if (!NearbyResourceService.TryResolveRequiredContainers(player, plan, capture, out handles, out failure)) { return false; } return true; } private static bool TryPlanCapacity(Player player, NearbyResourceCapture capture, ResourceWithdrawalPlan withdrawal, out ExpeditionCapacityPlan plan, out string failure) { failure = null; CompatibilityCatalog compatibilityCatalog = new CompatibilityCatalog(); Inventory inventory = ((Humanoid)player).GetInventory(); InventorySnapshot player2 = InventorySnapshots.CaptureInventory("player", inventory, compatibilityCatalog); List<ExpeditionCargoStack> list = new List<ExpeditionCargoStack>(withdrawal.Steps.Count); foreach (ResourceWithdrawalStep step in withdrawal.Steps) { if (!capture.RuntimeStacks.TryGetValue(step.StackId, out var value) || value.Item == null || value.Item.m_shared == null) { plan = null; failure = "planned expedition material disappeared"; return false; } list.Add(new ExpeditionCargoStack(step.StackId, compatibilityCatalog.KeyFor(value.Item), step.Quantity, value.Item.m_shared.m_maxStackSize, value.Item.GetWeight(step.Quantity))); } plan = CapacityPlanner.Plan(player2, list, inventory.GetTotalWeight(), player.GetMaxCarryWeight()); if (!plan.FitsWeight) { failure = "the complete expedition kit is too heavy"; return false; } if (!plan.FitsSlots) { failure = "the complete expedition kit does not fit in inventory"; return false; } return true; } private static bool ExecuteAtomic(Player player, NearbyResourceCapture capture, ResourceWithdrawalPlan withdrawal, ExpeditionCapacityPlan capacity, IReadOnlyList<ContainerReservation> reservations, out string failure) { //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_030b: Unknown result type (might be due to invalid IL or missing references) //IL_0310: Unknown result type (might be due to invalid IL or missing references) //IL_0339: Unknown result type (might be due to invalid IL or missing references) //IL_0340: Unknown result type (might be due to invalid IL or missing references) //IL_0398: Unknown result type (might be due to invalid IL or missing references) //IL_039f: Unknown result type (might be due to invalid IL or missing references) //IL_0405: Unknown result type (might be due to invalid IL or missing references) //IL_0407: Unknown result type (might be due to invalid IL or missing references) failure = null; Inventory inventory = ((Humanoid)player).GetInventory(); CompatibilityCatalog compatibilityCatalog = new CompatibilityCatalog(); foreach (ItemData allItem in inventory.GetAllItems()) { compatibilityCatalog.KeyFor(allItem); } foreach (ResourceWithdrawalStep step in withdrawal.Steps) { compatibilityCatalog.KeyFor(capture.RuntimeStacks[step.StackId].Item); } Dictionary<Container, ContainerReservation> dictionary = reservations.ToDictionary((ContainerReservation reservation) => reservation.Container); List<ExpeditionInventoryBackup> list = new List<ExpeditionInventoryBackup> { new ExpeditionInventoryBackup(inventory, preserveItemIdentity: true) }; list.AddRange(reservations.Select((ContainerReservation reservation) => new ExpeditionInventoryBackup(reservation.Container.GetInventory(), preserveItemIdentity: false))); List<CompletedExpeditionMove> list2 = new List<CompletedExpeditionMove>(); Dictionary<string, int> dictionary2 = new Dictionary<string, int>(StringComparer.Ordinal); try { foreach (ExpeditionDestinationStep step2 in capacity.Steps) { if (!capture.RuntimeStacks.TryGetValue(step2.SourceStackId, out var value) || value.Container == null) { failure = "planned expedition source disappeared"; return RollbackOrDisable(player, capture.Scope, reservations, list2, list, failure, out failure); } if (!dictionary.TryGetValue(value.Container.Container, out var value2) || !NearbyResourceService.ReservationMatches(player, capture.Scope, value2)) { failure = "required storage reservation changed before transfer"; return RollbackOrDisable(player, capture.Scope, reservations, list2, list, failure, out failure); } int value3; int num = (dictionary2.TryGetValue(step2.SourceStackId, out value3) ? value3 : 0); ItemData itemAt = value.Inventory.GetItemAt(value.Item.m_gridPos.x, value.Item.m_gridPos.y); if (itemAt != value.Item || itemAt.m_stack != value.Item.m_stack || itemAt.m_stack < step2.Quantity || !string.Equals(compatibilityCatalog.KeyFor(itemAt), step2.CompatibilityKey, StringComparison.Ordinal)) { failure = "planned expedition source stack changed before transfer"; return RollbackOrDisable(player, capture.Scope, reservations, list2, list, failure, out failure); } Vector2i val = InventorySnapshots.PositionForSlot(inventory, step2.DestinationSlot); ItemData itemAt2 = inventory.GetItemAt(val.x, val.y); if ((itemAt2 == null && step2.ExpectedDestinationQuantity != 0) || (itemAt2 != null && (itemAt2.m_stack != step2.ExpectedDestinationQuantity || !string.Equals(compatibilityCatalog.KeyFor(itemAt2), step2.CompatibilityKey, StringComparison.Ordinal)))) { failure = "player inventory changed before expedition transfer"; return RollbackOrDisable(player, capture.Scope, reservations, list2, list, failure, out failure); } ItemData val2 = itemAt.Clone(); val2.m_stack = step2.Quantity; Vector2i gridPos = itemAt.m_gridPos; int num2 = TotalUnits(value.Inventory); int num3 = TotalUnits(inventory); bool flag; try { flag = inventory.MoveItemToThis(value.Inventory, itemAt, step2.Quantity, val.x, val.y); } catch (Exception ex) { Plugin plugin = RuntimeContext.Plugin; if (plugin != null) { plugin.Log.LogError((object)("Expedition-kit transfer primitive threw: " + ex)); } throw; } int num4 = TotalUnits(value.Inventory); int num5 = TotalUnits(inventory); ItemData itemAt3 = inventory.GetItemAt(val.x, val.y); if (num4 == num2 - step2.Quantity && num5 == num3 + step2.Quantity && itemAt3 != null && itemAt3.m_stack == step2.ExpectedDestinationQuantity + step2.Quantity && string.Equals(compatibilityCatalog.KeyFor(itemAt3), step2.CompatibilityKey, StringComparison.Ordinal)) { list2.Add(new CompletedExpeditionMove(value, val2, gridPos, val, step2.CompatibilityKey, step2.Quantity, value2)); dictionary2[step2.SourceStackId] = num + step2.Quantity; if (!value2.AdvanceDataRevisionAfterMutation() || !NearbyResourceService.ReservationMatches(player, capture.Scope, value2)) { failure = "required storage reservation changed after transfer"; return RollbackOrDisable(player, capture.Scope, reservations, list2, list, failure, out failure); } continue; } if (!flag && num4 == num2 && num5 == num3) { failure = "game transfer primitive declined the expedition move"; return RollbackOrDisable(player, capture.Scope, reservations, list2, list, failure, out failure); } failure = "an unexpected expedition transfer result required full rollback"; if (!RestoreBackups(list, reservations)) { RuntimeContext.Disable("Expedition-kit rollback could not restore every inventory."); failure += "; rollback could not restore every item"; } else { RuntimeContext.Disable("Unexpected expedition-kit transfer result; inventories were restored."); failure += "; inventories were restored and Stackmaster was disabled"; } return false; } int requiredUnits = withdrawal.RequiredUnits; if (list2.Sum((CompletedExpeditionMove move) => move.Quantity) != requiredUnits) { failure = "expedition transfer did not complete the exact kit"; return RollbackOrDisable(player, capture.Scope, reservations, list2, list, failure, out failure); } return true; } catch (Exception ex2) { Plugin plugin2 = RuntimeContext.Plugin; if (plugin2 != null) { plugin2.Log.LogError((object)("Expedition-kit transaction threw after mutation began: " + ex2)); } bool flag2 = false; try { flag2 = RestoreBackups(list, reservations); } catch (Exception ex3) { Plugin plugin3 = RuntimeContext.Plugin; if (plugin3 != null) { plugin3.Log.LogError((object)("Expedition-kit emergency snapshot rollback threw: " + ex3)); } } if (flag2) { failure = "an expedition transfer exception required full rollback; inventories were restored and Stackmaster was disabled"; RuntimeContext.Disable("Expedition-kit transfer exception; inventories were restored."); } else { failure = "an expedition transfer exception required full rollback; rollback could not restore every item"; RuntimeContext.Disable("Expedition-kit rollback could not restore every inventory."); } NearbyResourceService.ResetCaches(); NearbyBuildHudPatch.ResetCache(); return false; } } private static bool RollbackOrDisable(Player player, StorageScope scope, IReadOnlyList<ContainerReservation> reservations, IList<CompletedExpeditionMove> completed, IReadOnlyList<ExpeditionInventoryBackup> backups, string reason, out string failure) { bool flag = false; try { flag = RollbackCompleted(player, scope, completed); } catch (Exception ex) { Plugin plugin = RuntimeContext.Plugin; if (plugin != null) { plugin.Log.LogError((object)("Expedition-kit reverse rollback threw; restoring snapshots: " + ex)); } } if (!flag) { try { flag = RestoreBackups(backups, reservations); } catch (Exception ex2) { Plugin plugin2 = RuntimeContext.Plugin; if (plugin2 != null) { plugin2.Log.LogError((object)("Expedition-kit snapshot rollback threw: " + ex2)); } } } failure = (flag ? (reason + "; all transfers were rolled back") : (reason + "; rollback could not restore every item")); if (!flag) { RuntimeContext.Disable("Expedition-kit rollback could not restore every inventory."); } NearbyResourceService.ResetCaches(); NearbyBuildHudPatch.ResetCache(); return false; } private static bool RollbackCompleted(Player player, StorageScope scope, IEnumerable<CompletedExpeditionMove> completed) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) Inventory inventory = ((Humanoid)player).GetInventory(); CompatibilityCatalog compatibilityCatalog = new CompatibilityCatalog(); foreach (ItemData allItem in inventory.GetAllItems()) { compatibilityCatalog.KeyFor(allItem); } foreach (CompletedExpeditionMove item in completed) { compatibilityCatalog.KeyFor(item.SourceClone); } foreach (CompletedExpeditionMove item2 in completed.Reverse()) { if (!NearbyResourceService.ReservationMatches(player, scope, item2.Reservation)) { return false; } ItemData itemAt = inventory.GetItemAt(item2.DestinationPosition.x, item2.DestinationPosition.y); if (itemAt == null || itemAt.m_stack < item2.Quantity || !string.Equals(compatibilityCatalog.KeyFor(itemAt), item2.CompatibilityKey, StringComparison.Ordinal)) { return false; } int num = TotalUnits(item2.Source.Inventory); item2.SourceClone.m_stack = item2.Quantity; item2.SourceClone.m_gridPos = item2.SourcePosition; if (!(AddItemAtMethod != null) || !(bool)AddItemAtMethod.Invoke(item2.Source.Inventory, new object[5] { item2.SourceClone, item2.Quantity, item2.SourcePosition.x, item2.SourcePosition.y, true }) || TotalUnits(item2.Source.Inventory) != num + item2.Quantity) { return false; } int num2 = TotalUnits(inventory); if (!inventory.RemoveItem(itemAt, item2.Quantity) || TotalUnits(inventory) != num2 - item2.Quantity || !item2.Reservation.AdvanceDataRevisionAfterMutation() || !NearbyResourceService.ReservationMatches(player, scope, item2.Reservation)) { return false; } } return true; } private static bool RestoreBackups(IEnumerable<ExpeditionInventoryBackup> backups, IEnumerable<ContainerReservation> reservations) { if (InventoryItemsField == null) { return false; } ExpeditionSnapshotRestoreTarget[] array; List<ItemData>[] array2; try { array = backups.Select((ExpeditionInventoryBackup backup) => backup.PrepareRestore()).ToArray(); array2 = array.Select((ExpeditionSnapshotRestoreTarget target) => target.Items.RebuildInventoryList()).ToArray(); if (array2.Any((List<ItemData> items) => items.Any((ItemData item) => item == null))) { return false; } } catch (Exception ex) { Plugin plugin = RuntimeContext.Plugin; if (plugin != null) { plugin.Log.LogError((object)("Expedition-kit snapshot preparation failed: " + ex)); } return false; } ExpeditionSnapshotRestoreTarget[] array3; try { array3 = array; foreach (ExpeditionSnapshotRestoreTarget expeditionSnapshotRestoreTarget in array3) { object value = InventoryItemsField.GetValue(expeditionSnapshotRestoreTarget.Inventory); InventoryItemsField.SetValue(expeditionSnapshotRestoreTarget.Inventory, value); if (value == null || InventoryItemsField.GetValue(expeditionSnapshotRestoreTarget.Inventory) != value) { return false; } } } catch (Exception ex2) { Plugin plugin2 = RuntimeContext.Plugin; if (plugin2 != null) { plugin2.Log.LogError((object)("Expedition-kit snapshot field preflight failed: " + ex2)); } return false; } bool flag = true; for (int num2 = 0; num2 < array.Length; num2++) { ExpeditionSnapshotRestoreTarget expeditionSnapshotRestoreTarget2 = array[num2]; try { expeditionSnapshotRestoreTarget2.Items.RestoreStates(RestoreItemDataState); InventoryItemsField.SetValue(expeditionSnapshotRestoreTarget2.Inventory, array2[num2]); } catch (Exception ex3) { flag = false; Plugin plugin3 = RuntimeContext.Plugin; if (plugin3 != null) { plugin3.Log.LogError((object)("Expedition-kit identity-preserving restore failed: " + ex3)); } } } array3 = array; foreach (ExpeditionSnapshotRestoreTarget expeditionSnapshotRestoreTarget3 in array3) { try { flag &= expeditionSnapshotRestoreTarget3.Items.HasExactOriginalReferences(expeditionSnapshotRestoreTarget3.Inventory.GetAllItems()) && expeditionSnapshotRestoreTarget3.Items.StatesMatch(ItemDataStateMatches) && TotalUnits(expeditionSnapshotRestoreTarget3.Inventory) == expeditionSnapshotRestoreTarget3.TotalUnits; expeditionSnapshotRestoreTarget3.Inventory.m_onChanged?.Invoke(); } catch (Exception ex4) { flag = false; Plugin plugin4 = RuntimeContext.Plugin; if (plugin4 != null) { plugin4.Log.LogError((object)("Expedition-kit restored-inventory notification failed: " + ex4)); } } } foreach (ContainerReservation reservation in reservations) { try { flag &= reservation.AdvanceDataRevisionAfterMutation(); } catch (Exception ex5) { flag = false; Plugin plugin5 = RuntimeContext.Plugin; if (plugin5 != null) { plugin5.Log.LogError((object)("Expedition-kit rollback revision update failed: " + ex5)); } } } return flag; } private static void RestoreItemDataState(ItemData original, ItemData snapshot) { //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) original.m_shared = snapshot.m_shared; original.m_stack = snapshot.m_stack; original.m_durability = snapshot.m_durability; original.m_equipped = snapshot.m_equipped; original.m_quality = snapshot.m_quality; original.m_variant = snapshot.m_variant; original.m_crafterID = snapshot.m_crafterID; original.m_crafterName = snapshot.m_crafterName; original.m_worldLevel = snapshot.m_worldLevel; original.m_pickedUp = snapshot.m_pickedUp; original.m_cheated = snapshot.m_cheated; original.m_gridPos = snapshot.m_gridPos; original.m_dropPrefab = snapshot.m_dropPrefab; original.m_lastAttackTime = snapshot.m_lastAttackTime; original.m_lastProjectile = snapshot.m_lastProjectile; original.m_customData = snapshot.m_customData; } private static bool ItemDataStateMatches(ItemData original, ItemData snapshot) { //IL_010d: Unknown result type (might be due to invalid IL or missing references) if (original.m_shared != snapshot.m_shared || original.m_stack != snapshot.m_stack || !original.m_durability.Equals(snapshot.m_durability) || original.m_equipped != snapshot.m_equipped || original.m_quality != snapshot.m_quality || original.m_variant != snapshot.m_variant || original.m_crafterID != snapshot.m_crafterID || !string.Equals(original.m_crafterName, snapshot.m_crafterName, StringComparison.Ordinal) || original.m_worldLevel != snapshot.m_worldLevel || original.m_pickedUp != snapshot.m_pickedUp || original.m_cheated != snapshot.m_cheated || !((Vector2i)(ref original.m_gridPos)).Equals(snapshot.m_gridPos) || original.m_dropPrefab != snapshot.m_dropPrefab || !original.m_lastAttackTime.Equals(snapshot.m_lastAttackTime) || original.m_lastProjectile != snapshot.m_lastProjectile) { return false; } if (original.m_customData == null || snapshot.m_customData == null) { if (original.m_customData == null) { return snapshot.m_customData == null; } return false; } string value; if (original.m_customData.Count == snapshot.m_customData.Count) { return snapshot.m_customData.All((KeyValuePair<string, string> pair) => original.m_customData.TryGetValue(pair.Key, out value) && string.Equals(value, pair.Value, StringComparison.Ordinal)); } return false; } private static int TotalUnits(Inventory inventory) { return inventory.GetAllItems().Sum((ItemData item) => item.m_stack); } private static void DisableAfterFatalFailure(string reason) { try { RuntimeContext.Disable(reason); } catch (Exception ex) { Plugin plugin = RuntimeContext.Plugin; if (plugin != null) { plugin.Log.LogError((object)("Stackmaster fatal-disable fallback failed: " + ex)); } } } private static void ShowFailure(string failure) { string text = (string.IsNullOrWhiteSpace(failure) ? "the complete kit could not be transferred" : failure); Plugin plugin = RuntimeContext.Plugin; if (plugin != null) { plugin.Log.LogWarning((object)("Expedition-kit action rejected safely: " + text + ".")); } RuntimeContext.ShowCenter("Stackmaster: " + text + ". Nothing was moved."); } } internal static class HarmonyTargetManifest { private static readonly IReadOnlyList<HarmonyTargetDescriptor> Targets = new HarmonyTargetDescriptor[32] { Postfix(typeof(InventoryGui), "Awake", isStatic: false, typeof(void), Type.EmptyTypes, typeof(InventoryGuiAwakePatch)), Both(typeof(InventoryGui), "Hide", isStatic: false, typeof(void), Type.EmptyTypes, typeof(InventoryGuiHidePatch)), Postfix(typeof(InventoryGui), "Show", isStatic: false, typeof(void), new Type[2] { typeof(Container), typeof(int) }, typeof(InventoryGuiShowPatch)), Both(typeof(InventoryGui), "Update", isStatic: false, typeof(void), Type.EmptyTypes, typeof(InventoryGuiStorageActionPatch)), Transactional(typeof(InventoryGui), "OnCraftPressed", isStatic: false, typeof(void), Type.EmptyTypes, typeof(CraftingStartPatch)), Prefix(typeof(InventoryGui), "OnCraftCancelPressed", isStatic: false, typeof(void), Type.EmptyTypes, typeof(CraftingCancelPatch)), Prefix(typeof(InventoryGui), "OnTabCraftPressed", isStatic: false, typeof(void), Type.EmptyTypes, typeof(CraftingSelectionPatch)), Prefix(typeof(InventoryGui), "OnTabUpgradePressed", isStatic: false, typeof(void), Type.EmptyTypes, typeof(CraftingSelectionPatch)), Prefix(typeof(InventoryGui), "OnSelectedRecipe", isStatic: false, typeof(void), new Type[1] { typeof(GameObject) }, typeof(CraftingSelectionPatch)), Postfix(typeof(InventoryGrid), "UpdateInventory", isStatic: false, typeof(void), new Type[3] { typeof(Inventory), typeof(Player), typeof(ItemData) }, typeof(InventoryGridUpdateInventoryPatch)), Prefix(typeof(InventoryGui), "OnSelectedItem", isStatic: false, typeof(void), new Type[4] { typeof(InventoryGrid), typeof(ItemData), typeof(Vector2i), typeof(Modifier) }, typeof(InventoryProtectionClickPatch)), Prefix(typeof(InventoryGui), "OnRightClickItem", isStatic: false, typeof(void), new Type[3] { typeof(InventoryGrid), typeof(ItemData), typeof(Vector2i) }, typeof(InventoryProtectionRightClickPatch)), Both(typeof(InventoryGui), "OnSelectedItem", isStatic: false, typeof(void), new Type[4] { typeof(InventoryGrid), typeof(ItemData), typeof(Vector2i), typeof(Modifier) }, typeof(ManualProtectionExitSelectionPatch)), Both(typeof(InventoryGui), "OnDropOutside", isStatic: false, typeof(void), Type.EmptyTypes, typeof(ManualProtectionExitDropOutsidePatch)), Prefix(typeof(Container), "Interact", isStatic: false, typeof(bool), new Type[3] { typeof(Humanoid), typeof(bool), typeof(bool) }, typeof(ContainerInteractPatch)), Postfix(typeof(Container), "GetHoverText", isStatic: false, typeof(string), Type.EmptyTypes, typeof(ContainerHoverTextPatch)), Postfix(typeof(Container), "RPC_RequestOpen", isStatic: false, typeof(void), new Type[2] { typeof(long), typeof(long) }, typeof(ContainerOpenRequestLeasePatch)), Prefix(typeof(Container), "RPC_StackResponse", isStatic: false, typeof(void), new Type[2] { typeof(long), typeof(bool) }, typeof(ContainerStackResponsePatch), preserveDuringCleanup: true), Prefix(typeof(Game), "Shutdown", isStatic: false, typeof(void), new Type[1] { typeof(bool) }, typeof(OwnershipLifecyclePatch)), Prefix(typeof(ZNet), "Shutdown", isStatic: false, typeof(void), new Type[1] { typeof(bool) }, typeof(OwnershipLifecyclePatch)), Prefix(typeof(ZNet), "ShutdownWithoutSave", isStatic: false, typeof(void), new Type[1] { typeof(bool) }, typeof(OwnershipLifecyclePatch)), Postfix(typeof(ZNet), "Update", isStatic: false, typeof(void), Type.EmptyTypes, typeof(OwnershipSafetyUpdatePatch), "Postfix", preserveDuringCleanup: true), Postfix(typeof(Player), "HaveRequirementItems", isStatic: false, typeof(bool), new Type[4] { typeof(Recipe), typeof(bool), typeof(int), typeof(int) }, typeof(NearbyRequirementPatches), "RecipePostfix"), Postfix(typeof(Player), "HaveRequirements", isStatic: false, typeof(bool), new Type[2] { typeof(Piece), typeof(RequirementMode) }, typeof(NearbyRequirementPatches), "PiecePostfix"), Postfix(typeof(Hud), "SetupPieceInfo", isStatic: false, typeof(void), new Type[1] { typeof(Piece) }, typeof(NearbyBuildHudPatch)), Prefix(typeof(BuildUi), "OnSelectPiece", isStatic: false, typeof(void), new Type[1] { typeof(Piece) }, typeof(ExpeditionKitClickPatch)), Both(typeof(InventoryGui), "SetupRequirement", isStatic: true, typeof(bool), new Type[6] { typeof(Transform), typeof(Requirement), typeof(Player), typeof(bool), typeof(int), typeof(int) }, typeof(NearbyCraftingHudPatch)), Postfix(typeof(Player), "GetFirstRequiredItem", isStatic: false, typeof(ItemData), new Type[6] { typeof(Inventory), typeof(Recipe), typeof(int), typeof(int).MakeByRefType(), typeof(int).MakeByRefType(), typeof(int) }, typeof(NearbyFirstRequiredItemPatch)), Transactional(typeof(InventoryGui), "DoCrafting", isStatic: false, typeof(void), new Type[1] { typeof(Player) }, typeof(NearbyCraftingActionPatch)), Transactional(typeof(Player), "UpdatePlacement", isStatic: false, typeof(void), new Type[2] { typeof(bool), typeof(float) }, typeof(NearbyBuildingActionPatch)), Both(typeof(Player), "TryPlacePiece", isStatic: false, typeof(bool), new Type[1] { typeof(Piece) }, typeof(NearbyTryPlacePiecePatch)), Prefix(typeof(Inventory), "RemoveItem", isStatic: false, typeof(void), new Type[4] { typeof(string), typeof(int), typeof(int), typeof(bool) }, typeof(NearbyResourceRemovalPatch)) }; internal static IReadOnlyList<HarmonyTargetDescriptor> Descriptors => Targets; internal static IReadOnlyList<PatchInstaller.PatchSpec> ResolveAll() { return Resolve(Targets); } internal static IReadOnlyList<PatchInstaller.PatchSpec> ResolveCleanupSafety() { return Resolve(Targets.Where((HarmonyTargetDescriptor target) => target.PreserveDuringCleanup).ToArray()); } private static IReadOnlyList<PatchInstaller.PatchSpec> Resolve(IReadOnlyList<HarmonyTargetDescriptor> targets) { List<PatchInstaller.PatchSpec> list = new List<PatchInstaller.PatchSpec>(targets.Count); foreach (HarmonyTargetDescriptor target in targets) { list.Add(target.Resolve()); } return list; } internal static void Validate(ICollection<string> failures) { try { ResolveAll(); } catch (Exception ex) { failures.Add("Harmony target manifest failed: " + ex.Message); } } private static HarmonyTargetDescriptor Prefix(Type targetType, string targetName, bool isStatic, Type returnType, Type[] parameters, Type patchType, bool preserveDuringCleanup = false) { return new HarmonyTargetDescriptor(targetType, targetName, isStatic, returnType, parameters, patchType, "Prefix", null, null, preserveDuringCleanup); } private static HarmonyTargetDescriptor Postfix(Type targetType, string targetName, bool isStatic, Type returnType, Type[] parameters, Type patchType, string patchName = "Postfix", bool preserveDuringCleanup = false) { return new HarmonyTargetDescriptor(targetType, targetName, isStatic, returnType, parameters, patchType, null, patchName, null, preserveDuringCleanup); } private static HarmonyTargetDescriptor Both(Type targetType, string targetName, bool isStatic, Type returnType, Type[] parameters, Type patchType) { return new HarmonyTargetDescriptor(targetType, targetName, isStatic, returnType, parameters, patchType, "Prefix", "Postfix", null); } private static HarmonyTargetDescriptor Transactional(Type targetType, string targetName, bool isStatic, Type returnType, Type[] parameters, Type patchType) { return new HarmonyTargetDescriptor(targetType, targetName, isStatic, returnType, parameters, patchType, "Prefix", "Postfix", "Finalizer"); } } internal sealed class HarmonyTargetDescriptor { internal Type TargetType { get; } internal string TargetName { get; } internal bool IsStatic { get; } internal Type ReturnType { get; } internal Type[] Parameters { get; } internal Type PatchType { get; } internal string PrefixName { get; } internal string PostfixName { get; } internal string FinalizerName { get; } internal bool PreserveDuringCleanup { get; } internal string Identity => TargetType.FullName + "." + TargetName + "(" + string.Join(",", Parameters.Select((Type type) => type.FullName).ToArray()) + ")"; internal HarmonyTargetDescriptor(Type targetType, string targetName, bool isStatic, Type returnType, Type[] parameters, Type patchType, string prefixName, string postfixName, string finalizerName, bool preserveDuringCleanup = false) { TargetType = targetType; TargetName = targetName; IsStatic = isStatic; ReturnType = returnType; Parameters = parameters; PatchType = patchType; PrefixName = prefixName; PostfixName = postfixName; FinalizerName = finalizerName; PreserveDuringCleanup = preserveDuringCleanup; } internal PatchInstaller.PatchSpec Resolve() { MethodInfo original = RuntimeContractValidator.ResolveExactMethod(TargetType, TargetName, IsStatic, declaredOnly: true, ReturnType, Parameters); MethodInfo prefix = ResolvePatch(PrefixName); MethodInfo postfix = ResolvePatch(PostfixName); MethodInfo finalizer = ResolvePatch(FinalizerName); HarmonyPatchCompatibility.Validate(original, prefix, postfix, finalizer); return new PatchInstaller.PatchSpec(original, prefix, postfix, finalizer); } private MethodInfo ResolvePatch(string name) { if (!string.IsNullOrEmpty(name)) { return RuntimeContractValidator.ResolveUniqueNamedMethod(PatchType, name, mustBeStatic: true); } return null; } } internal static class HarmonyPatchCompatibility { internal static void Validate(MethodInfo original, MethodInfo prefix, MethodInfo postfix, MethodInfo finalizer) { ValidatePatch(original, prefix, "prefix"); ValidatePatch(original, postfix, "postfix"); ValidatePatch(original, finalizer, "finalizer"); ValidateReturns(prefix, postfix, finalizer); ValidateState(prefix, postfix, finalizer); } private static void ValidateReturns(MethodInfo prefix, MethodInfo postfix, MethodInfo finalizer) { if (prefix != null && prefix.ReturnType != typeof(void) && prefix.ReturnType != typeof(bool)) { throw new InvalidOperationException(prefix.DeclaringType.Name + "." + prefix.Name + " has an invalid Harmony prefix return type"); } if (postfix != null && postfix.ReturnType != typeof(void)) { throw new InvalidOperationException(postfix.DeclaringType.Name + "." + postfix.Name + " has an invalid Harmony postfix return type"); } if (finalizer != null && finalizer.ReturnType != typeof(void) && finalizer.ReturnType != typeof(Exception)) { throw new InvalidOperationException(finalizer.DeclaringType.Name + "." + finalizer.Name + " has an invalid Harmony finalizer return type"); } } private static void ValidateState(MethodInfo prefix, MethodInfo postfix, MethodInfo finalizer) { ParameterInfo parameterInfo = prefix?.GetParameters().FirstOrDefault((ParameterInfo parameter) => parameter.Name == "__state"); ParameterInfo[] array = (from parameter in new MethodInfo[2] { postfix, finalizer }.Where((MethodInfo value) => value != null).SelectMany((MethodInfo value) => value.GetParameters()) where parameter.Name == "__state" select parameter).ToArray(); if (parameterInfo == null) { if (array.Length != 0) { throw new InvalidOperationException("Harmony __state is consumed without a prefix producer"); } return; } if (!parameterInfo.ParameterType.IsByRef) { throw new InvalidOperationException(prefix.DeclaringType.Name + "." + prefix.Name + " must produce Harmony __state by ref or out"); } Type type = ElementType(parameterInfo.ParameterType); ParameterInfo[] array2 = array; foreach (ParameterInfo parameterInfo2 in array2) { if (ElementType(parameterInfo2.ParameterType) != type) { throw new InvalidOperationException("Harmony __state types disagree for " + parameterInfo2.Member.DeclaringType.Name); } } } private static void ValidatePatch(MethodInfo original, MethodInfo patch, string role) { if (patch == null) { return; } if (!patch.IsStatic) { throw new InvalidOperationException(patch.DeclaringType.Name + "." + patch.Name + " " + role + " must be static"); } ParameterInfo[] parameters = original.GetParameters(); ParameterInfo[] parameters2 = patch.GetParameters(); foreach (ParameterInfo parameterInfo in parameters2) { string name = parameterInfo.Name ?? string.Empty; if (name == "__instance") { if (original.IsStatic || ElementType(parameterInfo.ParameterType) != original.DeclaringType) { throw new InvalidOperationException(patch.DeclaringType.Name + "." + patch.Name + " has incompatible __instance"); } } else if (name == "__result") { if (original.ReturnType == typeof(void) || ElementType(parameterInfo.ParameterType) != original.ReturnType) { throw new InvalidOperationException(patch.DeclaringType.Name + "." + patch.Name + " has incompatible __result"); } } else { if (name == "__state") { continue; } if (name == "__runOriginal") { if (ElementType(parameterInfo.ParameterType) != typeof(bool)) { throw new InvalidOperationException(patch.DeclaringType.Name + "." + patch.Name + " has incompatible __runOriginal"); } continue; } if (name == "__exception") { if (role != "finalizer" || ElementType(parameterInfo.ParameterType) != typeof(Exception)) { throw new InvalidOperationException(patch.DeclaringType.Name + "." + patch.Name + " has incompatible __exception"); } continue; } if (name.StartsWith("___", StringComparison.Ordinal)) { if (original.IsStatic) { throw new InvalidOperationException(patch.DeclaringType.Name + "." + patch.Name + " cannot inject an instance field for a static original"); } string fieldName = name.Substring(3); FieldInfo[] array = (from field in original.DeclaringType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where field.Name == fieldName select field).ToArray(); if (array.Length != 1 || ElementType(parameterInfo.ParameterType) != array[0].FieldType) { throw new InvalidOperationException(patch.DeclaringType.Name + "." + patch.Name + " has incompatible field injection " + name); } continue; } HarmonyArgument[] array2 = parameterInfo.GetCustomAttributes(typeof(HarmonyArgument), inherit: false).Cast<HarmonyAr