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 BestAutoSort v0.6.28
plugins/BestAutoSort.dll
Decompiled 16 hours 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.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using BestAutoSort; using BestAutoSort.Core; using BestAutoSort.Patches; using BestAutoSort.Runtime; using BestAutoSort.Tx; using BestAutoSort.TxCore; using HarmonyLib; using Microsoft.CodeAnalysis; using Splatform; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyCompany("maks2204")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.6.1.0")] [assembly: AssemblyInformationalVersion("0.6.1")] [assembly: AssemblyProduct("BestAutoSort")] [assembly: AssemblyTitle("BestAutoSort")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.6.1.0")] [module: UnverifiableCode] [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; } } } [HarmonyPatch(typeof(Container), "RPC_OpenResponse")] internal static class TxContainerOpenResponsePatch { private static void Postfix(Container __instance, long uid, bool granted) { if (!granted && Plugin.IsActive) { Plugin.LogInstance.LogInfo((object)"[ChestTX] open denied by manager (busy/lease/access?)"); } } } namespace BestAutoSort { [BepInPlugin("dev.maks2204.bestautosort", "BestAutoSort", "0.6.1")] [BepInProcess("valheim.exe")] [BepInProcess("valheim_server.exe")] [BepInIncompatibility("goldenrevolver.quick_stack_store")] [BepInIncompatibility("com.maxsch.valheim.MultiUserChest")] [BepInIncompatibility("Narolith.AutoFeed")] public sealed class Plugin : BaseUnityPlugin { internal const string PluginGuid = "dev.maks2204.bestautosort"; internal const string PluginName = "BestAutoSort"; internal const string PluginVersion = "0.6.28"; private Harmony? _harmony; private bool _shutDown; internal static Plugin Instance { get; private set; } internal static ManualLogSource LogInstance { get; private set; } internal QuickStackService QuickStackService { get; private set; } internal static bool IsActive { get; private set; } private void Awake() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown IsActive = false; _shutDown = false; Instance = this; LogInstance = ((BaseUnityPlugin)this).Logger; try { ModConfig.Bind(ConfigFileMigration.Open((BaseUnityPlugin)this, Paths.ConfigPath, ((BaseUnityPlugin)this).Logger)); QuickStackService = new QuickStackService(this); StorageCommands.Register(); _harmony = new Harmony("dev.maks2204.bestautosort"); PatchAllSafely(); IsActive = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"BestAutoSort 0.6.28 (2867975) loaded."); try { ((BaseUnityPlugin)this).Logger.LogInfo((object)("[ChestTX] config: TxVerbose=" + ModConfig.TxVerbose.Value + " ChestAuthorityMode=" + ModConfig.ChestAuthorityMode.Value.ToString() + " effective=" + ServerAuthority.EffectiveMode())); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("[ChestTX] config dump failed: " + ex.Message)); } } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)string.Format("{0} failed to initialize safely: {1}", "BestAutoSort", arg)); Shutdown(); throw; } } private void PatchAllSafely() { Type[] array = (from type2 in typeof(Plugin).Assembly.GetTypes() where type2.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Length != 0 select type2).OrderBy<Type, string>((Type type2) => type2.FullName, StringComparer.Ordinal).ToArray(); int num = 0; Type[] array2 = array; foreach (Type type in array2) { try { _harmony.PatchAll(type); num++; } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Failed to apply patch class {type.FullName}; other BestAutoSort features will remain available: {arg}"); } } ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Applied {num} of {array.Length} BestAutoSort patch classes."); if (ServerReleaseGuard.PatchedSites != 2) { ((BaseUnityPlugin)this).Logger.LogError((object)("ServerReleaseGuard transpiler applied to " + ServerReleaseGuard.PatchedSites + " of " + 2 + " ReleaseNearbyZDOS ownership sites - redistribution exclusion DISABLED, game update likely changed (SetOwner backstop still holds fail-closed).")); } else { ((BaseUnityPlugin)this).Logger.LogInfo((object)("ServerReleaseGuard rerouted " + ServerReleaseGuard.PatchedSites + " ReleaseNearbyZDOS ownership site(s) through the server-authority guard.")); } if (TxRenderPatch.ReplacedCount == 0) { ((BaseUnityPlugin)this).Logger.LogError((object)"TxRenderPatch transpiler found no Container.IsOwner call in InventoryGui.UpdateContainer — multi-viewer rendering is DISABLED, game update likely changed."); } else { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"TxRenderPatch replaced {TxRenderPatch.ReplacedCount} owner-check(s) for shared rendering."); } } private void Update() { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (!IsActive) { return; } ChestTxService.Pump(); AutoFeedService.Update(); NearbyPlaceIntent.Pump(); NearbyResourceService.RepairInvalidPlayerInventoryPositions(); if (ChestUpgradeService.UpdateLegacyMigration()) { return; } ChestRuleEditor.Update(); InventoryButtons.UpdateVisibility(); if (ModConfig.Enabled.Value && CanAcceptShortcut()) { if (IsShortcutDownAllowingOtherKeys(ModConfig.QuickStackShortcut.Value)) { QuickStackNearby(); } KeyboardShortcut value = ModConfig.SortChestShortcut.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { SortOpenChest(); } } } internal void QuickStackNearby() { if (!IsActive) { return; } try { QuickStackService.BeginNearbyQuickStack(RestockProfileService.RestockNearby); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Nearby quick stack failed: {arg}"); } } internal void SortOpenChest() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown if (!IsActive) { return; } try { Container val = InventoryAccess.CurrentContainer(InventoryGui.instance); if ((Object)val == (Object)null) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { ((Character)localPlayer).Message((MessageType)2, "Open a chest to sort it.", 0, (Sprite)null, false); } } else if (!ChestTxService.IsShared(val)) { if (ChestTxService.IsManager(val) && !ChestTxService.IsQuarantined(val)) { SortLocal(val); } else if (ChestTxService.IsQuarantined(val)) { TxLog.Warn("sort refused: chest quarantined (fail closed)"); Player localPlayer2 = Player.m_localPlayer; if ((Object)localPlayer2 != (Object)null) { ((Character)localPlayer2).Message((MessageType)2, "Chest state is still being verified. Try sorting again shortly.", 0, (Sprite)null, false); } } } else { ChestTxService.RequestSort(val, (int)ModConfig.ChestSortMode.Value, ModConfig.SortDescending.Value); } } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Chest sort failed: {arg}"); } } internal static void SortLocal(Container container) { int num = InventorySorter.Sort(container.GetInventory(), ModConfig.ChestSortMode.Value, ModConfig.SortDescending.Value); TxReflect.UpdateRows(container); TxReflect.SaveContainer(container); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { ((Character)localPlayer).Message((MessageType)2, string.Format("Sorted {0} item stack{1} by {2}.", num, (num == 1) ? string.Empty : "s", ModConfig.ChestSortMode.Value), 0, (Sprite)null, false); } } internal void UpgradeOpenChest(int targetTier) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown if (!IsActive) { return; } try { Container val = InventoryAccess.CurrentContainer(InventoryGui.instance); if ((Object)val == (Object)null) { return; } ServerAuthority.EnsureServerOwnership(val, "upgrade-open"); if (ServerAuthority.IsAuthorityMode() && ServerAuthority.IsServerManagedContainer(val)) { ChestUpgradeService.UpgradeOpenChest(targetTier); return; } if (!ChestTxService.IsShared(val) || ChestTxService.IsManager(val)) { ChestUpgradeService.UpgradeOpenChest(targetTier); return; } if (ChestTxService.AcquireForStructural(val)) { ChestUpgradeService.UpgradeOpenChest(targetTier); return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { ((Character)localPlayer).Message((MessageType)2, "Could not acquire the chest for upgrade. Try again.", 0, (Sprite)null, false); } } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Chest upgrade failed: {arg}"); Player localPlayer2 = Player.m_localPlayer; if ((Object)(object)localPlayer2 != (Object)null) { ((Character)localPlayer2).Message((MessageType)2, "Chest upgrade failed. See the log for details.", 0, (Sprite)null, false); } } } private static bool CanAcceptShortcut() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown if ((Object)Player.m_localPlayer == (Object)null) { return false; } if (ChestRuleEditor.IsOpen) { return false; } if ((Object)Chat.instance != (Object)null && Chat.instance.HasFocus()) { return false; } if (Console.IsVisible() || Menu.IsVisible() || TextInput.IsVisible()) { return false; } if ((Object)Minimap.instance != (Object)null && Minimap.InTextInput()) { return false; } return true; } private static bool IsShortcutDownAllowingOtherKeys(KeyboardShortcut shortcut) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) KeyCode mainKey = ((KeyboardShortcut)(ref shortcut)).MainKey; if ((int)mainKey == 0 || !UnityInput.Current.GetKeyDown(mainKey)) { return false; } return ((KeyboardShortcut)(ref shortcut)).Modifiers.All((KeyCode modifier) => modifier == mainKey || UnityInput.Current.GetKey(modifier)); } private void OnDestroy() { Shutdown(); } private void Shutdown() { if (!_shutDown) { _shutDown = true; IsActive = false; QuickStackService?.Reset(); ChestTxService.Reset(); TxDestroy.Reset(); ChestTxService.ResetUpgradeRefunds(); TxNet.Reset(); AutoFeedService.Reset(); InventoryButtons.Detach(); TransferContext.End(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _harmony = null; } } } internal static class BuildInfo { internal const string Commit = "2867975"; } } namespace BestAutoSort.Tx { internal static class ChestTxService { internal sealed class TransientRefusal { public long TxId; public TxOp Op; public long Sender; public ZDOID ZdoId; } private sealed class ServerUpgradeHost : ITxUpgradeHost { public string SpawnGhost(string sourceId, int tier) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) if (!UpgradeResolve(sourceId, out var container) || (Object)(object)container == (Object)null) { throw new InvalidOperationException("source chest not loaded"); } if ((Object)(object)ZNetScene.instance == (Object)null) { throw new InvalidOperationException("network scene unavailable"); } GameObject prefab = ZNetScene.instance.GetPrefab(ChestUpgradePath.PrefabForTier(tier)); if ((Object)(object)prefab == (Object)null) { throw new InvalidOperationException("target prefab unavailable"); } GameObject val = Object.Instantiate<GameObject>(prefab, ((Component)container).transform.position, ((Component)container).transform.rotation); Container val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Container>() : null); ZNetView val3 = (((Object)(object)val != (Object)null) ? val.GetComponent<ZNetView>() : null); Piece val4 = (((Object)(object)val != (Object)null) ? val.GetComponent<Piece>() : null); ZDO val5 = (((Object)(object)val3 != (Object)null) ? val3.GetZDO() : null); if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null || (Object)(object)val4 == (Object)null || val5 == null) { throw new InvalidOperationException("replacement chest did not initialize"); } if (!val3.IsOwner()) { throw new InvalidOperationException("replacement chest not owned by the server"); } try { Piece component = ((Component)container).GetComponent<Piece>(); TxReflect.GetNetView(container); if ((Object)(object)component != (Object)null) { val4.SetCreator(component.GetCreator(), PlatformUserID.None); } } catch { } val5.Set("BestAutoSort.ChestTier", tier); try { ChestRuleStore.TryWrite(val2, ChestRuleStore.Read(container), out var _); ChestReserveStore.Write(val2, ChestReserveStore.ReadRaw(container)); } catch (Exception ex) { throw new InvalidOperationException("rule migration failed: " + ex.Message); } string text = ((object)Unsafe.As<ZDOID, ZDOID>(ref val5.m_uid)/*cast due to .constrained prefix*/).ToString(); UpgradeLive[text] = val2; return text; } public void WriteReverseLink(string ghostId, string opId) { if (!UpgradeResolve(ghostId, out var container) || (Object)(object)container == (Object)null) { throw new InvalidOperationException("ghost not loaded for link-write"); } ZNetView netView = TxReflect.GetNetView(container); ZDO obj = (((Object)(object)netView != (Object)null) ? netView.GetZDO() : null) ?? throw new InvalidOperationException("ghost has no ZDO for link-write"); obj.Set("BestAutoSort.UpgradeOp", opId); obj.Set("BestAutoSort.UpgradeIncomplete", 1); } public string FindGhostByLink(string opId) { try { foreach (KeyValuePair<string, Container> item in UpgradeLive) { if (!((Object)(object)item.Value == (Object)null)) { ZNetView netView = TxReflect.GetNetView(item.Value); ZDO val = (((Object)(object)netView != (Object)null) ? netView.GetZDO() : null); if (val != null && string.Equals(val.GetString("BestAutoSort.UpgradeOp", string.Empty), opId, StringComparison.Ordinal) && val.GetInt("BestAutoSort.UpgradeIncomplete", 0) == 1) { return item.Key; } } } } catch { } return null; } public void CopyInventoryNonDestructive(string sourceId, string ghostId) { if (!UpgradeResolve(sourceId, out var container) || (Object)(object)container == (Object)null) { throw new InvalidOperationException("source not loaded for copy"); } if (!UpgradeResolve(ghostId, out var container2) || (Object)(object)container2 == (Object)null) { throw new InvalidOperationException("ghost not loaded for copy"); } Inventory inventory = container.GetInventory(); Inventory inventory2 = container2.GetInventory(); if (inventory == null || inventory2 == null) { throw new InvalidOperationException("missing inventory for copy"); } foreach (ItemData item in new List<ItemData>(inventory.GetAllItems())) { if (item != null && item.m_shared != null) { ItemData val = item.Clone(); if (!inventory2.AddItem(val)) { throw new InvalidOperationException("ghost cannot hold the copied contents"); } } } } public int CountItems(string chestId) { return UpgradeItemUnits(chestId); } public int HashItems(string chestId) { return UpgradeItemHash(chestId); } public bool ValidateReady(string ghostId, int wantCount, int wantHash) { if (CountItems(ghostId) == wantCount) { return HashItems(ghostId) == wantHash; } return false; } public string PointerSwitchOneWay(string sourceId, string ghostId) { if (!UpgradeResolve(sourceId, out var container) || (Object)(object)container == (Object)null) { throw new InvalidOperationException("source not loaded for pointer switch"); } if (!UpgradeResolve(ghostId, out var container2) || (Object)(object)container2 == (Object)null) { throw new InvalidOperationException("ghost not loaded for pointer switch"); } Inventory inventory = container.GetInventory(); if (inventory != null) { foreach (ItemData item in new List<ItemData>(inventory.GetAllItems())) { if (item != null) { inventory.RemoveItem(item, item.m_stack); } } } try { ChestUpgradeService.ApplyState(container2); } catch (Exception ex) { Plugin.LogInstance.LogWarning((object)("Upgrade ghost compact failed (contents safe): " + ex)); } TxReflect.UpdateRows(container2); TxReflect.SaveContainer(container2); ZNetView netView = TxReflect.GetNetView(container2); ZDO obj = (((Object)(object)netView != (Object)null) ? netView.GetZDO() : null) ?? throw new InvalidOperationException("new chest lost its ZDO at pointer switch"); string text = ((object)Unsafe.As<ZDOID, ZDOID>(ref obj.m_uid)/*cast due to .constrained prefix*/).ToString(); obj.Set("BestAutoSort.UpgradeIncomplete", 0); obj.Set("BestAutoSort.UpgradeReceipt", text); ZNetView netView2 = TxReflect.GetNetView(container); ZDO val = (((Object)(object)netView2 != (Object)null) ? netView2.GetZDO() : null); if (val != null) { val.Set("BestAutoSort.UpgradeReceipt", text); } return text; } public void RetireSource(string sourceId, string newId) { if (!UpgradeResolve(sourceId, out var container) || (Object)(object)container == (Object)null) { return; } ZNetView netView = TxReflect.GetNetView(container); try { if ((Object)(object)netView != (Object)null && netView.IsValid()) { netView.Destroy(); } else { Object.Destroy((Object)(object)((Component)container).gameObject); } } catch (Exception ex) { throw new InvalidOperationException("source retire failed: " + ex.Message); } UpgradeLive.Remove(sourceId); } public void DestroyGhost(string ghostId) { if (UpgradeResolve(ghostId, out var container) && (Object)(object)container != (Object)null) { ZNetView netView = TxReflect.GetNetView(container); try { if ((Object)(object)netView != (Object)null && netView.IsValid() && netView.IsOwner()) { netView.Destroy(); } else { Object.Destroy((Object)(object)((Component)container).gameObject); } } catch { } } UpgradeLive.Remove(ghostId); } public void LogLoud(string message) { try { Plugin.LogInstance.LogWarning((object)message); } catch { } TxLog.Warn(message); } public void CrashHook(TxUpgradeCrashPoint point) { } } internal const int BatchChunkSize = 4; private const float RequestTimeout = 3f; private const int PayloadAttempts = 4; private const int QueryAttempts = 2; private const float FailDeadline = 16f; private const float PresenceHeartbeat = 5f; private const float PresenceTimeout = 15f; private const float RefreshPoll = 0.25f; private const float SlowPump = 0.5f; private const int DrainPerFrame = 64; private const float AuthoritySweepInterval = 15f; private const float AuthoritySweepHeartbeat = 300f; internal const string RingKey = "BestAutoSort_TxRing"; internal const string FloorKey = "BestAutoSort_TxFloor"; internal const string ItemsKey = "items"; private static readonly Dictionary<int, ChestState> States = new Dictionary<int, ChestState>(); private static readonly Dictionary<long, PendingTx> Pending = new Dictionary<long, PendingTx>(); private static uint _clientCounter = 1u; internal static long SItemsNullDenials; private static uint _counterReservedCeil; private const uint CounterReserveBlock = 4096u; private static float _nextSlowPump; private static float _nextRefreshPoll; private static float _nextAuthoritySweep; private static float _nextAuthoritySweepHeartbeat; private static int _openInstanceId; private static float _nextPresenceAt; internal static TxCounterFile.CounterLoadState CounterLoadState = TxCounterFile.CounterLoadState.Fresh; private static bool _counterFailClosed; private static readonly TxClaimSet<ItemData> InFlightAdds = new TxClaimSet<ItemData>(); private static readonly Dictionary<long, TransientRefusal> TransientRefusals = new Dictionary<long, TransientRefusal>(); private static readonly Dictionary<string, TxUpgradeManager> UpgradeManagers = new Dictionary<string, TxUpgradeManager>(StringComparer.Ordinal); private static readonly Dictionary<string, Container> UpgradeLive = new Dictionary<string, Container>(StringComparer.Ordinal); private const string UpOpKey = "BestAutoSort.UpgradeOp"; private static readonly HashSet<string> AppliedUpgradeRefunds = new HashSet<string>(StringComparer.Ordinal); private const int UpgradeRefundCap = 256; private const string UpPhaseKey = "BestAutoSort.UpgradePhase"; private const string UpGhostKey = "BestAutoSort.UpgradeGhost"; private const string UpCountKey = "BestAutoSort.UpgradeCount"; private const string UpHashKey = "BestAutoSort.UpgradeHash"; private const string UpSpendKey = "BestAutoSort.UpgradeSpend"; private const string UpSpendRecKey = "BestAutoSort.UpgradeSpendRec"; private const string UpRefundKey = "BestAutoSort.UpgradeRefund"; private const string UpRefundDoneKey = "BestAutoSort.UpgradeRefundDone"; private const string UpReceiptKey = "BestAutoSort.UpgradeReceipt"; private const string UpNonceKey = "BestAutoSort.UpgradeNonce"; private const string UpSenderKey = "BestAutoSort.UpgradeSender"; private const string UpTierKey = "BestAutoSort.UpgradeTier"; private const string UpIncompleteKey = "BestAutoSort.UpgradeIncomplete"; private const int UpgradePumpGuard = 32; internal static bool CounterIssuanceBlocked => _counterFailClosed; internal static int TransientRefusalCount => TransientRefusals.Count; private static void Respond(Container container, long peer, long txId, TxStatus status, uint revision, ZPackage body, bool totalsOnly, TxOp op) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown ZNetView netView = TxReflect.GetNetView(container); if ((Object)(object)netView == (Object)null || !netView.IsValid()) { return; } if (peer == ZNet.GetUID()) { TxLog.Info("tx=" + txId + " respond loopback (manager==requester)"); } ZPackage val = new ZPackage(); TxCodec.WriteResponseHeader(val, txId, status, revision, totalsOnly); val.Write(body); if (body != null) { try { int num = val.GetArray().Length; if (num > 4096) { TxLog.Info("tx=" + txId + " respond bytes=" + num + " (large)"); } } catch { } } try { netView.InvokeRPC(peer, "BestAutoSort_TxResponse", new object[1] { val }); } catch (Exception ex) { TxLog.Warn("tx=" + txId + " respond failed: " + ex.Message); } } internal static ZPackage EncodeResultBody(TxOpCall call, StoredResult r) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Expected O, but got Unknown //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Expected O, but got Unknown ZPackage val = new ZPackage(); switch (call.Op) { case TxOp.Add: case TxOp.AddBatch: case TxOp.Move: case TxOp.Sort: case TxOp.Upgrade: case TxOp.SetRule: { val.Write(r.Accepted.Count); for (int j = 0; j < r.Accepted.Count; j++) { val.Write(r.Accepted[j]); } break; } case TxOp.UpgradeRequest: { val.Write(r.Accepted.Count); for (int k = 0; k < r.Accepted.Count; k++) { val.Write(r.Accepted[k]); } val.Write(r.Receipt ?? string.Empty); try { int num = 0; try { if (r.RefundPrefabs != null && r.RefundAmounts != null) { num = Math.Min(r.RefundPrefabs.Count, r.RefundAmounts.Count); } } catch { num = 0; } if (num < 0) { num = 0; } val.Write(num); for (int l = 0; l < num; l++) { val.Write(r.RefundPrefabs[l]); val.Write(r.RefundAmounts[l]); } } catch { } break; } case TxOp.Take: case TxOp.TakeBatch: { val.Write(r.Takes.Count); for (int i = 0; i < r.Takes.Count; i++) { TakeEntry takeEntry = r.Takes[i]; if (takeEntry == null) { val.Write(0); val.Write(new ZPackage()); val.Write(0); } else { ZPackage val2 = new ZPackage(); takeEntry.Item.Save(val2); val.Write(takeEntry.PrefabHash); val.Write(val2); val.Write(takeEntry.Accepted); } } break; } } return val; } private static void RespondQuery(Container container, long sender, long txId, bool isTransientRetry) { //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02bb: Expected O, but got Unknown //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Expected O, but got Unknown //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Expected O, but got Unknown //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Expected O, but got Unknown //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Expected O, but got Unknown ChestState state = GetState(container); if (state == null) { return; } TransientRefusal value2; if (state.Processed.TryGetValue(txId, out var value)) { if (value.Sender != 0L && TxIdGen.PeerKey(value.Sender) != TxIdGen.PeerKey(sender)) { TxLog.Warn("container=" + TxLog.Zid(state.ZdoId) + " tx=" + txId + " QUERY sender mismatch"); Respond(container, sender, txId, TxStatus.Rejected, value.Revision, new ZPackage(), totalsOnly: false, value.Op); } else { TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " tx=" + txId + " QUERY hit status=" + value.Status); Respond(container, sender, txId, value.Status, value.Revision, EncodeCachedBody(value), value.TotalsOnly, value.Op); } } else if (TransientRefusals.TryGetValue(txId, out value2)) { if (sender != 0L && !TxIdGen.MatchesPeer(sender, txId)) { TxLog.Warn("container=" + TxLog.Zid(state.ZdoId) + " tx=" + txId + " TRANSIENT-QUERY sender mismatch"); Respond(container, sender, txId, TxStatus.Rejected, CurrentRevision(container), new ZPackage(), totalsOnly: false, TxOp.Query); } else { TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " tx=" + txId + " TRANSIENT-QUERY hit (same-tx retry retained)"); Respond(container, sender, txId, TxStatus.TransientUnavailable, CurrentRevision(container), new ZPackage(), totalsOnly: false, value2.Op); } } else if (isTransientRetry) { if (sender != 0L && !TxIdGen.MatchesPeer(sender, txId)) { TxLog.Warn("container=" + TxLog.Zid(state.ZdoId) + " tx=" + txId + " FLAGGED-QUERY sender mismatch"); Respond(container, sender, txId, TxStatus.Rejected, CurrentRevision(container), new ZPackage(), totalsOnly: false, TxOp.Query); } else { TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " tx=" + txId + " FLAGGED-QUERY no record (same-tx retry retained)"); Respond(container, sender, txId, TxStatus.TransientUnavailable, CurrentRevision(container), new ZPackage(), totalsOnly: false, TxOp.Query); } } else { Respond(container, sender, txId, TxStatus.UnknownTx, CurrentRevision(container), new ZPackage(), totalsOnly: true, TxOp.Query); } } internal static ZPackage EncodeCachedBody(StoredResult cached) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); if (cached.TotalsOnly) { if (cached.Op == TxOp.Take || cached.Op == TxOp.TakeBatch) { val.Write(0); return val; } val.Write(1); val.Write(cached.AcceptedTotal()); return val; } return EncodeResultBody(new TxOpCall { Op = cached.Op }, cached); } internal static void HandleServerTxResponse(long sender, ZDOID chestId, ZPackage inner) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown try { if (!Plugin.IsActive || inner == null || ((ZDOID)(ref chestId)).IsNone()) { return; } long num = 0L; try { ZPackage val = new ZPackage(inner.GetArray()); long txId = 0L; TxStatus status = TxStatus.UnknownTx; uint revision = 0u; bool totalsOnly = false; if (TxCodec.ReadResponseHeader(val, out txId, out status, out revision, out totalsOnly)) { num = txId; } } catch { } if (num == 0L) { return; } PendingTx value = null; bool flag = false; try { flag = Pending.TryGetValue(num, out value); } catch { flag = false; } Container val2 = null; try { val2 = ((flag && value != null) ? value.Container : FindContainerByZdo(chestId)); } catch { val2 = null; } if ((Object)(object)val2 == (Object)null) { return; } try { inner.SetPos(0); } catch { return; } try { OnTxResponse(val2, sender, inner); } catch { } } catch { } } private static Container FindContainerByZdo(ZDOID chestId) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) try { foreach (KeyValuePair<int, ChestState> state in States) { ChestState value = state.Value; if (value == null || (Object)(object)value.Container == (Object)null) { continue; } try { if (value.ZdoId == chestId) { return value.Container; } } catch { } } } catch { } return null; } private static void OnTxResponse(Container container, long sender, ZPackage pkg) { //IL_0398: Unknown result type (might be due to invalid IL or missing references) //IL_039f: Expected O, but got Unknown if (!Plugin.IsActive) { return; } if (!TxCodec.ReadResponseHeader(pkg, out var txId, out var status, out var revision, out var totalsOnly)) { TxLog.Warn("bad response packet from " + sender); return; } if (!Pending.TryGetValue(txId, out var value)) { RefreshNow(container); TxLog.Info("tx=" + txId + " late-duplicate ignored (already completed)"); return; } TxLog.Info("tx=" + txId + " response status=" + status.ToString() + " rev=" + revision + (totalsOnly ? " totals-only" : "")); ZPackage val; try { val = pkg.ReadPackage(); } catch (Exception ex) { TxLog.Warn("tx=" + txId + " response body unreadable: " + ex.Message); RefreshNow(value.Container); if (!ForceRefetch(txId)) { FinalizeUnknownTx(txId); } return; } pkg = val; TxCompletionKind txCompletionKind = TxResponsePolicy.Classify(status, totalsOnly, value.Op, value.ExpectedItems); switch (txCompletionKind) { case TxCompletionKind.TransientRetrySameTx: { value.IsTransientRetry = true; float num = TxPendingDrain.TransientBackoffSeconds(value.TransientAttempts + 1); value.NextTryAt = Time.realtimeSinceStartup + num; value.Deadline = Time.realtimeSinceStartup + 16f; TxLog.Warn("tx=" + txId + " TRANSIENT op=" + value.Op.ToString() + " (same-tx retry retained, backoff " + num + "s — nothing credited/removed)"); RefreshNow(value.Container); return; } case TxCompletionKind.Indeterminate: TxLog.Warn("tx=" + txId + " outcome UNKNOWN op=" + value.Op.ToString() + " (manager has no record, nothing credited/removed, no cascade)"); TellPlayer("Chest request outcome unknown. Check the chest before retrying."); CompletePendingTerminal(txId, EmptyCountBody(), status, revision, txCompletionKind); return; case TxCompletionKind.FailedNotCommitted: TxLog.Warn("tx=" + txId + " REJECTED op=" + value.Op.ToString() + " (nothing applied, nothing credited/removed)"); CompletePendingTerminal(txId, EmptyCountBody(), status, revision, txCompletionKind); return; case TxCompletionKind.CommittedTakeDetailsUnavailable: TxLog.Warn("tx=" + txId + " COMMITTED Take totals-only: applied but items unrecoverable from cache (nothing credited)"); TellPlayer("Chest applied the move but the items cannot be recovered. Check the chest."); CompletePendingTerminal(txId, EmptyCountBody(), status, revision, txCompletionKind); return; case TxCompletionKind.CommittedMultiAddDetailsUnavailable: TxLog.Warn("tx=" + txId + " COMMITTED multi-add totals-only: applied but per-item counts unknown (nothing removed, no auto-retry)"); TellPlayer("Chest applied the move but per-item counts are unknown. Check the chest before retrying."); CompletePendingTerminal(txId, EmptyCountBody(), status, revision, txCompletionKind); return; } if (!totalsOnly && IsTakeOp(value.Op) && !TakeBodyReadable(pkg)) { TxLog.Warn("tx=" + txId + " take body corrupt: re-querying instead of voiding"); RefreshNow(value.Container); if (!ForceRefetch(txId)) { FinalizeUnknownTx(txId); } } else if (!totalsOnly && !IsTakeOp(value.Op) && value.ExpectedItems >= 0 && !AddBodyMatchesArity(pkg, value.ExpectedItems)) { int num2 = -1; int num3 = -2147483647; try { ZPackage val2 = new ZPackage(pkg.GetArray()); num2 = val2.GetArray().Length; try { num3 = val2.ReadInt(); } catch { } } catch { } TxLog.Warn("tx=" + txId + " add body corrupt: expected=" + value.ExpectedItems + " bodyLen=" + num2 + " bodyFirst=" + num3 + ", re-querying instead of misattributing"); RefreshNow(value.Container); if (!ForceRefetch(txId)) { FinalizeUnknownTx(txId); } } else { CompletePendingTerminal(txId, pkg, status, revision, txCompletionKind); } } private static void CompletePendingTerminal(long txId, ZPackage body, TxStatus status, uint rev, TxCompletionKind disp) { if (!Pending.TryGetValue(txId, out var value)) { return; } Pending.Remove(txId); ReleaseClaimed(value.Claimed); try { if (value.OnResponse != null) { value.OnResponse(body, status, rev, disp); } } catch (Exception ex) { TxLog.Error("tx=" + txId + " completion failed: " + ex.Message); } RefreshNow(value.Container); } private static ZPackage EmptyCountBody() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(0); val.SetPos(0); return val; } internal static bool ForceRefetch(long txId) { if (!Pending.TryGetValue(txId, out var value) || value.FinalQuerySent) { return false; } if ((Object)(object)value.Container == (Object)null) { return false; } value.FinalQuerySent = true; value.QuerySent = true; SendQuery(value); value.Attempts++; value.Deadline = Time.realtimeSinceStartup + 3f; value.NextTryAt = Time.realtimeSinceStartup + 3f; TxLog.Warn("tx=" + txId + " forced re-query (decode/timeout), deadline extended once"); return true; } internal static void FinalizeUnknownTx(long txId) { if (Pending.TryGetValue(txId, out var value)) { TxLog.Warn("tx=" + value.TxId + " UNKNOWN op=" + value.Op.ToString() + " (applied-or-not indeterminate, no queries left)"); TellPlayer("Chest request outcome unknown. Check the chest before retrying."); CompletePendingTerminal(txId, null, TxStatus.UnknownTx, 0u, TxCompletionKind.Indeterminate); } } private static bool TakeBodyReadable(ZPackage body) { try { if (body == null) { return false; } int num = body.ReadInt(); if (num < 0 || num > 4096) { return false; } for (int i = 0; i < num; i++) { body.ReadInt(); body.ReadPackage(); body.ReadInt(); } body.SetPos(0); return true; } catch { return false; } } private static bool AddBodyMatchesArity(ZPackage body, int expected) { try { if (body == null) { return false; } int num = body.ReadInt(); if (num != expected) { return false; } for (int i = 0; i < num; i++) { body.ReadInt(); } body.SetPos(0); return true; } catch { return false; } } private static int ReadAcceptedAt(ZPackage pkg, int index) { try { if (pkg == null) { return 0; } int num = pkg.ReadInt(); int result = 0; for (int i = 0; i < num; i++) { int num2 = pkg.ReadInt(); if (i == index) { result = num2; } } return result; } catch (Exception) { return 0; } } private static List<int> ReadAcceptedList(ZPackage pkg, int count) { List<int> list = new List<int>(); try { if (pkg == null) { return list; } int num = pkg.ReadInt(); for (int i = 0; i < num && i < count; i++) { list.Add(pkg.ReadInt()); } } catch (Exception) { } while (list.Count < count) { list.Add(0); } return list; } private static void CompleteAdd(Inventory srcInv, ItemData itemRef, TxOpItem sent, int accepted, TxStatus status, uint rev, Container container, Action<ZPackage, TxStatus, uint, TxCompletionKind> onDone) { CompleteAdd(srcInv, itemRef, sent, accepted, status, rev, container, onDone, alreadyRemoved: false, TxCompletionKind.Normal); } private static void LogNonMainInventory(string op, Inventory inv) { try { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && inv != null && inv != ((Humanoid)localPlayer).GetInventory()) { TxLog.Info(op + " uses non-main inventory (dedicated slots?)"); } } catch { } } private static void CompleteAdd(Inventory srcInv, ItemData itemRef, TxOpItem sent, int accepted, TxStatus status, uint rev, Container container, Action<ZPackage, TxStatus, uint, TxCompletionKind> onDone, bool alreadyRemoved, TxCompletionKind disp) { LogNonMainInventory("add", srcInv); if (alreadyRemoved) { if (!TxDecision.ShouldRestoreDragRemainder(disp)) { TxLog.Warn("drag-restore withheld: outcome " + status.ToString() + "/" + disp.ToString() + " is not known-safe — chest may already hold the items, nothing restored"); TellPlayer("Chest request outcome unknown. Check the chest before retrying."); } else { RestoreDragRemainder(srcInv, itemRef, accepted, disp); } if (status == TxStatus.UnknownTx && TxDecision.ShouldRestoreDragRemainder(disp)) { TellPlayer("Chest request outcome unknown. Check the chest before retrying."); } else if (status == TxStatus.Rejected) { TellPlayer("The shared chest refused the move. Try again."); } onDone?.Invoke(null, status, rev, disp); return; } if (accepted > 0 && srcInv != null && sent != null) { string name = ((sent.Snapshot != null && sent.Snapshot.m_shared != null) ? sent.Snapshot.m_shared.m_name : null); int quality = ((sent.Snapshot != null) ? sent.Snapshot.m_quality : (-1)); int variant = ((sent.Snapshot != null) ? sent.Snapshot.m_variant : (-1)); float world = ((sent.Snapshot != null) ? ((float)sent.Snapshot.m_worldLevel) : (-1f)); int num = TxInventory.RemoveForTake(srcInv, itemRef, name, quality, accepted, variant, world); TxLog.Info("add-remove accepted=" + accepted + " removed=" + num); if (num < accepted) { int amount = accepted - num; TxLog.Warn("tx short-removal: accepted=" + accepted + " removed=" + num + " compensating " + amount); CompensateAddBack(container, sent, amount); } } switch (status) { case TxStatus.UnknownTx: TellPlayer("Chest request outcome unknown. Check the chest before retrying."); break; case TxStatus.Rejected: TellPlayer("The shared chest refused the move. Try again."); break; } onDone?.Invoke(null, status, rev, disp); } private static void RestoreDragRemainder(Inventory srcInv, ItemData itemRef, int accepted, TxCompletionKind disp) { if (srcInv == null || itemRef == null) { return; } CustomDataTags.StripBenign(itemRef); TxLog.Info("drag-restore stack=" + itemRef.m_stack + " accepted=" + accepted + " disp=" + disp); int num = TxDecision.DragRestoreAmount(itemRef.m_stack, accepted, disp); if (num <= 0) { return; } itemRef.m_stack = num; if (srcInv.AddItem(itemRef)) { srcInv.m_onChanged?.Invoke(); return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { ((Humanoid)localPlayer).DropItem(srcInv, itemRef, num); } else { TxLog.Error("drag restore failed: no player, items lost: " + num); } } private static void CompensateAddBack(Container container, TxOpItem sent, int amount) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) if (amount <= 0 || sent == null || sent.Snapshot == null || sent.Snapshot.m_shared == null) { return; } TxOpItem txOpItem = SnapshotAuto(sent.Snapshot, amount); if (txOpItem == null) { return; } List<TxOpItem> list = new List<TxOpItem>(); list.Add(txOpItem); RequestTakeCustom(container, list, respectReserves: false, delegate(List<DecodedTake> decoded, TxStatus status, uint rev, TxCompletionKind burnDisp) { int num = 0; if (decoded != null) { foreach (DecodedTake item in decoded) { if (item != null && item.Accepted > 0) { num += item.Accepted; } } } if (burnDisp == TxCompletionKind.Indeterminate || (status != TxStatus.Accepted && status != TxStatus.Partial && status != TxStatus.Duplicate)) { TxLog.Warn("short-removal burn not applied (" + status.ToString() + "/" + burnDisp.ToString() + "), excess stays in chest — check the chest"); TellPlayer("Chest could not correct an over-deposit. Check the chest."); } else if (num < amount) { TxLog.Warn("short-removal burn partial: burned=" + num + " of " + amount); TellPlayer("Chest could not correct an over-deposit. Check the chest."); } else { TxLog.Info("short-removal burn ok: " + num); } RefreshNow(container); }, 0L); } private static void CompleteTake(Inventory dstInv, ZPackage pkg, TxStatus status, uint rev, Container container, Action<ZPackage, TxStatus, uint, TxCompletionKind> onDone, int wantDstX = -1, int wantDstY = -1, Action<Dictionary<string, int>> onPlaced = null, TxCompletionKind disp = TxCompletionKind.Normal) { LogNonMainInventory("take", dstInv); if ((status == TxStatus.Accepted || status == TxStatus.Partial || status == TxStatus.Duplicate) && dstInv != null && pkg != null) { int num = 0; try { num = pkg.ReadInt(); } catch (Exception ex) { int num2 = -1; try { num2 = pkg.GetArray().Length; } catch { } TxLog.Error("take completion decode failed: " + ex.Message + " (body bytes=" + num2 + ")"); } Dictionary<string, int> dictionary = null; for (int i = 0; i < num; i++) { int num3 = 0; ZPackage val = null; int num4 = 0; try { num3 = pkg.ReadInt(); val = pkg.ReadPackage(); num4 = pkg.ReadInt(); } catch (Exception ex2) { TxLog.Error("take completion entry " + i + " unreadable (" + ex2.Message + "), aborting rest"); break; } if (num4 <= 0) { continue; } try { ItemData val2 = TxCodec.ResolvePrefab(num3, val); CustomDataTags.StripBenign(val2); if (val2 == null) { TxLog.Error("take completion: prefab " + num3 + " missing, compensating " + num4); CompensateTakeBack(container, num3, val, num4); continue; } int num5 = TxInventory.AddTakeAndCount(dstInv, val2, wantDstX, wantDstY); if (num5 > 0 && onPlaced != null && val2.m_shared != null) { if (dictionary == null) { dictionary = new Dictionary<string, int>(StringComparer.Ordinal); } dictionary.TryGetValue(val2.m_shared.m_name, out var value); dictionary[val2.m_shared.m_name] = value + num5; } if (num5 < num4) { int stack = num4 - num5; TxLog.Warn("take completion: player full, compensating " + stack); ItemData val3 = val2.Clone(); val3.m_stack = stack; CompensateTakeBackItem(container, num3, val3); } } catch (Exception ex3) { TxLog.Error("take completion entry " + i + " failed (" + ex3.Message + "), compensating " + num4); try { CompensateTakeBack(container, num3, val, num4); } catch (Exception ex4) { TxLog.Error("take completion entry " + i + " compensation failed: " + ex4.Message); } } } if (dictionary != null && dictionary.Count > 0 && onPlaced != null) { try { onPlaced(dictionary); } catch { } } } switch (status) { case TxStatus.UnknownTx: TellPlayer("Chest request outcome unknown. Check the chest before retrying."); break; case TxStatus.Rejected: TellPlayer("The shared chest changed. Try again."); break; } onDone?.Invoke(pkg, status, rev, disp); } private static void CompensateTakeBack(Container container, int prefabHash, ZPackage inner, int amount) { ItemData val = TxCodec.ResolvePrefab(prefabHash, inner); if (val == null) { TxLog.Error("compensation failed: prefab missing"); } else { CompensateTakeBackItem(container, prefabHash, val); } } internal static void CompensateTakeBackItem(Container container, int prefabHash, ItemData item) { int wanted = ((item != null) ? item.m_stack : 0); TxOpItem txOpItem = SnapshotAuto(item, item.m_stack); if (txOpItem == null) { return; } TxOpCall txOpCall = new TxOpCall(); txOpCall.Op = TxOp.Add; txOpCall.Items.Add(txOpItem); Submit(container, txOpCall, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp) { if (disp == TxCompletionKind.Indeterminate) { TxLog.Warn("compensate-add indeterminate: outcome unknown, nothing restored — check the chest"); TellPlayer("Chest request outcome unknown. Check the chest before retrying."); RefreshNow(container); } else { int num = ReadAcceptedAt(pkg, 0); TxLog.Info("compensate-add status=" + status.ToString() + " accepted=" + num + " of " + wanted); if (num < wanted) { RestoreCompensateLeftover(item, num); } RefreshNow(container); } }, 0L); } private static void RestoreCompensateLeftover(ItemData item, int accepted) { if (item == null) { return; } int num = item.m_stack - accepted; if (num <= 0) { return; } Player localPlayer = Player.m_localPlayer; Inventory val = (((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).GetInventory() : null); if (val == null) { TxLog.Error("compensate restore failed: no player inventory, items lost: " + num); return; } ItemData val2 = item.Clone(); val2.m_stack = num; CustomDataTags.StripBenign(val2); if (TxInventory.AddAndCount(val, val2) < num) { ((Humanoid)localPlayer).DropItem(val, val2, num); TxLog.Warn("compensate leftover did not fit, dropped at feet: " + num); } else { TxLog.Warn("compensate-add short, restored to player: " + num); } TellPlayer("Chest could not take back some items. They were returned to you."); } internal static void SubmitTakePrefetch(Container container, TxOpCall call, Inventory dstInv, Action<Dictionary<string, int>> onLanded = null) { Submit(container, call, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp) { CompleteTake(dstInv, pkg, status, rev, container, null, -1, -1, onLanded, disp); }, 0L); } internal static void RequestTakeCustom(Container container, List<TxOpItem> items, bool respectReserves, Action<List<DecodedTake>, TxStatus, uint, TxCompletionKind> onDone, long playerId = 0L, Vector3? actorPos = null, ZDOID feederId = default(ZDOID)) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) if (items == null || items.Count == 0) { return; } TxOpCall txOpCall = new TxOpCall(); txOpCall.Op = TxOp.TakeBatch; txOpCall.Items.AddRange(items); txOpCall.RespectReserves = respectReserves; txOpCall.FeederId = feederId; if (IsManager(container)) { MutateLocal(container, txOpCall, delegate(StoredResult r) { RefreshNow(container); List<DecodedTake> list = new List<DecodedTake>(); if (r != null && r.Takes != null) { foreach (TakeEntry take in r.Takes) { if (take != null && take.Accepted > 0) { list.Add(new DecodedTake { PrefabHash = take.PrefabHash, Item = take.Item, Accepted = take.Accepted }); } } } TxStatus txStatus = r?.Status ?? TxStatus.UnknownTx; uint arg = r?.Revision ?? CurrentRevision(container); TxCompletionKind arg2 = ((r != null) ? TxResponsePolicy.Classify(txStatus, r.TotalsOnly, TxOp.TakeBatch, items.Count) : TxCompletionKind.Indeterminate); if (onDone != null) { onDone(list, txStatus, arg, arg2); } }, playerId, actorPos); return; } Submit(container, txOpCall, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp) { List<DecodedTake> list = TxCodec.ReadTakeResults(pkg); if (list == null) { if (disp == TxCompletionKind.Indeterminate) { TxLog.Warn("take-custom completion decode failed (indeterminate, nothing credited, no retry)"); if (onDone != null) { onDone(new List<DecodedTake>(), status, rev, disp); } } else if (status == TxStatus.Accepted || status == TxStatus.Partial || status == TxStatus.Duplicate) { TxLog.Warn("take-custom completion decode failed (committed, payload lost: nothing credited, no retry)"); if (onDone != null) { onDone(new List<DecodedTake>(), status, rev, TxCompletionKind.CommittedTakeDetailsUnavailable); } } else { TxLog.Warn("take-custom completion decode failed"); if (onDone != null) { onDone(new List<DecodedTake>(), status, rev, disp); } } } else if (onDone != null) { onDone(list, status, rev, disp); } }, playerId, actorPos); } internal static void SubmitCall(Container container, TxOpCall call, Inventory srcInv, Action<List<TransferRecord>, TxCompletionKind> onDone) { SubmitCallDetailed(container, call, srcInv, delegate(List<TransferRecord> records, List<int> accepted, TxStatus status, TxCompletionKind disp) { if (onDone != null) { onDone(records, disp); } }); } internal static void SubmitCallDetailed(Container container, TxOpCall call, Inventory srcInv, Action<List<TransferRecord>, List<int>, TxStatus, TxCompletionKind> onDone) { if (call != null && call.Items.Count > 4) { for (int i = 0; i < call.Items.Count; i += 4) { TxOpCall txOpCall = new TxOpCall(); txOpCall.Op = call.Op; txOpCall.EnforceRule = call.EnforceRule; txOpCall.RespectReserves = call.RespectReserves; for (int j = i; j < call.Items.Count && j < i + 4; j++) { txOpCall.Items.Add(call.Items[j]); } SubmitCallDetailed(container, txOpCall, srcInv, onDone); } return; } if (IsManager(container)) { MutateLocal(container, call, delegate(StoredResult r) { List<int> list = new List<int>(); for (int k = 0; k < call.Items.Count; k++) { list.Add((r != null && r.Accepted != null && k < r.Accepted.Count) ? r.Accepted[k] : 0); } TxStatus txStatus = r?.Status ?? TxStatus.UnknownTx; List<TransferRecord> arg = FinishBatchCompletion(container, srcInv, call, r); TxCompletionKind arg2 = ((r != null) ? TxResponsePolicy.Classify(txStatus, r.TotalsOnly, call.Op, call.Items.Count) : TxCompletionKind.Indeterminate); if (onDone != null) { onDone(arg, list, txStatus, arg2); } }, 0L); return; } Submit(container, call, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp) { switch (disp) { case TxCompletionKind.Indeterminate: TxLog.Warn("batch indeterminate: outcome unknown, nothing removed, no cascade — check the chest before retrying"); TellPlayer("Chest request outcome unknown. Check the chest before retrying."); RefreshNow(container); if (onDone != null) { onDone(new List<TransferRecord>(), ZeroAccepted(call.Items.Count), status, disp); } break; case TxCompletionKind.CommittedMultiAddDetailsUnavailable: TxLog.Warn("batch details-unavailable: committed but unattributed, nothing removed, no cascade"); TellPlayer("Chest applied the move but per-item counts are unknown. Check the chest before retrying."); RefreshNow(container); if (onDone != null) { onDone(new List<TransferRecord>(), ZeroAccepted(call.Items.Count), status, disp); } break; default: { List<int> list = ReadAcceptedList(pkg, call.Items.Count); List<TransferRecord> list2 = new List<TransferRecord>(); for (int k = 0; k < call.Items.Count && k < list.Count; k++) { TxOpItem txOpItem = call.Items[k]; CompleteAdd(srcInv, txOpItem.SourceRef, txOpItem, list[k], status, rev, container, null, alreadyRemoved: false, disp); if (list[k] > 0 && txOpItem.Snapshot != null && txOpItem.Snapshot.m_shared != null) { list2.Add(new TransferRecord(txOpItem.Snapshot.m_shared.m_name, txOpItem.Snapshot.GetIcon(), list[k], txOpItem.Snapshot.m_shared.m_maxStackSize)); } } RefreshNow(container); if (onDone != null) { onDone(list2, list, status, disp); } break; } } }, 0L, null, call.Items.Count); } private static List<int> ZeroAccepted(int count) { List<int> list = new List<int>((count >= 0) ? count : 0); for (int i = 0; i < count; i++) { list.Add(0); } return list; } private static List<TransferRecord> FinishBatchCompletion(Container container, Inventory srcInv, TxOpCall call, StoredResult r) { List<TransferRecord> list = new List<TransferRecord>(); for (int i = 0; i < call.Items.Count && i < r.Accepted.Count; i++) { TxOpItem txOpItem = call.Items[i]; int num = r.Accepted[i]; if (num > 0 && srcInv != null) { string name = ((txOpItem.Snapshot != null && txOpItem.Snapshot.m_shared != null) ? txOpItem.Snapshot.m_shared.m_name : null); int quality = ((txOpItem.Snapshot != null) ? txOpItem.Snapshot.m_quality : (-1)); int variant = ((txOpItem.Snapshot != null) ? txOpItem.Snapshot.m_variant : (-1)); float world = ((txOpItem.Snapshot != null) ? ((float)txOpItem.Snapshot.m_worldLevel) : (-1f)); int num2 = TxInventory.RemoveForTake(srcInv, txOpItem.SourceRef, name, quality, num, variant, world); if (num2 < num) { int amount = num - num2; TxLog.Warn("local batch short-removal: compensating " + amount); CompensateAddBack(container, txOpItem, amount); } if (txOpItem.Snapshot != null && txOpItem.Snapshot.m_shared != null) { list.Add(new TransferRecord(txOpItem.Snapshot.m_shared.m_name, txOpItem.Snapshot.GetIcon(), num, txOpItem.Snapshot.m_shared.m_maxStackSize)); } } } RefreshNow(container); return list; } private static void PumpPending() { if (Pending.Count == 0) { return; } float now = Time.realtimeSinceStartup; TxPendingDrain.Drain(Pending, delegate(long txId, PendingTx p) { if ((Object)(object)p.Container == (Object)null) { return TxPendingDrain.Step.Terminal; } return p.IsTransientRetry ? TxPendingDrain.DecideTransient(now, p.NextTryAt) : TxPendingDrain.Decide(now, p.NextTryAt, p.Deadline, p.Attempts, p.QuerySent, p.FinalQuerySent, 4, 2); }, delegate(long txId, PendingTx p, TxPendingDrain.Step step) { switch (step) { case TxPendingDrain.Step.Resend: if (p.IsTransientRetry) { SendTransientRetry(p); } else if (CanSendStoredMutationPayload(p)) { SendPayload(p.Container, p.Payload); } else { TxLog.Warn("tx=" + p.TxId + " resend skipped: no stored payload (entry retained)"); } p.Attempts++; p.NextTryAt = now + (p.IsTransientRetry ? TxPendingDrain.TransientBackoffSeconds(p.TransientAttempts) : 3f); break; case TxPendingDrain.Step.Query: p.QuerySent = true; SendQuery(p); p.Attempts++; p.NextTryAt = now + 3f; break; case TxPendingDrain.Step.FinalQuery: p.FinalQuerySent = true; p.QuerySent = true; SendQuery(p); p.Attempts++; p.Deadline = now + 3f; p.NextTryAt = now + 3f; TxLog.Warn("tx=" + p.TxId + " TIMEOUT op=" + p.Op.ToString() + " final query sent"); break; } }, delegate(long txId) { FinalizeUnknownTx(txId); }); } private static bool CanSendStoredMutationPayload(PendingTx p) { if (p != null && TxPendingDrain.CanSendStoredMutationPayload(p.IsTransientRetry)) { return p.Payload != null; } return false; } private static void SendTransientRetry(PendingTx p) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown bool encodeSucceeded = false; try { if (p.Call != null) { p.Call.IsTransientRetry = true; ZPackage val = EncodeCall(p.Call, CurrentRevision(p.Container), LocalPlayerId(), LocalActorPos()); ZPackage val2 = new ZPackage(); val2.Write(p.TxId); val2.Write(val); p.Payload = val2; encodeSucceeded = true; } else { TxLog.Warn("tx=" + p.TxId + " transient re-encode skipped (no stored call), sending flagged query instead"); } } catch (Exception ex) { TxLog.Warn("tx=" + p.TxId + " transient re-encode failed, sending flagged query instead (stored bytes never resent): " + ex.Message); } p.TransientAttempts++; if (TxPendingDrain.DecideTransientSend(encodeSucceeded) == TxPendingDrain.TransientSendAction.FlaggedMutation) { TxLog.Info("tx=" + p.TxId + " TRANSIENT-RETRY (flagged same-tx resend #" + p.TransientAttempts + ")"); SendPayload(p.Container, p.Payload); } else { TxLog.Info("tx=" + p.TxId + " TRANSIENT-QUERY (flagged same-tx query #" + p.TransientAttempts + ", re-encode unavailable)"); SendQuery(p); } } private static void SendQuery(PendingTx p) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(3); val.Write(102); val.Write(0u); val.Write(LocalPlayerId()); val.Write(LocalActorPos()); val.Write(false); val.Write(false); val.Write(p.IsTransientRetry); ZPackage val2 = new ZPackage(); val2.Write(p.TxId); val2.Write(val); TxLog.Info("tx=" + p.TxId + " QUERY (response was lost)"); SendPayload(p.Container, val2); } internal static void RefreshNow(Container container) { ChestState state = GetState(container); if (state != null) { state.SeenRev = 0u; state.SeenOnce = false; state.SeenBytes = null; } } private static void PumpViewerRefresh() { //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Unknown result type (might be due to invalid IL or missing references) //IL_02b8: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Expected O, but got Unknown //IL_030e: Unknown result type (might be due to invalid IL or missing references) if (Time.realtimeSinceStartup < _nextRefreshPoll) { return; } _nextRefreshPoll = Time.realtimeSinceStartup + 0.25f; InventoryGui instance = InventoryGui.instance; Container val = InventoryAccess.CurrentContainer(instance); int num = (((Object)(object)val != (Object)null) ? ((Object)val).GetInstanceID() : 0); if (num != _openInstanceId) { ChestState value = null; if (_openInstanceId != 0) { States.TryGetValue(_openInstanceId, out value); } if (value != null && (Object)(object)value.Container != (Object)null) { SendPresence(value.Container, TxOp.ViewerClose); } _openInstanceId = num; if ((Object)(object)val != (Object)null) { RefreshNow(val); SendPresence(val, TxOp.ViewerOpen); _nextPresenceAt = Time.realtimeSinceStartup + 5f; } } if ((Object)(object)val == (Object)null || !IsShared(val)) { return; } if (Time.realtimeSinceStartup >= _nextPresenceAt) { SendPresence(val, TxOp.ViewerOpen); _nextPresenceAt = Time.realtimeSinceStartup + 5f; } ChestState state = GetState(val); if (state == null) { return; } ZNetView netView = TxReflect.GetNetView(val); if ((Object)(object)netView == (Object)null || !netView.IsValid()) { return; } uint dataRevision = netView.GetZDO().DataRevision; if (!TxHandoffGuard.ShouldApplyViewerRefresh(state.SeenOnce, state.SeenRev, dataRevision)) { if (dataRevision < state.SeenRev) { TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " viewer stale rev=" + dataRevision + " (seen " + state.SeenRev + "), skipped"); } return; } byte[] array; try { array = TxSItemsGuard.CloneBytes(netView.GetZDO().GetByteArray(ZDOVars.s_items, (byte[])null)); } catch (Exception) { return; } if (array == null) { TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " viewer null s_items at rev=" + dataRevision + " (keeping last-good, never presumed empty)"); return; } if (SameBytes(array, state.SeenBytes)) { state.SeenRev = dataRevision; return; } if (!TxSItemsGuard.TryValidate(array, out var reason)) { TxLog.Warn("viewer refresh refused invalid s_items (" + reason + "), keeping last-good"); return; } if ((Object)(object)instance != (Object)null) { TxReflect.CancelDragFrom(instance, val.GetInventory()); } TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " viewer dims before=" + val.GetInventory().GetWidth() + "x" + val.GetInventory().GetHeight()); try { val.GetInventory().Load(new ZPackage(TxSItemsGuard.CloneBytes(array))); TxReflect.SetLastRevision(val, dataRevision); TxReflect.UpdateRows(val); } catch (Exception ex2) { TxLog.Warn("viewer refresh failed: " + ex2.Message); return; } state.SeenRev = dataRevision; state.SeenOnce = true; state.SeenBytes = TxSItemsGuard.CloneBytes(array); TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " viewer refresh rev=" + dataRevision); } internal static bool SameBytes(byte[] a, byte[] b) { if (a == null || b == null) { return a == b; } if (a.Length != b.Length) { return false; } for (int i = 0; i < a.Length; i++) { if (a[i] != b[i]) { return false; } } return true; } private static void SendPresence(Container container, TxOp op) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown if (!IsShared(container) || IsManager(container)) { return; } ZNetView netView = TxReflect.GetNetView(container); if (!((Object)(object)netView == (Object)null) && netView.IsValid()) { long num = IssueTxId(); if (num != 0L) { ZPackage val = new ZPackage(); val.Write(3); val.Write((int)op); val.Write(0u); val.Write(LocalPlayerId()); val.Write(LocalActorPos()); val.Write(false); val.Write(false); val.Write(false); ZPackage val2 = new ZPackage(); val2.Write(num); val2.Write(val); SendPayload(container, val2); } } } private static void HandlePresence(Container container, long sender, TxOp op) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) ChestState state = GetState(container); if (state != null) { if (op == TxOp.ViewerOpen) { state.Viewers[sender] = Time.realtimeSinceStartup; TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " viewer open peer=" + sender); } else { state.Viewers.Remove(sender); TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " viewer close peer=" + sender); } } } private static void PumpManagerSlow() { //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) if (Time.realtimeSinceStartup < _nextSlowPump) { return; } _nextSlowPump = Time.realtimeSinceStartup + 0.5f; List<int> list = null; foreach (KeyValuePair<int, ChestState> state in States) { ChestState value = state.Value; if ((Object)(object)value.Container == (Object)null) { if (list == null) { list = new List<int>(); } list.Add(state.Key); continue; } ZNetView netView = TxReflect.GetNetView(value.Container); if ((Object)(object)netView == (Object)null || !netView.IsValid()) { continue; } bool flag = ServerAuthority.IsServerManagedContainer(value.Container); if (flag) { ServerAuthority.EnsureServerOwnership(value.Container, "pump"); } long owner = netView.GetZDO().GetOwner(); if (owner == ZNet.GetUID() && value.LastOwner != owner) { bool flag2 = false; try { flag2 = (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer(); } catch { flag2 = false; } if (TxAuthorityRouting.TakeoverAllowed(ServerAuthority.IsAuthorityMode(), flag, flag2)) { Takeover(value); } else { TxLog.Warn("container=" + TxLog.Zid(value.ZdoId) + " server-authority: remote Takeover refused (no client handoff; manager stays the server)"); } } value.LastOwner = owner; if (owner != ZNet.GetUID()) { continue; } if (value.TxQuarantined && !TryReloadAuthoritative(value)) { bool flag3 = false; try { flag3 = TxNullEscape.ShouldWarnQuarantine(value.QuarantineWarns); } catch { flag3 = true; } value.QuarantineWarns++; if (flag3) { TxLog.Warn("container=" + TxLog.Zid(value.ZdoId) + " still quarantined (pump reload failed, queue fail-closed, attempt=" + value.QuarantineWarns + ")"); } } Drain(value); try { TxReflect.UpdateRows(value.Container); } catch (Exception) { } PruneViewers(value); EnforceLid(value); } if (list != null) { for (int i = 0; i < list.Count; i++) { States.Remove(list[i]); } } } private static void Takeover(ChestState state) { //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: Unknown result type (might be due to invalid IL or missing references) long oldOwner = 0L; try { oldOwner = state.LastOwner; } catch { } try { ZNetView netView = TxReflect.GetNetView(state.Container); byte[] array = TxSItemsGuard.CloneBytes(netView.GetZDO().GetByteArray(ZDOVars.s_items, (byte[])null)); bool flag = false; string reason = null; if ((Object)(object)InventoryGui.instance != (Object)null) { TxReflect.CancelDragFrom(InventoryGui.instance, state.Container.GetInventory()); } if (TxSItemsLoad.TryLoad(state.Container, array, out reason)) { TxReflect.SetLastRevision(state.Container, netView.GetZDO().DataRevision); TxReflect.UpdateRows(state.Container); flag = true; } List<TxJob> list = DequeueAll(state); state.Processed.Clear(); state.ProcOrder.Clear(); RingData data = ReadRing(state.Container); SeedFromRing(state, data, ReadFloor(state.Container)); TxStatus txStatus = TxStatus.Rejected; if (list.Count > 0) { List<long> list2 = new List<long>(); foreach (TxJob item in list) { if (SeedReject(state, item.TxId, (item.Call != null) ? item.Call.Op : TxOp.Query, item.Sender, advanceFloor: true)) { list2.Add(item.TxId); } } bool ringPersisted = WriteRing(state); bool floorPersisted = WriteFloor(state); txStatus = TxDecision.HandoffDropTerminal(ringPersisted, floorPersisted); if (txStatus == TxStatus.UnknownTx) { TxLog.Warn("container=" + TxLog.Zid(state.ZdoId) + " takeover drops not persisted (indeterminate)"); foreach (long item2 in list2) { EvictSeededReject(state, item2); } } RequestPropagation(state); } foreach (TxJob item3 in list) { AnswerReject(state, item3, txStatus); } state.Viewers.Clear(); state.SeenRev = 0u; state.SeenOnce = false; state.SeenBytes = null; if (flag) { state.TxQuarantined = false; state.QuarantinedSince = 0f; state.QuarantineOldOwner = 0L; state.QuarantineWarns = 0; } else { StampQuarantine(state, oldOwner); if (array == null) { NoteSItemsNull("takeover"); TxLog.Warn("container=" + TxLog.Zid(state.ZdoId) + " takeover without authoritative reload (null s_items), staying quarantined"); } else { TxLog.Warn("container=" + TxLog.Zid(state.ZdoId) + " takeover without authoritative reload (invalid s_items: " + reason + "), staying quarantined"); } } TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " manager changed old=" + state.LastOwner + " new=" + ZNet.GetUID() + " revision=" + netView.GetZDO().DataRevision); } catch (Exception ex) { TxLog.Error("takeover failed: " + ex.Message); try { StampQuarantine(state, oldOwner); } catch { } } } private static void PruneViewers(ChestState state) { float realtimeSinceStartup = Time.realtimeSinceStartup; List<long> list = null; foreach (KeyValuePair<long, float> viewer in state.Viewers) { if (realtimeSinceStartup - viewer.Value > 15f) { if (list == null) { list = new List<long>(); } list.Add(viewer.Key); } } if (list != null) { for (int i = 0; i < list.Count; i++) { state.Viewers.Remove(list[i]); } } } private static void EnforceLid(ChestState state) { try { bool inUse = state.Viewers.Count > 0 || IsOpenByMe(state.Container); state.Container.SetInUse(inUse); } catch (Exception) { } } private static bool IsOpenByMe(Container container) { InventoryGui instance = InventoryGui.instance; if ((Object)(object)instance == (Object)null || !InventoryGui.IsVisible()) { return false; } return (Object)(object)InventoryAccess.CurrentContainer(instance) == (Object)(object)container; } internal static void Pump() { if (Plugin.IsActive) { TxNet.PumpHello(); ServerChestDirector.PumpPing(); TxDestroy.Pump(); PumpAuthoritySweep(); PumpManagerSlow(); ServerChestManager.Pump(); PumpPending(); PumpViewerRefresh(); } } private static uint CurrentRevision(Container container) { ZNetView netView = TxReflect.GetNetView(container); if ((Object)(object)netView == (Object)null || !netView.IsValid()) { return 0u; } return netView.GetZDO().DataRevision; } private static Vector3 LocalActorPos() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return Vector3.zero; } return ((Component)localPlayer).transform.position; } private static long LocalPlayerId() { Game instance = Game.instance; if ((Object)(object)instance == (Object)null) { return 0L; } PlayerProfile playerProfile = instance.GetPlayerProfile(); if (playerProfile == null) { return 0L; } return playerProfile.GetPlayerID(); } private static void TellPlayer(string message) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { ((Character)localPlayer).Message((MessageType)2, message, 0, (Sprite)null, false); } } internal static void Reset() { States.Clear(); Pending.Clear(); _clientCounter = 1u; _counterReservedCeil = 0u; SItemsNullDenials = 0L; LoadReservedCounter(); _nextSlowPump = 0f; _nextRefreshPoll = 0f; _nextAuthoritySweep = 0f; _nextAuthoritySweepHeartbeat = 0f; _openInstanceId = 0; _nextPresenceAt = 0f; } internal static void OnContainerAwake(Container container) { OnContainerAwake(container, fromSweep: false); } internal static void OnContainerAwake(Container container, bool fromSweep) { //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) try { bool flag = false; try { flag = ModConfig.TxVerbose.Value; } catch { flag = false; } if (flag) { Plugin.LogInstance.LogInfo((object)("[ChestTX] container awake: " + (((Object)(object)container == (Object)null) ? "null" : ((Object)container).name))); } } catch { } if ((Object)(object)container == (Object)null) { return; } ZNetView netView = TxReflect.GetNetView(container); if ((Object)(object)netView == (Object)null || !netView.IsValid()) { return; } try { netView.Register<ZPackage>("BestAutoSort_TxRequest", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg) { OnTxRequest(container, sender, pkg); }); netView.Register<ZPackage>("BestAutoSort_TxResponse", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg) { OnTxResponse(container, sender, pkg); }); } catch (Exception ex) { TxLog.Warn("container " + TxLog.Zid(netView.GetZDO().m_uid) + " RPC register failed: " + ex.Message); } int instanceID = ((Object)container).GetInstanceID(); if (!States.ContainsKey(instanceID)) { ChestState chestState = new ChestState(); chestState.Container = container; chestState.ZdoId = netView.GetZDO().m_uid; chestState.LastOwner = netView.GetZDO().GetOwner(); States[instanceID] = chestState; } AutoFeedService.Attach(container); ServerAuthority.EnsureOnAwake(container, fromSweep); TxRemoteUpgradeOnAwake(container); } private static void PumpAuthoritySweep() { try { if (!Plugin.IsActive) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < _nextAuthoritySweep) { return; } _nextAuthoritySweep = realtimeSinceStartup + 15f; bool flag = false; try { ZNet instance = ZNet.instance; flag = (Object)(object)instance != (Object)null && instance.IsServer(); } catch { flag = false; } if (!flag) { return; } bool flag2 = false; try { flag2 = ServerAuthority.IsAuthorityMode(); } catch { flag2 = false; } if (!flag2) { return; } Container[] array = null; try { array = Object.FindObjectsOfType<Container>(); } catch { array = null; } if (array == null) { return; } int num = array.Length; int num2 = 0; int num3 = 0; Container[] array2 = array; foreach (Container val in array2) { try { if ((Object)(object)val == (Object)null || !ServerAuthority.IsServerManagedContainer(val)) { continue; } num2++; if (GetState(val) == null) { OnContainerAwake(val, fromSweep: true); if (GetState(val) != null) { num3++; } } } catch { } } bool flag3 = false; if (realtimeSinceStartup >= _nextAuthoritySweepHeartbeat) { _nextAuthoritySweepHeartbeat = realtimeSinceStartup + 300f; flag3 = true; } if (!(num3 > 0 || flag3)) { return; } try { Plugin.LogInstance.LogInfo((object)("[ChestTX] server-authority: discovery sweep found=" + num + " managed=" + num2 + " registered=" + num3 + " known=" + States.Count)); } catch { } } catch { } } internal static bool IsSharedVerbose(Container container, out string why) { why = "ok"; if (!ModConfig.AllowConcurrentChestUse.Value) { why = "AllowConcurrentChestUse disabled"; return false; } if ((Object)(object)container == (Object)null) { why = "null container"; return false; } if (!((Behaviour)container).isActiveAndEnabled) { why = "behaviour disabled"; return false; } if (AutoFeedService.IsLocked(container)) { why = "autofeed lease/block active"; return false; } if ((Object)(object)container.m_wagon != (Object)null) { why = "wagon cart"; return false; } if ((Object)(object)((Component)container).GetComponentInParent<Ship>() != (Object)null) { why = "ship"; return false; } if ((Object)(object)((Component)container).GetComponent<Player>() != (Object)null) { why = "player inventory"; return false; } if ((Object)(object)((Component)container).GetComponent<TombStone>() != (Object)null) { why = "tombstone"; return false; } ZNetView netView = TxReflect.GetNetView(container); if ((Object)(object)netView == (Object)null) { why = "no netview"; return false; } if (!netView.IsValid()) { why = "netview invalid"; return false; } return true; } internal static bool IsShared(Container container) { string why; return IsSharedVerbose(container, out why); } internal static bool IsManager(Container container) { try { if (ServerAuthority.IsAuthorityMode() && ServerAuthority.IsServerManagedContainer(container)) { return ServerAuthority.IsAuthorityManager(container); } } catch { } return container.IsOwner(); } internal static bool IsQuarantined(Container container) { try { return GetState(container)?.TxQuarantined ?? false; } catch { return false; } } internal static ChestState GetState(Container container) { if ((Object)(object)container == (Object)null) { return null; } States.TryGetValue(((Object)container).GetInstanceID(), out var value); return value; } internal static bool HasPendingFor(Container container) { try { if ((Object)(object)container == (Object)null) { return false; } foreach (PendingTx value in Pending.Values) { try { if (value != null && (Object)(object)value.Container == (Object)(object)container) { return true; } } catch { } } } catch { } return false; } internal static void RequestAdd(Container container, Inventory srcInv, ItemData item, int amount, int wantX, int wantY, Action<ZPackage, TxStatus, uint, TxCompletionKind> onDone) { RequestAdd(container, srcInv, item, amount, wantX, wantY, onDone, alreadyRemoved: false); } internal static void RequestAdd(Container container, Inventory srcInv, ItemData item, int amount, int wantX, int wantY, Action<ZPackage, TxStatus, uint, TxCompletionKind> onDone, bool alreadyRemoved) { Inventory val = (((Object)(object)container != (Object)null) ? container.GetInventory() : null); if (val != null && wantX >= 0 && wantY >= 0 && (wantX >= val.GetWidth() || wantY >= val.GetHeight())) { TxLog.Info("want-clamped (" + wantX + "," + wantY + ") vs " + val.GetWidth() + "x" + val.GetHeight()); wantX = -1; wantY = -1; } TxOpItem opItem = SnapshotItem(item, amount, wantX, wantY); if (opItem == null) { TellPlayer("Item cannot be sent (no prefab)."); return; } if (wantX < 0 || wantY < 0) { opItem.X = -1; opItem.Y = -1; } TxOpCall txOpCall = new TxOpCall(); txOpCall.Op = TxOp.Add; txOpCall.Items.Add(opItem); Submit(container, txOpCall, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp) { int accepted = ReadAcceptedAt(pkg, 0); CompleteAdd(srcInv, item, opItem, accepted, status, rev, container, onDone, alreadyRemoved, disp); }, 0L, null, 1); } internal static void RequestAddBatch(Container container, Inventory srcInv, List<TxOpItem> items, Action<ZPackage, TxStatus, uint, TxCompletionKind> onDone) { if (items == null || items.Count == 0) { return; } TxOpCall txOpCall = new TxOpCall(); txOpCall.Op = TxOp.AddBatch; txOpCall.Items.AddRange(items); Submit(container, txOpCall, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp) { switch (disp) { case TxCompletionKind.Indeterminate: TxLog.Warn("addbatch indeterminate: outcome unknown, nothing removed — check the chest before retrying"); TellPlayer("Chest request outcome unknown. Check the chest before retrying."); RefreshNow(container); if (onDone != null) { onDone(EmptyCountBody(), status, rev, disp); } break; case TxCompletionKind.CommittedMultiAddDetailsUnavailable: TxLog.Warn("addbatch details-unavailable: committed but unattributed, nothing removed"); TellPlayer("Chest applied the move but per-item counts are unknown. Check the chest before retrying."); RefreshNow(container); if (onDone != null) { onDone(EmptyCountBody(), status, rev, disp); } break; default: { List<int> list = ReadAcceptedList(pkg, items.Count); for (int i = 0; i < items.Count && i < list.Count; i++) { CompleteAdd(srcInv, null, items[i], list[i], status, rev, container, null, alreadyRemoved: false, disp); } RefreshNow(container); if (onDone != null) { onDone(pkg, status, rev, disp); } break; } } }, 0L, null, items.Count); } internal static TxOpItem SnapshotAuto(ItemData item, int amount) { TxOpItem txOpItem = SnapshotItem(item, amount, -1, -1); if (txOpItem != null) { txOpItem.X = -1; txOpItem.Y = -1; } return txOpItem; } internal static TxOpItem SnapshotItem(ItemData item, int amount, int wantX, int wantY) { if (item == null || item.m_shared == null || (Object)(object)item.m_dropPrefab == (Object)null || amount <= 0) { return null; } ItemData val = item.Clone(); val.m_stack = amount; TxOpItem txOpItem = new TxOpItem(); txOpItem.Snapshot = val; txOpItem.SourceRef = item; txOpItem.PrefabHash = StringExtensionMethods.GetStableHashCode(((Object)item.m_dropPrefab).name); txOpItem.Amount = amount; txOpItem.X = item.m_gridPos.x; txOpItem.Y = item.m_gridPos.y; txOpItem.MaxStack = item.m_shared.m_maxStackSize; if (wantX >= 0 && wantY >= 0) { txOpItem.X = wantX; txOpItem.Y = wantY; } return txOpItem; } internal static void RequestTake(Container container, Inventory dstInv, ItemData snapshot, int amount, Action<ZPackage, TxStatus, uint, TxCompletionKind> onDone, int wantDstX = -1, int wantDstY = -1) { TxOpItem txOpItem = SnapshotItem(snapshot, amount, -1, -1); if (txOpItem != null) { TxOpCall txOpCall = new TxOpCall(); txOpCall.Op = TxOp.Take; txOpCall.Items.Add(txOpItem); Submit(container, txOpCall, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp) { CompleteTake(dstInv, pkg, status, rev, container, onDone, wantDstX, wantDstY, null, disp); }, 0L); } } internal static void RequestTakeBatchChunked(Container container, Inventory dstInv, List<TxOpItem> items, Action<ZPackage, TxStatus, uint, TxCompletionKind> onDone) { if (items == null || items.Count == 0) { return; } if (items.Count <= 4) { RequestTakeBatch(container, dstInv, items, onDone); return; } for (int i = 0; i < items.Count; i += 4) { List<TxOpItem> list = new List<TxOpItem>(); for (int j = i; j < items.Count && j < i + 4; j++) { list.Add(items[j]); } RequestTakeBatch(container, dstInv, list, onDone); } } internal static void RequestTakeBatch(Container container, Inventory dstInv, List<TxOpItem> items, Action<ZPackage, TxStatus, uint, TxCompletionKind> onDone) { if (items != null && items.Count != 0) { TxOpCall txOpCall = new TxOpCall(); txOpCall.Op = TxOp.TakeBatch; txOpCall.Items.AddRange(items); Submit(container, txOpCall, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp) { CompleteTake(dstInv, pkg, status, rev, container, onDone, -1, -1, null, disp); }, 0L); } } internal static void RequestMove(Container container, ItemData snapshot, int amount, int dstX, int dstY, Action<ZPackage, TxStatus, uint, TxCompletionKind> onDone) { TxOpItem txOpItem = SnapshotItem(snapshot, amount, -1, -1); if (txOpItem == null) { return; } TxOpCall txOpCall = new TxOpCall(); txOpCall.Op = TxOp.Move; txOpCall.Items.Add(txOpItem); txOpCall.DstX = dstX; txOpCall.DstY = dstY; Submit(container, txOpCall, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp) { RefreshNow(container); switch (status) { case TxStatus.UnknownTx: TellPlayer("Chest request outcome unknown. Check the chest before retrying."); break; case TxStatus.Rejected: TellPlayer("The shared chest changed. Try that item move again."); break; } if (onDone != null) { onDone(pkg, status, rev, disp); } }, 0L); } internal static void RequestSort(Container container, int mode, bool desc) { TxOpCall txOpCall = new TxOpCall(); txOpCall.Op = TxOp.Sort; txOpCall.Mode = mode; txOpCall.Desc = desc; Submit(container, txOpCall, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp) { RefreshNow(container); switch (status) { case TxStatus.UnknownTx: TellPlayer("Chest request outcome unknown. Check the chest before retrying."); break; default: TellPlayer("Chest sort failed. Try again."); break; case TxStatus.Accepted: case TxStatus.Duplicate: break; } }, 0L); } internal static void RequestSetRule(Container container, string rule) { TxOpCall txOpCall = new TxOpCall(); txOpCall.Op = TxOp.SetRule; txOpCall.Rule = rule ?? string.Empty; Submit(container, txOpCall, delegate(ZPackage pkg, TxStatus status, uint rev, TxCompletionKind disp) { RefreshNow(container); switch (status) { case TxStatus.UnknownTx: TellPlayer("Chest request outcome unknown. Check the chest before retrying."); break; default: TellPlayer("Chest rule was not saved. Try again."); break; case TxStatus.Accepted: case TxStatus.Duplicate: break; } }, 0L); } internal static void MutateLocal(Container container, TxOpCall call, Action<StoredResult> onDone, long playerId = 0L, Vector3? actorPos = null) { //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) ServerAuthority.EnsureServerOwnership(container, "local-tx"); ChestState state = GetState(container); if (state == null || !IsManager(container)) { if (onDone != null) { StoredResult storedResult = new StoredResult(); storedResult.Status = TxStatus.Rejected; onDone(storedResult); } return; } if (!TryClaimAddItems(call, out var claimed)) { TxLog.Info("local add skipped: all items already in flight"); if (onDone != null) { StoredResult storedResult2 = new StoredResult(); storedResult2.Op = call.Op; storedResult2.Status = TxStatus.Accepted; storedResult2.Revision = CurrentRevision(container); onDone(storedResult2); } return; } try { TxJob txJob = new TxJob(); txJob.Container = container; txJob.IsLocal = true; txJob.TxId = NextLocalTxId(); if (txJob.TxId == 0L) { TxLog.Warn("local tx refused: counter exhausted (fail closed)"); if (onDone != null) { StoredResult storedResult3 = new StoredResult(); storedResult3.Op = call.Op; storedResult3.Status = TxStatus.Rejected; storedResult3.Revision = CurrentRevision(container); onDone(storedResult3); } return; } txJob.Sender = ZNet.GetUID(); txJob.PlayerId = ((playerId != 0L) ? playerId : LocalPlayerId()); txJob.ActorPos = (actorPos.HasValue ? actorPos.Value : LocalActorPos()); txJob.BaseRev = CurrentRevision(container); txJob.Call = call; txJob.Complete = onDone; if (state.RingCorrupt && state.FloorCorrupt) { TxLog.Warn("tx=" + txJob.TxId + " local refused: ring corrupt (fail closed)"); if (onDone != null) { StoredResult storedResult4 = new StoredResult(); storedResult4.Op = call.Op; storedResult4.Status = TxStatus.UnknownTx; storedResult4.Revision = CurrentRevision(container); onDone(storedResult4); } } else if (state.TxQuarantined) { bool anyDurable; TxStatus txStatus = PersistQuarantineRefusal(state, txJob.TxId, call.Op, txJob.Sender, out anyDurable); if (txStatus == TxStatus.Rejected) { RequestPropagation(state, txJob.Sender); } TxLog.Warn("tx=" + txJob.TxId + " local refused: quarantined after post-fence failure (status=" + txStatus.ToString() + ", durable=" + anyDurable + ")"); if (onDone != null) { StoredResult storedResult5 = new StoredResult(); storedResult5.Op = call.Op; storedResult5.Status = txStatus; storedResult5.Revision = CurrentRevision(container); onDone(storedResult5); } } else if (IsFloorStale(state, txJob.TxId)) { TxLog.Warn("tx=" + txJob.TxId + " local refused: at/below execution floor (indeterminate)"); if (onDone != null) { StoredResult storedResult6 = new StoredResult(); storedResult6.Op = call.Op; storedResult6.Status = TxStatus.UnknownTx; storedResult6.Revision = CurrentRevision(container); onDone(storedResult6); } } else if (IsFloorCappedFor(state, txJob.TxId)) { TxLog.Warn("tx=" + txJob.TxId + " local refused: execution floor at capacity (fail closed)"); if (onDone != null) { StoredResult storedResult7 = new StoredResult(); storedResult7.Op = call.Op; storedResult7.Status = TxStatus.UnknownTx; storedResult7.Revision = CurrentRevision(container); onDone(storedResult7); } } else { state.Queue.Enqueue(txJob); Drain(state); } } finally { ReleaseClaimed(claimed); } } internal static void DrainForLocal(Container container) { ChestState state = GetState(container); if (state != null && IsManager(container)) { Drain(state); } } internal static bool AcquireForStructural(Container container) { //IL_02a2: Unknown result type (might be due to invalid IL or missing references) if (!IsShared(container)) { return false; } ServerAuthority.EnsureServerOwnership(container, "structural-acquire"); if (ServerAuthority.BlockStructuralForRemote(container, "acquire")) { return false; } if (IsManager(container)) { return true; } ZNetView netView = TxReflect.GetNetView(container); if ((Object)(object)netView == (Object)null || !netView.IsValid()) { return false; } try { long oldOwner = 0L; try { oldOwner = netView.GetZDO().GetOwner(); } catch { } netView.ClaimOwnership(); if (!container.IsOwner()) { return false; } byte[] array = TxSItemsGuard.CloneBytes(netView.GetZDO().GetByteArray(ZDOVars.s_items, (byte[])null)); if (!container.IsOwner()) { return false; } bool flag = false; string reason = null; if (TxSItemsLoad.TryLoad(container, array, out reason)) { TxReflect.SetLastRevision(container, netView.GetZDO().DataRevision); TxReflect.UpdateRows(container); flag = true; } ChestState state = GetState(container); if (state != null) { List<TxJob> list = DequeueAll(state); state.Processed.Clear(); state.ProcOrder.Clear(); SeedFromRing(state, ReadRing(container), ReadFloor(container)); TxStatus txStatus = TxStatus.Rejected; if (list.Count > 0) { List<long> list2 = new List<long>(); foreach (TxJob item in list) { if (SeedReject(state, item.TxId, (item.Call != null) ? item.Call.Op : TxOp.Query, item.Sender, advanceFloor: true)) { list2.Add(item.TxId); } } bool ringPersisted = WriteRing(state); bool floorPersisted = WriteFloor(state); txStatus = TxDecision.HandoffDropTerminal(ringPersisted, floorPersisted); if (txStatus == TxStatus.UnknownTx) { TxLog.Warn("structural acquire drops not persisted (indeterminate)"); foreach (long item2 in list2) { EvictSeededReject(state, item2); } } RequestPropagation(state); } foreach (TxJob item3 in list) { AnswerReject(state, item3, txStatus); } state.LastOwner = ZNet.GetUID(); if (flag) { state.TxQuarantined = false; state.QuarantinedSince = 0f; state.QuarantineOldOwner = 0L; state.QuarantineWarns = 0; } else { StampQuarantine(state, oldOwner); if (array == null) { NoteSItemsNull("structural-acquire"); TxLog.Warn("structural acquire without authoritative reload (null s_items), staying quarantined"); } else { TxLog.Warn("structural acquire without authoritative reload (invalid s_items: " + reason + "), staying quarantined"); } } } TxLog.Info("container=" + TxLog.Zid(netView.GetZDO().m_uid) + " structural acquire by " + ZNet.GetUID()); return true; } catch (Exception ex) { TxLog.Warn("structural acquire failed: " + ex.Message); return false; } } internal static long IssueTxId() { if (!ReserveTxCounter()) { return 0L; } if (!TxIdGen.TryNext(ZNet.GetUID(