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 StabilityFix v0.4.2
plugins/BestAutoSort.dll
Decompiled 9 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; 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.4.0.0")] [assembly: AssemblyInformationalVersion("0.4.0")] [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.4.0.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.4.0")] [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.4.0"; private Harmony? _harmony; private bool _shutDown; private static readonly Stopwatch UpdateTimer = new Stopwatch(); 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.4.0 (f0f1576) loaded."); } 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 (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_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) if (!IsActive) { return; } Timed("ConfigSync.Pump", ConfigSync.Pump); Timed("ChestTxService.Pump", ChestTxService.Pump); Timed("AutoFeedService.Update", AutoFeedService.Update); Timed("NearbyPlaceIntent.Pump", 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(); } } } private void Timed(string name, Action step) { UpdateTimer.Restart(); step(); UpdateTimer.Stop(); if (UpdateTimer.ElapsedMilliseconds > 100) { ((BaseUnityPlugin)this).Logger.LogWarning((object)$"Slow frame step {name}: {UpdateTimer.ElapsedMilliseconds} ms"); } } 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 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 (val.IsOwner()) { SortLocal(val); } } 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; } if (!ChestTxService.IsShared(val) || val.IsOwner()) { 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(); TxNet.Reset(); ConfigSync.Shutdown(); AutoFeedService.Reset(); InventoryButtons.Detach(); TransferContext.End(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _harmony = null; } } } internal static class BuildInfo { internal const string Commit = "f0f1576"; } } namespace BestAutoSort.Tx { internal static class ChestTxService { 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; internal const string RingKey = "BestAutoSort_TxRing"; 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; private static float _nextSlowPump; private static float _nextRefreshPoll; private static int _openInstanceId; private static float _nextPresenceAt; private static readonly HashSet<ItemData> InFlightAdds = new HashSet<ItemData>(); 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); } } private 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_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Expected O, but got Unknown //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: 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.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) { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) ChestState state = GetState(container); if (state != null) { if (state.Processed.TryGetValue(txId, out var value)) { TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " tx=" + txId + " QUERY hit"); Respond(container, sender, txId, TxStatus.Duplicate, value.Revision, EncodeCachedBody(value), value.TotalsOnly, value.Op); } else { Respond(container, sender, txId, TxStatus.UnknownTx, CurrentRevision(container), new ZPackage(), totalsOnly: true, TxOp.Query); } } } private 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); } private static void OnTxResponse(Container container, long sender, ZPackage pkg) { //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0168: 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; if (totalsOnly && status == TxStatus.Rejected) { Pending.Remove(txId); ReleaseClaimed(value.Claimed); TxLog.Info("tx=" + txId + " rejected by manager (nothing applied)"); try { if (value.OnResponse != null) { value.OnResponse(new ZPackage(), TxStatus.Rejected, revision); } } catch (Exception ex2) { TxLog.Error("tx=" + txId + " reject completion failed: " + ex2.Message); } RefreshNow(container); return; } if (totalsOnly && IsTakeOp(value.Op)) { Pending.Remove(txId); ReleaseClaimed(value.Claimed); TxLog.Warn("tx=" + txId + " totals-only Take: applied but items unrecoverable from cache"); TellPlayer("Chest applied the move but the items cannot be recovered. Check the chest."); RefreshNow(value.Container); return; } if (totalsOnly && !IsTakeOp(value.Op) && value.ExpectedItems > 1) { Pending.Remove(txId); ReleaseClaimed(value.Claimed); TxLog.Warn("tx=" + txId + " totals-only multi-add: applied but per-item counts unknown"); TellPlayer("Chest applied the move but per-item counts are unknown. Check the chest before retrying."); RefreshNow(value.Container); 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); } return; } if (!totalsOnly && !IsTakeOp(value.Op) && value.ExpectedItems >= 0 && !AddBodyMatchesArity(pkg, value.ExpectedItems)) { TxLog.Warn("tx=" + txId + " add body corrupt: re-querying instead of misattributing"); RefreshNow(value.Container); if (!ForceRefetch(txId)) { FinalizeUnknownTx(txId); } return; } Pending.Remove(txId); ReleaseClaimed(value.Claimed); try { if (value.OnResponse != null) { value.OnResponse(pkg, status, revision); } } catch (Exception ex3) { TxLog.Error("tx=" + txId + " completion failed: " + ex3.Message); } RefreshNow(container); } 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)) { return; } Pending.Remove(txId); ReleaseClaimed(value.Claimed); RefreshNow(value.Container); 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."); try { if (value.OnResponse != null) { value.OnResponse(null, TxStatus.UnknownTx, 0u); } } catch (Exception ex) { TxLog.Error("tx=" + value.TxId + " unknown completion failed: " + ex.Message); } } 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> onDone) { CompleteAdd(srcInv, itemRef, sent, accepted, status, rev, container, onDone, alreadyRemoved: false); } 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> onDone, bool alreadyRemoved) { LogNonMainInventory("add", srcInv); if (alreadyRemoved) { RestoreDragRemainder(srcInv, itemRef, accepted); if (status == TxStatus.Rejected || status == TxStatus.UnknownTx) { TellPlayer("The shared chest refused the move. Try again."); } onDone?.Invoke(null, status, rev); 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); } } if (status == TxStatus.Rejected || status == TxStatus.UnknownTx) { TellPlayer("The shared chest refused the move. Try again."); } onDone?.Invoke(null, status, rev); } private static void RestoreDragRemainder(Inventory srcInv, ItemData itemRef, int accepted) { if (srcInv == null || itemRef == null) { return; } CustomDataTags.StripBenign(itemRef); TxLog.Info("drag-restore stack=" + itemRef.m_stack + " accepted=" + accepted); int num = itemRef.m_stack - accepted; if (num > 0) { itemRef.m_stack = num; if (srcInv.AddItem(itemRef)) { srcInv.m_onChanged?.Invoke(); } else { DropAtFeet(itemRef, "drag restore"); } } } private static void DropAtFeet(ItemData item, string what) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: 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_0063: Unknown result type (might be due to invalid IL or missing references) if (item != null && item.m_stack > 0) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { TxLog.Error(what + " failed: no player, items lost: " + item.m_stack); return; } Transform transform = ((Component)localPlayer).transform; ItemDrop.DropItem(item, item.m_stack, transform.position + transform.forward + transform.up, transform.rotation); } } private static void CompensateAddBack(Container container, TxOpItem sent, int amount) { 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) { int num = 0; if (decoded != null) { foreach (DecodedTake item in decoded) { if (item != null && item.Accepted > 0) { num += item.Accepted; } } } if (status != TxStatus.Accepted && status != TxStatus.Partial && status != TxStatus.Duplicate) { TxLog.Warn("short-removal burn rejected (" + status.ToString() + "), excess stays in 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> onDone, int wantDstX = -1, int wantDstY = -1, Action<Dictionary<string, int>> onPlaced = null) { 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 { } } } if (status == TxStatus.Rejected || status == TxStatus.UnknownTx) { TellPlayer("The shared chest changed. Try again."); } onDone?.Invoke(pkg, status, rev); } 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) { 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); int num2 = TxInventory.AddAndCount(val, val2); if (num2 < num) { val2.m_stack = num - num2; DropAtFeet(val2, "compensate restore"); TxLog.Warn("compensate leftover did not fit, dropped at feet: " + (num - num2)); } 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) { CompleteTake(dstInv, pkg, status, rev, container, null, -1, -1, onLanded); }, 0L); } internal static void RequestTakeCustom(Container container, List<TxOpItem> items, bool respectReserves, Action<List<DecodedTake>, TxStatus, uint> onDone, long playerId = 0L, Vector3? actorPos = null) { if (items == null || items.Count == 0) { return; } TxOpCall txOpCall = new TxOpCall(); txOpCall.Op = TxOp.TakeBatch; txOpCall.Items.AddRange(items); txOpCall.RespectReserves = respectReserves; if (container.IsOwner()) { 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 != null) ? take.Item.Clone() : null), Accepted = take.Accepted }); } } } TxStatus arg = r?.Status ?? TxStatus.UnknownTx; uint arg2 = r?.Revision ?? CurrentRevision(container); if (onDone != null) { onDone(list, arg, arg2); } }, playerId, actorPos); return; } Submit(container, txOpCall, delegate(ZPackage pkg, TxStatus status, uint rev) { List<DecodedTake> list = TxCodec.ReadTakeResults(pkg); if (list == null) { TxLog.Warn("take-custom completion decode failed"); if (onDone != null) { onDone(new List<DecodedTake>(), TxStatus.Rejected, rev); } } else if (onDone != null) { onDone(list, status, rev); } }, playerId, actorPos); } internal static void SubmitCall(Container container, TxOpCall call, Inventory srcInv, Action<List<TransferRecord>> onDone) { SubmitCallDetailed(container, call, srcInv, delegate(List<TransferRecord> records, List<int> accepted, TxStatus status) { if (onDone != null) { onDone(records); } }); } internal static void SubmitCallDetailed(Container container, TxOpCall call, Inventory srcInv, Action<List<TransferRecord>, List<int>, TxStatus> onDone) { if (call != null && call.Items.Count > 4) { SubmitChunked(container, call, srcInv, onDone); return; } if (container.IsOwner()) { MutateLocal(container, call, delegate(StoredResult r) { List<int> list = new List<int>(); for (int i = 0; i < call.Items.Count; i++) { list.Add((r != null && r.Accepted != null && i < r.Accepted.Count) ? r.Accepted[i] : 0); } TxStatus arg = r?.Status ?? TxStatus.UnknownTx; List<TransferRecord> arg2 = FinishBatchCompletion(container, srcInv, call, r); if (onDone != null) { onDone(arg2, list, arg); } }, 0L); return; } Submit(container, call, delegate(ZPackage pkg, TxStatus status, uint rev) { List<int> list = ReadAcceptedList(pkg, call.Items.Count); List<TransferRecord> list2 = new List<TransferRecord>(); for (int i = 0; i < call.Items.Count && i < list.Count; i++) { TxOpItem txOpItem = call.Items[i]; CompleteAdd(srcInv, txOpItem.SourceRef, txOpItem, list[i], status, rev, container, null); if (list[i] > 0 && txOpItem.Snapshot != null && txOpItem.Snapshot.m_shared != null) { list2.Add(new TransferRecord(txOpItem.Snapshot.m_shared.m_name, txOpItem.Snapshot.GetIcon(), list[i], txOpItem.Snapshot.m_shared.m_maxStackSize)); } } RefreshNow(container); if (onDone != null) { onDone(list2, list, status); } }, 0L, null, call.Items.Count); } private static void SubmitChunked(Container container, TxOpCall call, Inventory srcInv, Action<List<TransferRecord>, List<int>, TxStatus> onDone) { List<TxOpCall> parts = new List<TxOpCall>(); 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]); } parts.Add(txOpCall); } List<int>[] partAccepted = new List<int>[parts.Count]; List<TransferRecord> allRecords = new List<TransferRecord>(); int left = parts.Count; bool anyUnknown = false; for (int k = 0; k < parts.Count; k++) { int index = k; SubmitCallDetailed(container, parts[index], srcInv, delegate(List<TransferRecord> records, List<int> accepted, TxStatus status) { if (partAccepted[index] == null) { partAccepted[index] = accepted ?? new List<int>(); if (records != null) { allRecords.AddRange(records); } if (status == TxStatus.UnknownTx) { anyUnknown = true; } int num = left; left = num - 1; if (left <= 0) { call.Items.Clear(); List<int> list = new List<int>(); int num2 = 0; bool flag = true; for (int l = 0; l < parts.Count; l++) { for (int m = 0; m < parts[l].Items.Count; m++) { TxOpItem txOpItem = parts[l].Items[m]; int num3 = ((m < partAccepted[l].Count) ? partAccepted[l][m] : 0); call.Items.Add(txOpItem); list.Add(num3); num2 += num3; if (num3 < txOpItem.Amount) { flag = false; } } } TxStatus arg = (anyUnknown ? TxStatus.UnknownTx : ((num2 == 0) ? TxStatus.Rejected : ((!flag) ? TxStatus.Partial : TxStatus.Accepted))); if (onDone != null) { onDone(allRecords, list, arg); } } } }); } } 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 realtimeSinceStartup = Time.realtimeSinceStartup; List<long> list = null; foreach (KeyValuePair<long, PendingTx> item in new List<KeyValuePair<long, PendingTx>>(Pending)) { PendingTx value = item.Value; if (!Pending.ContainsKey(item.Key)) { continue; } if ((Object)(object)value.Container == (Object)null) { if (list == null) { list = new List<long>(); } list.Add(item.Key); ReleaseClaimed(value.Claimed); } else if (realtimeSinceStartup >= value.Deadline) { if (!value.FinalQuerySent && (Object)(object)value.Container != (Object)null) { value.FinalQuerySent = true; value.QuerySent = true; SendQuery(value); value.Attempts++; value.Deadline = realtimeSinceStartup + 3f; value.NextTryAt = realtimeSinceStartup + 3f; TxLog.Warn("tx=" + value.TxId + " TIMEOUT op=" + value.Op.ToString() + " final query sent"); } else { if (list == null) { list = new List<long>(); } list.Add(item.Key); FinalizeUnknownTx(item.Key); } } else if (!(realtimeSinceStartup < value.NextTryAt) && value.Attempts < 4 + ((!value.QuerySent) ? 2 : 0)) { if (!value.QuerySent && value.Attempts >= 4) { value.QuerySent = true; SendQuery(value); } else { SendPayload(value.Container, value.Payload); } value.Attempts++; value.NextTryAt = realtimeSinceStartup + 3f; } } if (list == null) { return; } for (int i = 0; i < list.Count; i++) { if (!Pending.TryGetValue(list[i], out var value2)) { continue; } Pending.Remove(list[i]); if ((Object)(object)value2.Container == (Object)null && value2.OnResponse != null) { try { value2.OnResponse(null, TxStatus.UnknownTx, 0u); } catch (Exception ex) { TxLog.Error("tx=" + value2.TxId + " orphan completion failed: " + ex.Message); } } } } 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_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(2); val.Write(102); val.Write(0u); val.Write(LocalPlayerId()); val.Write(LocalActorPos()); val.Write(false); val.Write(false); 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_0150: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: 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 || val.IsOwner() || !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 (state.SeenOnce && dataRevision <= state.SeenRev) { if (dataRevision < state.SeenRev) { TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " viewer stale rev=" + dataRevision + " (seen " + state.SeenRev + "), skipped"); } return; } byte[] byteArray; try { byteArray = netView.GetZDO().GetByteArray(ZDOVars.s_items, (byte[])null); } catch (Exception) { return; } if (SameBytes(byteArray, state.SeenBytes)) { state.SeenRev = dataRevision; 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 { ZPackage pkg = (ZPackage)((byteArray == null) ? ((object)EmptyInventoryPackage(val)) : ((object)new ZPackage(byteArray))); TxReflect.LoadInventoryQuiet(val, pkg); 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 = byteArray; TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " viewer refresh rev=" + dataRevision); } private static ZPackage EmptyInventoryPackage(Container container) { //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_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(109); val.Write((ushort)0); return val; } private 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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown if (IsShared(container) && !container.IsOwner()) { ZNetView netView = TxReflect.GetNetView(container); if (!((Object)(object)netView == (Object)null) && netView.IsValid()) { long num = TxIdGen.Next(ZNet.GetUID(), ref _clientCounter); ZPackage val = new ZPackage(); val.Write(2); val.Write((int)op); val.Write(0u); val.Write(LocalPlayerId()); val.Write(LocalActorPos()); 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() { if (Time.realtimeSinceStartup < _nextSlowPump) { return; } _nextSlowPump = Time.realtimeSinceStartup + 0.5f; List<int> list = null; foreach (KeyValuePair<int, ChestState> item in new List<KeyValuePair<int, ChestState>>(States)) { ChestState value = item.Value; if ((Object)(object)value.Container == (Object)null) { if (list == null) { list = new List<int>(); } list.Add(item.Key); continue; } ZNetView netView = TxReflect.GetNetView(value.Container); if ((Object)(object)netView == (Object)null || !netView.IsValid()) { continue; } long owner = netView.GetZDO().GetOwner(); if (owner == ZNet.GetUID() && value.LastOwner != owner) { Takeover(value); } value.LastOwner = owner; if (owner == ZNet.GetUID()) { 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_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown try { ZNetView netView = TxReflect.GetNetView(state.Container); byte[] byteArray = netView.GetZDO().GetByteArray(ZDOVars.s_items, (byte[])null); if (byteArray != null) { if ((Object)(object)InventoryGui.instance != (Object)null) { TxReflect.CancelDragFrom(InventoryGui.instance, state.Container.GetInventory()); } TxReflect.LoadInventoryQuiet(state.Container, new ZPackage(byteArray)); TxReflect.SetLastRevision(state.Container, netView.GetZDO().DataRevision); TxReflect.UpdateRows(state.Container); } RejectQueued(state); state.Queue.Clear(); state.Processed.Clear(); state.ProcOrder.Clear(); List<RingEntry> ring = ReadRing(state.Container); SeedFromRing(state, ring); state.Viewers.Clear(); state.SeenRev = 0u; state.SeenOnce = false; state.SeenBytes = null; 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); } } internal static void RejectQueued(ChestState state) { if (state == null) { return; } while (state.Queue.Count > 0) { TxJob txJob = state.Queue.Dequeue(); try { StoredResult storedResult = new StoredResult(); storedResult.Op = ((txJob.Call != null) ? txJob.Call.Op : TxOp.Query); storedResult.Status = TxStatus.Rejected; try { ZNetView netView = TxReflect.GetNetView(state.Container); storedResult.Revision = (((Object)(object)netView != (Object)null && netView.IsValid()) ? netView.GetZDO().DataRevision : 0u); } catch { storedResult.Revision = 0u; } if (txJob.Complete != null) { txJob.Complete(storedResult); } } catch (Exception ex) { TxLog.Error("tx=" + txJob.TxId + " reject-queued failed: " + ex.Message); } } } 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) { if ((Object)(object)Player.m_localPlayer == (Object)null) { return; } 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(); PumpManagerSlow(); 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; _nextSlowPump = 0f; _nextRefreshPoll = 0f; _openInstanceId = 0; _nextPresenceAt = 0f; } internal static void OnContainerAwake(Container container) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) 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); } 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) { return container.IsOwner(); } 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 void RequestAdd(Container container, Inventory srcInv, ItemData item, int amount, int wantX, int wantY, Action<ZPackage, TxStatus, uint> 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> 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) { int accepted = ReadAcceptedAt(pkg, 0); CompleteAdd(srcInv, item, opItem, accepted, status, rev, container, onDone, alreadyRemoved); }, 0L, null, 1); } internal static void RequestAddBatch(Container container, Inventory srcInv, List<TxOpItem> items, Action<ZPackage, TxStatus, uint> 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) { 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); } RefreshNow(container); if (onDone != null) { onDone(pkg, status, rev); } }, 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> 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) { CompleteTake(dstInv, pkg, status, rev, container, onDone, wantDstX, wantDstY); }, 0L); } } internal static void RequestTakeBatchChunked(Container container, Inventory dstInv, List<TxOpItem> items, Action<ZPackage, TxStatus, uint> 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> 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) { CompleteTake(dstInv, pkg, status, rev, container, onDone); }, 0L); } } internal static void RequestMove(Container container, ItemData snapshot, int amount, int dstX, int dstY, Action<ZPackage, TxStatus, uint> 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) { RefreshNow(container); if (status == TxStatus.Rejected) { TellPlayer("The shared chest changed. Try that item move again."); } if (onDone != null) { onDone(pkg, status, rev); } }, 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) { RefreshNow(container); if (status != TxStatus.Accepted && status != TxStatus.Duplicate) { TellPlayer("Chest sort failed. Try again."); } }, 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) { RefreshNow(container); if (status != TxStatus.Accepted && status != TxStatus.Duplicate) { TellPlayer("Chest rule was not saved. Try again."); } }, 0L); } internal static void MutateLocal(Container container, TxOpCall call, Action<StoredResult> onDone, long playerId = 0L, Vector3? actorPos = null) { //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: 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) ChestState state = GetState(container); if (state == null || !container.IsOwner()) { 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(); 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; state.Queue.Enqueue(txJob); Drain(state); } finally { ReleaseClaimed(claimed); } } internal static void DrainForLocal(Container container) { ChestState state = GetState(container); if (state != null && container.IsOwner()) { Drain(state); } } internal static bool AcquireForStructural(Container container) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown //IL_00cf: Unknown result type (might be due to invalid IL or missing references) if (!IsShared(container)) { return false; } if (container.IsOwner()) { return true; } ZNetView netView = TxReflect.GetNetView(container); if ((Object)(object)netView == (Object)null || !netView.IsValid()) { return false; } try { netView.ClaimOwnership(); if (!container.IsOwner()) { return false; } byte[] byteArray = netView.GetZDO().GetByteArray(ZDOVars.s_items, (byte[])null); if (byteArray != null) { TxReflect.LoadInventoryQuiet(container, new ZPackage(byteArray)); TxReflect.SetLastRevision(container, netView.GetZDO().DataRevision); TxReflect.UpdateRows(container); } ChestState state = GetState(container); if (state != null) { RejectQueued(state); state.Queue.Clear(); state.Processed.Clear(); state.ProcOrder.Clear(); SeedFromRing(state, ReadRing(container)); state.LastOwner = ZNet.GetUID(); } 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; } } private static long NextLocalTxId() { return TxIdGen.Next(ZNet.GetUID(), ref _clientCounter); } private static bool TryClaimAddItems(TxOpCall call, out List<ItemData> claimed) { claimed = null; if (call == null || (call.Op != TxOp.Add && call.Op != TxOp.AddBatch)) { return true; } for (int num = call.Items.Count - 1; num >= 0; num--) { ItemData val = call.Items[num]?.SourceRef; if (val != null) { if (InFlightAdds.Add(val)) { if (claimed == null) { claimed = new List<ItemData>(); } claimed.Add(val); } else { call.Items.RemoveAt(num); TxLog.Info("add submit pruned duplicate in-flight item (already sending)"); } } } return call.Items.Count > 0; } private static void ReleaseClaimed(List<ItemData> claimed) { if (claimed == null) { return; } for (int i = 0; i < claimed.Count; i++) { if (claimed[i] != null) { InFlightAdds.Remove(claimed[i]); } } } private static void Submit(Container container, TxOpCall call, Action<ZPackage, TxStatus, uint> onDone, long playerId = 0L, Vector3? actorPos = null, int expectedItems = -1) { //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Expected O, but got Unknown //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Expected O, but got Unknown //IL_020f: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.IsActive || !IsShared(container)) { if (AutoFeedService.IsLocked(container)) { TellPlayer("Chest is busy (animal feeding in progress). Try again shortly."); } else { TellPlayer("Shared chest is not available."); } RejectEarly(container, onDone); return; } if (container.IsOwner()) { MutateLocal(container, call, delegate(StoredResult r) { RefreshNow(container); if (onDone != null) { ZPackage val3 = null; try { val3 = EncodeResultBody(call, r); if (val3 != null) { val3.SetPos(0); } } catch { } onDone(val3, r.Status, r.Revision); } }, playerId, actorPos); return; } if (!TryClaimAddItems(call, out var claimed)) { TxLog.Info("add submit skipped: all items already in flight"); if (onDone == null) { return; } try { onDone(new ZPackage(), TxStatus.Accepted, CurrentRevision(container)); return; } catch (Exception ex) { TxLog.Error("pruned-add completion failed: " + ex.Message); return; } } long num = TxIdGen.Next(ZNet.GetUID(), ref _clientCounter); ZPackage val = EncodeCall(call, CurrentRevision(container), playerId, actorPos); ZPackage val2 = new ZPackage(); val2.Write(num); val2.Write(val); ZNetView netView = TxReflect.GetNetView(container); if ((Object)(object)netView == (Object)null || !netView.IsValid()) { ReleaseClaimed(claimed); TellPlayer("Shared chest is not available."); RejectEarly(container, onDone); return; } PendingTx pendingTx = new PendingTx(); pendingTx.TxId = num; pendingTx.Container = container; pendingTx.Payload = val2; pendingTx.Op = call.Op; pendingTx.Claimed = claimed; pendingTx.Attempts = 0; pendingTx.ExpectedItems = expectedItems; pendingTx.NextTryAt = 0f; pendingTx.Deadline = Time.realtimeSinceStartup + 16f; pendingTx.OnResponse = onDone; Pending[num] = pendingTx; TxLog.Info("container=" + TxLog.Zid(netView.GetZDO().m_uid) + " tx=" + num + " peer=" + ZNet.GetUID() + " op=" + call.Op.ToString() + " " + DescribeCall(call) + " SEND"); SendPayload(container, val2); pendingTx.Attempts = 1; pendingTx.NextTryAt = Time.realtimeSinceStartup + 3f; } private static void RejectEarly(Container container, Action<ZPackage, TxStatus, uint> onDone) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown if (onDone == null) { return; } try { onDone(new ZPackage(), TxStatus.Rejected, CurrentRevision(container)); } catch (Exception ex) { TxLog.Error("early-reject completion failed: " + ex.Message); } } private static void SendPayload(Container container, ZPackage request) { ZNetView netView = TxReflect.GetNetView(container); if ((Object)(object)netView == (Object)null || !netView.IsValid()) { return; } try { netView.InvokeRPC("BestAutoSort_TxRequest", new object[1] { request }); } catch (Exception ex) { TxLog.Warn("send failed: " + ex.Message); } } private static string DescribeCall(TxOpCall call) { try { if (call.Items.Count == 0) { return ""; } TxOpItem txOpItem = call.Items[0]; string text = ((txOpItem.Snapshot != null && txOpItem.Snapshot.m_shared != null) ? txOpItem.Snapshot.m_shared.m_name : "?"); string text2 = "item=" + text + " x" + txOpItem.Amount + " want=(" + txOpItem.X + "," + txOpItem.Y + ")"; if (call.Items.Count > 1) { text2 = text2 + " (+" + (call.Items.Count - 1) + " more)"; } return text2; } catch (Exception) { return ""; } } private static ZPackage EncodeCall(TxOpCall call, uint baseRev, long playerId, Vector3? actorPos) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) ZPackage val = new ZPackage(); val.Write(2); val.Write((int)call.Op); val.Write(baseRev); val.Write((playerId != 0L) ? playerId : LocalPlayerId()); val.Write(actorPos.HasValue ? actorPos.Value : LocalActorPos()); val.Write(call.EnforceRule); val.Write(call.RespectReserves); switch (call.Op) { case TxOp.Add: case TxOp.AddBatch: case TxOp.Take: case TxOp.TakeBatch: { val.Write(call.Items.Count); for (int i = 0; i < call.Items.Count; i++) { WriteOpItem(val, call.Items[i]); } break; } case TxOp.Move: WriteOpItem(val, call.Items[0]); val.Write(call.DstX); val.Write(call.DstY); break; case TxOp.Sort: val.Write(call.Mode); val.Write(call.Desc); break; case TxOp.Upgrade: val.Write(call.Tier); break; case TxOp.SetRule: val.Write(call.Rule ?? string.Empty); break; } return val; } private static void WriteOpItem(ZPackage pkg, TxOpItem item) { //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(); item.Snapshot.Save(val); pkg.Write(item.PrefabHash); pkg.Write(val); pkg.Write(item.Amount); pkg.Write(item.X); pkg.Write(item.Y); pkg.Write(item.MaxStack); } private static void OnTxRequest(Container container, long sender, ZPackage request) { //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Expected O, but got Unknown //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Expected O, but got Unknown //IL_027b: 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) if (!Plugin.IsActive || !IsShared(container) || !container.IsOwner()) { return; } long txId; ZPackage payload; try { txId = request.ReadLong(); payload = request.ReadPackage(); } catch (Exception ex) { TxLog.Warn("bad request packet from " + sender + ": " + ex.Message); return; } if (!DecodeCall(payload, out var call, out var baseRev, out var playerId, out var actorPos)) { TxLog.Warn("tx=" + txId + " undecodable request from " + sender); return; } if (!TxNet.IsCompatiblePeer(sender) && sender != ZNet.GetUID()) { TxLog.Warn("tx=" + txId + " REJECT incompatible peer " + sender); Respond(container, sender, txId, TxStatus.Rejected, CurrentRevision(container), new ZPackage(), totalsOnly: true, call.Op); return; } if (call.Op == TxOp.ViewerOpen || call.Op == TxOp.ViewerClose) { HandlePresence(container, sender, call.Op); return; } if (call.Op == TxOp.Query) { RespondQuery(container, sender, txId); return; } if (!ChestAuthority.CanUse(container, sender, playerId, actorPos, out var why)) { TxLog.Warn("tx=" + txId + " REJECT access(" + why + ") peer=" + sender); Respond(container, sender, txId, TxStatus.Rejected, CurrentRevision(container), new ZPackage(), totalsOnly: true, call.Op); return; } ChestState state = GetState(container); if (state != null) { TxJob txJob = new TxJob(); txJob.Container = container; txJob.TxId = txId; txJob.Sender = sender; txJob.PlayerId = playerId; txJob.BaseRev = baseRev; txJob.ActorPos = actorPos; txJob.Call = call; txJob.Complete = delegate(StoredResult r) { ZPackage body = EncodeResultBody(call, r); Respond(container, sender, txId, r.Status, r.Revision, body, r.TotalsOnly, call.Op); }; state.Queue.Enqueue(txJob); Drain(state); } } private static bool DecodeCall(ZPackage payload, out TxOpCall call, out uint baseRev, out long playerId, out Vector3 actorPos) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) call = null; baseRev = 0u; playerId = 0L; actorPos = Vector3.zero; try { if (payload.ReadInt() != 2) { return false; } TxOp txOp = (TxOp)payload.ReadInt(); baseRev = payload.ReadUInt(); playerId = payload.ReadLong(); actorPos = payload.ReadVector3(); call = new TxOpCall(); call.Op = txOp; call.EnforceRule = payload.ReadBool(); call.RespectReserves = payload.ReadBool(); switch (txOp) { case TxOp.Add: case TxOp.AddBatch: case TxOp.Take: case TxOp.TakeBatch: { int num = payload.ReadInt(); if (num < 0 || num > 64) { return false; } for (int i = 0; i < num; i++) { if (!ReadOpItem(payload, out var item2)) { return false; } call.Items.Add(item2); } break; } case TxOp.Move: { if (!ReadOpItem(payload, out var item)) { return false; } call.Items.Add(item); call.DstX = payload.ReadInt(); call.DstY = payload.ReadInt(); break; } case TxOp.Sort: call.Mode = payload.ReadInt(); call.Desc = payload.ReadBool(); break; case TxOp.Upgrade: call.Tier = payload.ReadInt(); break; case TxOp.SetRule: call.Rule = payload.ReadString(); break; default: return false; case TxOp.ViewerOpen: case TxOp.ViewerClose: case TxOp.Query: break; } return true; } catch (Exception) { return false; } } private static bool ReadOpItem(ZPackage payload, out TxOpItem item) { item = null; try { int num = payload.ReadInt(); ZPackage inner = payload.ReadPackage(); int num2 = payload.ReadInt(); int x = payload.ReadInt(); int y = payload.ReadInt(); int num3 = payload.ReadInt(); if (num == 0 || num2 <= 0 || num2 > 9999) { return false; } ItemData val = TxCodec.ResolvePrefab(num, inner); if (val == null) { return false; } item = new TxOpItem(); item.Snapshot = val; item.PrefabHash = num; item.Amount = num2; item.X = x; item.Y = y; item.MaxStack = ((num3 <= 0) ? 50 : num3); return true; } catch (Exception) { return false; } } private static void Drain(ChestState state) { if (state == null || state.Draining || (Object)(object)state.Container == (Object)null || !state.Container.IsOwner()) { return; } state.Draining = true; try { int num = 0; while (state.Queue.Count > 0 && num < 64) { num++; TxJob txJob = state.Queue.Dequeue(); Stopwatch stopwatch = Stopwatch.StartNew(); StoredResult storedResult; try { storedResult = ApplyJob(state, txJob); stopwatch.Stop(); if (stopwatch.ElapsedMilliseconds > 100) { TxLog.Warn("tx=" + txJob.TxId + " op=" + ((txJob.Call != null) ? txJob.Call.Op.ToString() : "?") + " slow apply: " + stopwatch.ElapsedMilliseconds + " ms"); } } catch (Exception ex) { TxLog.Error("tx=" + txJob.TxId + " apply failed: " + ex.Message); storedResult = new StoredResult(); storedResult.Op = ((txJob.Call != null) ? txJob.Call.Op : TxOp.Query); storedResult.Status = TxStatus.Rejected; storedResult.Revision = CurrentRevision(state.Container); } try { if (txJob.Complete != null) { txJob.Complete(storedResult); } } catch (Exception ex2) { TxLog.Error("tx=" + txJob.TxId + " completion failed: " + ex2.Message); } if ((txJob.Call.Op == TxOp.Add || txJob.Call.Op == TxOp.AddBatch || txJob.Call.Op == TxOp.TakeBatch) && storedResult.AcceptedTotal() > 0) { TxFlights.BroadcastFlights(state, txJob, storedResult); } } } finally { state.Draining = false; } } private static StoredResult ApplyJob(ChestState state, TxJob job) { //IL_0026: 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) if (state.Processed.TryGetValue(job.TxId, out var value)) { TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " tx=" + job.TxId + " DUPLICATE returning cached result"); return CloneStored(value, TxStatus.Duplicate); } Inventory inventory = job.Container.GetInventory(); if (inventory == null) { return Reject(state, job); } StoredResult storedResult = ExecuteCall(state, job, inventory); CommitResult(state, job, storedResult); uint baseRev = job.BaseRev; uint num = CurrentRevision(job.Container); if (baseRev != 0 && baseRev != num && baseRev != storedResult.Revision) { TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " tx=" + job.TxId + " stale_revision client=" + baseRev + " server=" + num); } return storedResult; } private static StoredResult ExecuteCall(ChestState state, TxJob job, Inventory inv) { switch (job.Call.Op) { case TxOp.Add: case TxOp.AddBatch: return ExecuteAdd(inv, job.Call, state); case TxOp.Take: case TxOp.TakeBatch: return ExecuteTake(inv, job.Call, state); case TxOp.Move: return ExecuteMove(inv, job.Call); case TxOp.Sort: return ExecuteSort(inv, job.Call); case TxOp.Upgrade: return ExecuteUpgrade(state, job); case TxOp.SetRule: return ExecuteSetRule(state, job); default: return RejectNew(); } } private static StoredResult ExecuteAdd(Inventory inv, TxOpCall call, ChestState state) { StoredResult storedResult = new StoredResult(); storedResult.Op = call.Op; HashSet<string> hashSet = null; HashSet<ItemCategory> hashSet2 = null; ChestStorageRule rule = null; if (call.EnforceRule) { hashSet = new HashSet<string>(); hashSet2 = new HashSet<ItemCategory>(); QuickStackTransfer.DestSeeds(inv, hashSet, hashSet2); rule = ChestRuleStore.Read(state.Container); } int num = 0; for (int i = 0; i < call.Items.Count; i++) { TxOpItem txOpItem = call.Items[i]; string name = ((txOpItem.Snapshot.m_shared != null) ? txOpItem.Snapshot.m_shared.m_name : "?"); if (txOpItem.X >= inv.GetWidth() || txOpItem.Y >= inv.GetHeight()) { TxLog.Info("op=ADD item=" + TxCodec.Describe(name, txOpItem.Amount) + " want=(" + txOpItem.X + "," + txOpItem.Y + ") oob vs " + inv.GetWidth() + "x" + inv.GetHeight() + ", auto-place"); txOpItem.X = -1; txOpItem.Y = -1; } ItemData val = txOpItem.Snapshot.Clone(); val.m_stack = txOpItem.Amount; int num2 = 0; string text = ""; if (!call.EnforceRule || QuickStackTransfer.CanAcceptFromRule(rule, val, hashSet, hashSet2)) { if (txOpItem.X >= 0 && txOpItem.Y >= 0) { num2 = TxInventory.AddPositional(inv, val, txOpItem.Amount, txOpItem.X, txOpItem.Y); if (num2 < txOpItem.Amount) { ItemData val2 = null; string text2 = "oob"; if (txOpItem.X < inv.GetWidth() && txOpItem.Y < inv.GetHeight()) { val2 = inv.GetItemAt(txOpItem.X, txOpItem.Y); text2 = ((val2 != null && val2.m_shared != null) ? (val2.m_shared.m_name + " x" + val2.m_stack) : "empty"); } text = ((num2 > 0) ? "partial-pos" : "pos-blocked") + " chest=" + inv.GetWidth() + "x" + inv.GetHeight() + " cell=" + text2; } } else { num2 = TxInventory.AddAndCount(inv, val); if (num2 < txOpItem.Amount) { text = ((num2 > 0) ? "partial-full" : "full"); } } if (num2 > 0 && call.EnforceRule) { hashSet.Add(val.m_shared.m_name); hashSet2.Add(ValheimItemCategoryClassifier.Classify(val)); } } else { text = "rule"; } TxLog.Info("op=ADD item=" + TxCodec.Describe(name, txOpItem.Amount) + " want=(" + txOpItem.X + "," + txOpItem.Y + ") accepted=" + num2 + ((text.Length > 0) ? (" why=" + text) : "")); storedResult.Accepted.Add(num2); if (num2 == txOpItem.Amount) { num++; } } storedResult.Status = ((num != storedResult.Accepted.Count || storedResult.Accepted.Count <= 0) ? ((storedResult.AcceptedTotal() > 0) ? TxStatus.Partial : TxStatus.Rejected) : TxStatus.Accepted); return storedResult; } private static StoredResult ExecuteTake(Inventory inv, TxOpCall call, ChestState state) { StoredResult storedResult = new StoredResult(); storedResult.Op = call.Op; int num = 0; for (int i = 0; i < call.Items.Count; i++) { TxOpItem txOpItem = call.Items[i]; TakeEntry entry; int num2 = ((!call.RespectReserves) ? TakeSingle(inv, txOpItem, out entry) : TakeRespectingReserves(state, inv, txOpItem, out entry)); storedResult.Accepted.Add(num2); storedResult.Takes.Add(entry); if (num2 == txOpItem.Amount) { num++; } TxLog.Info("op=TAKE item=" + TxCodec.Describe(txOpItem.Snapshot.m_shared.m_name, txOpItem.Amount) + " requested=" + txOpItem.Amount + " taken=" + num2); } storedResult.Status = ((num != storedResult.Accepted.Count || storedResult.Accepted.Count <= 0) ? ((storedResult.AcceptedTotal() > 0) ? TxStatus.Partial : TxStatus.Rejected) : TxStatus.Accepted); return storedResult; } private static int TakeSingle(Inventory inv, TxOpItem it, out TakeEntry entry) { entry = null; ItemData val = TxCodec.ResolveIn(inv, it.Snapshot.m_shared.m_name, it.Snapshot.m_quality, it.Snapshot.m_variant, it.Snapshot.m_worldLevel, it.X, it.Y); if (val == null) { return 0; } int num = ((it.Amount < val.m_stack) ? it.Amount : val.m_stack); ItemData val2 = val.Clone(); val2.m_stack = num; int num2 = TxInventory.RemoveExact(inv, val, num); if (num2 <= 0) { return 0; } val2.m_stack = num2; entry = new TakeEntry(); entry.PrefabHash = it.PrefabHash; entry.Item = val2; entry.Accepted = num2; return num2; } private static int TakeRespectingReserves(ChestState state, Inventory inv, TxOpItem it, out TakeEntry entry) { entry = null; List<ItemData> list = TxCodec.ResolveAllIn(inv, it.Snapshot.m_shared.m_name, it.Snapshot.m_quality, it.Snapshot.m_variant, it.Snapshot.m_worldLevel); if (list.Count == 0) { return 0; } int num = it.Amount; int num2 = 0; ItemData val = null; foreach (ItemData item in list) { if (num <= 0) { break; } int num3 = ChestReserveStore.Available(state.Container, item); if (num3 > 0) { int num4 = ((num < item.m_stack) ? num : item.m_stack); if (num4 > num3) { num4 = num3; } if (val == null) { val = item.Clone(); } int num5 = TxInventory.RemoveExact(inv, item, num4); num2 += num5; num -= num5; } } if (num2 <= 0) { return 0; } val.m_stack = num2; entry = new TakeEntry(); entry.PrefabHash = it.PrefabHash; entry.Item = val; entry.Accepted = num2; return num2; } private static StoredResult ExecuteMove(Inventory inv, TxOpCall call) { StoredResult storedResult = new StoredResult(); storedResult.Op = call.Op; if (call.Items.Count == 0) { storedResult.Status = TxStatus.Rejected; storedResult.Accepted.Add(0); return storedResult; } TxOpItem txOpItem = call.Items[0]; ItemData val = TxCodec.ResolveIn(inv, txOpItem.Snapshot.m_shared.m_name, txOpItem.Snapshot.m_quality, txOpItem.Snapshot.m_variant, txOpItem.Snapshot.m_worldLevel, txOpItem.X, txOpItem.Y); int num = 0; if (val != null) { num = TxInventory.MoveWithin(inv, val, txOpItem.Amount, call.DstX, call.DstY); } storedResult.Status = ((num <= 0) ? TxStatus.Rejected : TxStatus.Accepted); storedResult.Accepted.Add(num); TxLog.Info("op=MOVE item=" + TxCodec.Describe(txOpItem.Snapshot.m_shared.m_name, txOpItem.Amount) + " dst=(" + call.DstX + "," + call.DstY + ") moved=" + num); return storedResult; } private static StoredResult ExecuteSort(Inventory inv, TxOpCall call) { StoredResult storedResult = new StoredResult(); storedResult.Op = call.Op; int item = InventorySorter.Sort(inv, (SortMode)call.Mode, call.Desc); storedResult.Status = TxStatus.Accepted; storedResult.Accepted.Add(item); TxLog.Info("op=SORT mode=" + call.Mode + " desc=" + call.Desc + " items=" + item); return storedResult; } private static StoredResult ExecuteUpgrade(ChestState state, TxJob job) { StoredResult storedResult = new StoredResult(); storedResult.Op = job.Call.Op; if ((Object)(object)InventoryAccess.CurrentContainer(InventoryGui.instance) != (Object)(object)state.Container) { storedResult.Status = TxStatus.Rejected; storedResult.Accepted.Add(0); TxLog.Info("op=UPGRADE REJECT manager-gui-mismatch tier=" + job.Call.Tier); return storedResult; } ChestUpgradeService.UpgradeOpenChest(job.Call.Tier); storedResult.Status = TxStatus.Accepted; storedResult.Accepted.Add(1); return storedResult; } private static StoredResult ExecuteSetRule(ChestState state, TxJob job) { StoredResult storedResult = new StoredResult(); storedResult.Op = job.Call.Op; if (!ChestStorageRuleCodec.TryDeserialize(job.Call.Rule ?? string.Empty, out var rule)) { storedResult.Status = TxStatus.Rejected; storedResult.Accepted.Add(0); return storedResult; } if (!ChestRuleStore.TryWrite(state.Container, rule, out var error)) { storedResult.Status = TxStatus.Rejected; storedResult.Accepted.Add(0); TxLog.Info("op=SETRULE REJECT " + error); return storedResult; } storedResult.Status = TxStatus.Accepted; storedResult.Accepted.Add(1); return storedResult; } private static void CommitResult(ChestState state, TxJob job, StoredResult result) { //IL_00dc: Unknown result type (might be due to invalid IL or missing references) bool flag = result.Status == TxStatus.Accepted || result.Status == TxStatus.Partial; if (flag) { TxReflect.UpdateRows(state.Container); TxReflect.SaveContainer(state.Container); } result.Revision = CurrentRevision(state.Container); state.Processed[job.TxId] = CloneStored(result, result.Status); state.ProcOrder.AddLast(job.TxId); while (state.ProcOrder.Count > 128) { long value = state.ProcOrder.First.Value; state.ProcOrder.RemoveFirst(); state.Processed.Remove(value); } if (flag) { WriteRing(state); TxReflect.SetLastRevision(state.Container, CurrentRevision(state.Container)); } TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " tx=" + job.TxId + " peer=" + job.Sender + " op=" + job.Call.Op.ToString() + " accepted=" + result.AcceptedTotal() + " revision=" + result.Revision); } private static StoredResult Reject(ChestState state, TxJob job) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) StoredResult storedResult = new StoredResult(); storedResult.Op = job.Call.Op; storedResult.Status = TxStatus.Rejected; storedResult.Revision = CurrentRevision(job.Container); TxLog.Info("container=" + TxLog.Zid(state.ZdoId) + " tx=" + job.TxId + " REJECT op=" + job.Call.Op); return storedResult; } private static StoredResult RejectNew() { return new StoredResult { Status = TxStatus.Rejected }; } private static StoredResult CloneStored(StoredResult src, TxStatus status) { return new StoredResult { Op = src.Op, Status = status, Revision = src.Revision, Accepted = new List<int>(src.Accepted), Takes = new List<TakeEntry>(src.Takes), TotalsOnly = src.TotalsOnly }; } private static bool IsTakeOp(TxOp op) { if (op != TxOp.Take) { return op == TxOp.TakeBatch; } return true; } private static void WriteRing(ChestState state) { try { ZNetView netView = TxReflect.GetNetView(state.Container); if ((Object)(object)netView == (Object)null || !netView.IsValid()) { return; } List<RingEntry> list = new List<RingEntry>(); LinkedListNode<long> linkedListNode = state.ProcOrder.Last; int num = 0; RingEntry item = default(RingEntry); while (linkedListNode != null && num < 32) { if (state.Processed.TryGetValue(linkedListNode.Value, out var value)) { item.TxId = linkedListNode.Value; item.AcceptedTotal = value.AcceptedTotal(); item.Revision = value.Revision; list.Add(item); num++; } linkedListNode = linkedListNode.Previous; } list.Reverse(); netView.GetZDO().Set("BestAutoSort_TxRing", BestAutoSort.TxCore.TxCore.EncodeRing(list)); } catch (Exception ex) { TxLog.Warn("ring persist failed: " + ex.Message); } } private static List<RingEntry> ReadRing(Container container) { try { ZNetView netView = TxReflect.GetNetView(container); if ((Object)(object)netView == (Object)null || !netView.IsValid()) { return new List<RingEntry>(); } byte[] byteArray = netView.GetZDO().GetByteArray(StringExtensionMethods.GetStableHashCode("BestAutoSort_TxRing"), (byte[])null); if (byteArray == null) { return new List<RingEntry>(); } return BestAutoSort.TxCore.TxCore.DecodeRing(byteArray); } catch (Exception) { return new List<RingEntry>(); } } private static void SeedFromRing(ChestState state, List<RingEntry> ring) { state.Processed.Clear(); state.ProcOrder.Clear(); for (int i = 0; i < ring.Count && i < 32; i++) { StoredResult storedResult = new StoredResult(); storedResult.Status = TxStatus.Duplicate; storedResult.Revision = ring[i].Revision; storedResult.Accepted = new List<int> { ring[i].AcceptedTotal }; storedResult.TotalsOnly = true; state.Processed[ring[i].TxId] = storedResult; state.ProcOrder.AddLast(ring[i].TxId); } } } internal static class TxCodec { public const int ProtoVersion = 2; public static void WriteHeader(ZPackage pkg, long txId, TxOp op, uint baseRev, long playerId, Vector3 actorPos) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) pkg.Write(2); pkg.Write(txId); pkg.Write((int)op); pkg.Write(baseRev); pkg.Write(playerId); pkg.Write(actorPos); } public static bool ReadHeader(ZPackage pkg, out long txId, out TxOp op, out uint baseRev, out long playerId, out Vector3 actorPos) { //IL_0011: 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_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) txId = 0L; op = (TxOp)0; baseRev = 0u; playerId = 0L; actorPos = Vector3.zero; try { if (pkg.ReadInt() != 2) { return false; } txId = pkg.ReadLong(); op = (TxOp)pkg.ReadInt(); baseRev = pkg.ReadUInt(); playerId = pkg.ReadLong(); actorPos = pkg.ReadVector3(); return true; } catch (Exception) { return false; } } public static void WriteResponseHeader(ZPackage pkg, long txId, TxStatus status, uint revision, bool totalsOnly) { pkg.Write(txId); pkg.Write((int)status); pkg.Write(revision); pkg.Write(totalsOnly); } public static bool ReadResponseHeader(ZPackage pkg, out long txId, out TxStatus status, out uint revision, out bool totalsOnly) { txId = 0L; status = TxStatus.Rejected; revision = 0u; totalsOnly = false; try { txId = pkg.ReadLong(); status = (TxStatus)pkg.ReadInt(); revision = pkg.ReadUInt(); totalsOnly = pkg.ReadBool(); return true; } catch (Exception) { return false; } } public static bool WriteItem(ZPackage pkg, ItemData item, int amount) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown if (item == null || item.m_shared == null || (Object)(object)item.m_dropPrefab == (Object)null || amount <= 0) { return false; } ItemData val = item.Clone(); val.m_stack = amount; ZPackage val2 = new ZPackage(); val.Save(val2); pkg.Write(StringExtensionMethods.GetStableHashCode(((Object)item.m_dropPrefab).name)); pkg.Write(val2); pkg.Write(amount); pkg.Write(val.m_gridPos.x); pkg.Write(val.m_gridPos.y); pkg.Write(item.m_shared.m_maxStackSize); return true; } public static bool ReadItem(ZPackage pkg, out int prefabHash, out ItemData item, out int amount, out int x, out int y, out int maxStack) { prefabHash = 0; item = null; amount = 0; x = -1; y = -1; maxStack = 50; try { prefabHash = pkg.ReadInt(); ZPackage inner = pkg.ReadPackage(); amount = pkg.ReadInt(); x = pkg.ReadInt(); y = pkg.ReadInt(); maxStack = pkg.ReadInt(); if (prefabHash == 0 || amount <= 0) { return false; } item = ResolvePrefab(prefabHash, inner); return item != null; } catch (Exception) { item = null; return false; } } public static ItemData ResolvePrefab(int prefabHash, ZPackage inner) { try { ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null) { return null; } GameObject itemPrefab = instance.GetItemPrefab(prefabHash); if ((Object)(object)itemPrefab == (Object)null) { return null; } ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null || component.m_itemData == null || component.m_itemData.m_shared == null) { return null; } ItemData val = component.m_itemData.Clone(); ItemData.Load(inner, val, (Item)109); val.m_dropPrefab = itemPrefab; return val; } catch (Exception) { return null; } } public static ItemData ResolveIn(Inventory inv, string name, int quality, int variant, int world, int x, int y) { if (inv == null || string.IsNullOrEmpty(name)) { return null; } if (x >= 0 && y >= 0) { ItemData itemAt = inv.GetItemAt(x, y); if (itemAt != null && itemAt.m_shared != null && string.Equals(itemAt.m_shared.m_name, name, StringComparison.Ordinal) && itemAt.m_quality == quality) { return itemAt; } } foreach (ItemData allItem in inv.GetAllItems()) { if (allItem != null && allItem.m_shared != null && string.Equals(allItem.m_shared.m_name, name, StringComparison.Ordinal) && allItem.m_quality == quality && allItem.m_variant == variant && allItem.m_worldLevel == world) { return allItem; } } return null; } public static List<ItemData> ResolveAllIn(Inventory inv, string name, int quality, int variant, int world) { List<ItemData> list = new List<ItemData>(); if (inv == null || string.IsNullOrEmpty(name)) { return list; } foreach (ItemData allItem in inv.GetAllItems()) { if (allItem != null && allItem.m_shared != null && string.Equals(allItem.m_shared.m_name, name, StringComparison.Ordinal) && allItem.m_quality == quality && allItem.m_variant == variant && allItem.m_worldLevel == world) { list.Add(allItem); } } return list; } public static string Describe(ItemData item) { if (item == null || item.m_shared == null) { return "null"; } return item.m_shared.m_name + " x" + item.m_stack;