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 RunicStorageNetworkFork v1.0.0
plugins/RunicStorageNetwork/RunicStorageNetwork.dll
Decompiled 2 weeks 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.Cryptography; using System.Text; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using RunicStorageNetwork.API; using RunicStorageNetwork.Logic; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.Rendering; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("RunicStorageNetwork")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] namespace RunicStorageNetwork { internal static class Actions { internal sealed class Pending { internal Operation Op; internal Player Player; internal InventoryGui Gui; internal Recipe Recipe; internal Piece Piece; internal ItemData Upgrade; internal ItemData Tool; internal int UpgradeQuality; internal int Variant; internal bool Multi; internal bool Output; internal bool Cancelled; internal bool Prepared; internal Vector3 Position; internal Quaternion Rotation; internal float Started; internal float LastPoll; internal bool Warned; internal RecoveryAttempt Recovery; internal string RollbackFailure; internal List<Debit> Plan; internal InventoryDelta Delta; } internal static Pending Waiting; internal static Pending Active; private static readonly OutcomeReceipts Outcomes = new OutcomeReceipts(); internal static bool Locked(Inventory inv) { return Waiting != null && Object.op_Implicit((Object)(object)Waiting.Player) && ((Humanoid)Waiting.Player).GetInventory() == inv; } internal static void Clear() { Waiting = null; Active = null; Outcomes.Clear(); TerminalTransfer.Clear(); NetworkTerminal.Close(); Stockroom.ClearObservations(); CraftPreparation.Clear(); RecipeIndex.Invalidate(); Plugin.ClearCritical(); } internal static PieceTable BuildTable(Player p) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown if (!Object.op_Implicit((Object)(object)p) || (Object)(object)p != (Object)(object)Player.m_localPlayer || !Object.op_Implicit((Object)(object)ObjectDB.instance)) { return null; } ItemData val = (ItemData)R.Call(p, "GetRightItem", Type.EmptyTypes); PieceTable val2 = val?.m_shared?.m_buildPieces; return (Object.op_Implicit((Object)(object)val2) && (Object)(object)p.GetBuildTool() == (Object)(object)val2 && BuildToolPolicy.Tool(val)) ? val2 : null; } internal static bool BuildPiece(Player p, Piece piece) { PieceTable val = BuildTable(p); if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)piece) || piece.m_repairPiece || piece.m_removePiece) { return false; } GameObject val2 = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.GetPrefab(R.Id(((Component)piece).gameObject)) : ((Component)piece).gameObject); return Object.op_Implicit((Object)(object)val2) && val.m_pieces != null && val.m_pieces.Contains(val2) && BuildToolPolicy.PieceReason(val2) == null; } internal static bool LocalBuildMaterials(Player p, Piece piece, RequirementMode mode) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Invalid comparison between Unknown and I4 if (!Object.op_Implicit((Object)(object)p) || !Object.op_Implicit((Object)(object)piece)) { return false; } Requirement[] resources = piece.m_resources; foreach (Requirement val in resources) { if (Object.op_Implicit((Object)(object)val.m_resItem) && val.m_amount > 0) { int num = (((int)mode == 2) ? 1 : val.m_amount); if (((Humanoid)p).GetInventory().CountItems(val.m_resItem.m_itemData.m_shared.m_name, -1, true) < num) { return false; } } } return true; } internal static bool BuildToolUsable(Player p, ItemData tool) { return tool != null && (!tool.m_shared.m_useDurability || tool.m_durability > 0f) && ((Character)p).HaveStamina(tool.m_shared.m_attack.m_attackStamina); } internal static Core Context(Player p, bool craft) { //IL_0089: 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) if (!Object.op_Implicit((Object)(object)p) || (Object)(object)p != (Object)(object)Player.m_localPlayer || !Plugin.Enabled || (!craft && !Object.op_Implicit((Object)(object)BuildTable(p)))) { return null; } CraftingStation currentCraftingStation = p.GetCurrentCraftingStation(); if (craft && (!Object.op_Implicit((Object)(object)currentCraftingStation) || currentCraftingStation.m_upgrader)) { return null; } return craft ? Core.Choose(((Component)currentCraftingStation).transform.position, p.GetPlayerID()) : Topology.Choose(((Component)p).transform.position, p.GetPlayerID(), BuilderCodex.Binding(p)); } internal static bool Craft(InventoryGui gui, Player p) { if (Active != null) { return true; } if (Waiting != null) { return false; } if (CraftPreparation.Claimed) { return CraftPreparation.Execute(gui, p); } Recipe val = R.Get<Recipe>(gui, "m_craftRecipe"); Core core = Context(p, craft: true); if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)core) || p.NoCostCheat() || ZoneSystem.instance.GetGlobalKey((GlobalKeys)25)) { return true; } return CraftPreparation.Execute(gui, p); } internal static bool Build(Player p, Piece piece) { //IL_00cc: 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) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Expected O, but got Unknown //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: 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_02b3: Unknown result type (might be due to invalid IL or missing references) switch (FastPathCore.BuildEntry(FastPath.Blocks(p), FastPath.Running, FastPath.Owns(p), FastPath.NativeReady(p))) { case FastPathBuildEntry.Native: return FastPath.ConsumeNativePlacement(p); case FastPathBuildEntry.Refuse: return false; default: { if (Active != null) { return true; } if (!BuildPiece(p, piece)) { return true; } if (Waiting != null) { return false; } if (LocalBuildMaterials(p, piece, (RequirementMode)0)) { return true; } Core core = Context(p, craft: false); if (!Object.op_Implicit((Object)(object)core) || p.NoCostCheat() || R.Get<bool>(p, "m_noPlacementCost") || ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey())) { return true; } Operation operation = Create(p, core, build: true, R.Id(((Component)piece).gameObject), 0, 1); if (Object.op_Implicit((Object)(object)piece.m_craftingStation)) { CraftingStation val = CraftingStation.HaveBuildStationInRange(piece.m_craftingStation.m_name, ((Component)p).transform.position); if (Object.op_Implicit((Object)(object)val) && R.Valid(R.View((Component)(object)val))) { operation.Station = R.View((Component)(object)val).GetZDO().m_uid; } } if (!operation.Validate(out var _, out var _, out var _)) { return false; } List<Debit> list = Planner.Plan(operation.Needs, Stockroom.Available(p, core, operation.Needs), compact: true); if (list == null) { return false; } if (list.All((Debit d) => d.Source == "player")) { return true; } R.Call(p, "UpdatePlacementGhost", new Type[1] { typeof(bool) }, false); if (R.Get<object>(p, "m_placementStatus").ToString() != "Valid") { return true; } GameObject val2 = R.Get<GameObject>(p, "m_placementGhost"); if (!Object.op_Implicit((Object)(object)val2)) { return true; } Pending pending = new Pending { Op = operation, Player = p, Piece = piece, Tool = (ItemData)R.Call(p, "GetRightItem", Type.EmptyTypes), Position = val2.transform.position, Rotation = val2.transform.rotation }; if (FastPath.TryBuild(pending, core, list)) { return false; } Start(pending, list); return false; } } } internal static Operation Create(Player p, Core core, bool build, string target, int quality, int multiplier) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) return new Operation { Id = Guid.NewGuid().ToString("N"), Target = target, Build = build, Quality = quality, Multiplier = multiplier, Actor = ((Character)p).GetZDOID(), Station = (build ? ZDOID.None : R.View((Component)(object)p.GetCurrentCraftingStation()).GetZDO().m_uid), Core = core.Id, Network = ((Component)core).GetComponent<NetworkMember>().SavedNetwork, Peer = ZNet.GetUID(), PlayerId = p.GetPlayerID() }; } internal static bool Propose(Pending pending, List<Debit> preview) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) Core core = Operation.CoreObject(pending.Op.Core); string network = (Object.op_Implicit((Object)(object)core) ? ((Component)core).GetComponent<NetworkMember>().Network : ""); pending.Op.Nodes = Topology.OperationNodes(network); if (pending.Op.Nodes.Length == 0 || pending.Op.Nodes.Length > 4096) { return false; } pending.Op.Sources = SourceSelection.Sources(preview); if (pending.Op.Sources == null) { return false; } pending.Op.PlayerStock = Stockroom.Snapshot(((Humanoid)pending.Player).GetInventory(), "player", pending.Op.Needs, chest: false); return true; } private static void Start(Pending pending, List<Debit> preview) { if (!Propose(pending, preview)) { if (Waiting == pending) { Waiting = null; } return; } Waiting = pending; if (pending.Recovery == null) { pending.Started = Time.unscaledTime; pending.Recovery = new RecoveryAttempt(pending.Started); } pending.Recovery.Sent(); pending.LastPoll = Time.unscaledTime; try { Transport.Instance.Begin(pending.Op); } catch (Exception ex) { Plugin.Debug(pending.Op.Id + " request; will replay same ID: " + ex.Message); } } internal static void RecoverCraft(Pending pending) { pending.Prepared = false; pending.Started = Time.unscaledTime; pending.Recovery = new RecoveryAttempt(pending.Started); pending.Recovery.Retry("offer expired", pending.Started); Waiting = pending; Topology.Dirty(); } internal static void Tick() { Pending waiting = Waiting; if (waiting == null) { return; } float unscaledTime = Time.unscaledTime; if (!Intent(waiting, out var reason)) { waiting.Cancelled = true; } if (!waiting.Warned && unscaledTime - waiting.Started > 10f) { waiting.Warned = true; Plugin.Critical(waiting.Op.Id, "Craft/build acknowledgement unresolved after 10 seconds; replaying the existing operation"); } if (waiting.Recovery.InFlight) { if (!(unscaledTime - waiting.LastPoll >= 2f)) { return; } waiting.LastPoll = unscaledTime; try { if (waiting.Prepared) { Transport.Instance.UseQuote(waiting.Op.Id); } else { Transport.Instance.Begin(waiting.Op); } return; } catch (Exception ex) { Plugin.Debug(waiting.Op.Id + " waiting for coordinator: " + ex.Message); return; } } if (!((double)unscaledTime < waiting.Recovery.Due) && !CraftPreparation.Releasing) { if (!Intent(waiting, out reason) || waiting.Recovery.Expired(unscaledTime)) { Waiting = null; Topology.Dirty(); } else { Retry(waiting); } } } internal static void Cancel() { if (Waiting != null) { Waiting.Cancelled = true; } } internal static void Refused(string id, string reason) { Plugin.Debug(id + " terminal refusal (resources released): " + reason); Pending waiting = Waiting; if (waiting?.Op.Id != id || !waiting.Recovery.InFlight) { return; } waiting.Prepared = false; if (waiting.Recovery.Retry(reason, Time.unscaledTime) && Intent(waiting, out var _)) { Topology.Dirty(); Plugin.Debug(id + " rebuilding supply and retrying automatically"); return; } if (!waiting.Cancelled && waiting.Recovery.Expired(Time.unscaledTime) && RecoveryAttempt.Transient(reason) && !reason.Contains("insufficient")) { Plugin.Critical(id, "Automatic recovery exhausted: " + reason); } Waiting = null; Topology.Dirty(); } private static void Retry(Pending p) { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) try { Topology.Dirty(); Topology.Refresh(force: true); Core core = Context(p.Player, !p.Op.Build); if (!Object.op_Implicit((Object)(object)core)) { if (!p.Recovery.Retry("network path or coverage changed", Time.unscaledTime)) { Waiting = null; } return; } Operation operation = Create(p.Player, core, p.Op.Build, p.Op.Target, p.Op.Quality, p.Op.Multiplier); operation.Station = p.Op.Station; if (!operation.Validate(out var _, out var _, out var reason)) { if (!p.Recovery.Retry(reason, Time.unscaledTime)) { Waiting = null; } return; } Stockroom.Refresh(core, p.Op.Sources); List<Stock> stock = Stockroom.Available(p.Player, core, operation.Needs); List<Debit> list = (operation.SelectNeeds(stock) ? Planner.Plan(operation.Needs, stock, compact: true) : null); if (list == null) { if (!p.Recovery.Retry("insufficient fresh resources", Time.unscaledTime)) { Waiting = null; } return; } Plugin.Debug(p.Op.Id + " retry -> " + operation.Id + " attempt=" + (p.Recovery.Attempts + 1)); p.Op = operation; p.Plan = null; p.Delta = null; Start(p, list); } catch (Exception e) { Plugin.Error(p.Op.Id + " recovery", e); Waiting = null; } } private static bool Intent(Pending p, out string reason) { //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Invalid comparison between Unknown and O //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Expected O, but got Unknown //IL_0166: 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_017d: 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) //IL_0192: Unknown result type (might be due to invalid IL or missing references) reason = "player context changed"; if (p.Cancelled || !Object.op_Implicit((Object)(object)p.Player) || (Object)(object)p.Player != (Object)(object)Player.m_localPlayer || ((Character)p.Player).IsDead() || p.Player.NoCostCheat()) { return false; } if (p.Op.Build) { reason = "build tool context changed"; if (!BuildPiece(p.Player, p.Piece) || !((Character)p.Player).InPlaceMode() || (Object)(object)p.Player.GetSelectedPiece() != (Object)(object)p.Piece || (object)(ItemData)R.Call(p.Player, "GetRightItem", Type.EmptyTypes) != p.Tool) { return false; } goto IL_026f; } reason = "recipe changed or disabled"; if (!RecipeIndex.Matches(p.Recipe, p.Op.Target)) { return false; } reason = "craft cancelled/changed"; if (Object.op_Implicit((Object)(object)p.Gui) && InventoryGui.IsVisible()) { CraftingStation currentCraftingStation = p.Player.GetCurrentCraftingStation(); ZDOID? val = ((currentCraftingStation == null) ? ((ZDOID?)null) : ((Component)currentCraftingStation).GetComponent<ZNetView>().GetZDO()?.m_uid); ZDOID station = p.Op.Station; if (val.HasValue && !(val.GetValueOrDefault() != station) && !((Object)(object)R.Get<Recipe>(p.Gui, "m_craftRecipe") != (Object)(object)p.Recipe) && R.Get<ItemData>(p.Gui, "m_craftUpgradeItem") == p.Upgrade) { object obj = R.Get<object>(p.Gui, "m_selectedRecipe"); if ((Object)(Recipe)obj.GetType().GetProperty("Recipe").GetValue(obj, null) != (Object)(object)p.Recipe) { return false; } if (p.Upgrade != null && (!((Humanoid)p.Player).GetInventory().ContainsItem(p.Upgrade) || p.Upgrade.m_quality != p.UpgradeQuality)) { return false; } goto IL_026f; } } return false; IL_026f: reason = "ok"; return true; } internal static void Ready(string id, List<Debit> plan) { //IL_04ce: Unknown result type (might be due to invalid IL or missing references) //IL_04de: Expected O, but got Unknown //IL_0422: Unknown result type (might be due to invalid IL or missing references) //IL_0434: Unknown result type (might be due to invalid IL or missing references) //IL_0439: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Invalid comparison between Unknown and O //IL_0449: Unknown result type (might be due to invalid IL or missing references) //IL_044e: Unknown result type (might be due to invalid IL or missing references) //IL_0393: Unknown result type (might be due to invalid IL or missing references) //IL_0399: Unknown result type (might be due to invalid IL or missing references) //IL_03b1: Unknown result type (might be due to invalid IL or missing references) //IL_03b7: Unknown result type (might be due to invalid IL or missing references) if (Outcomes.TryGet(id, out var result)) { Transport.Instance.Result(id, result.Success, result.Reason); return; } Pending waiting = Waiting; if (waiting == null || waiting.Op.Id != id) { Transport.Instance.Result(id, success: false, "no pending action"); return; } if (waiting.RollbackFailure != null) { Transport.InternalMutation++; try { waiting.Delta.Restore(); } catch (Exception e) { Plugin.Error(id + " retrying local rollback", e); return; } finally { Transport.InternalMutation--; } Outcomes.Record(id, result: false, waiting.RollbackFailure); Transport.Instance.Result(id, success: false, waiting.RollbackFailure); return; } bool flag = false; string text = "action not completed"; try { if (!Intent(waiting, out var reason)) { throw new InvalidOperationException(reason); } if (!waiting.Op.Validate(out var _, out var core, out var reason2)) { throw new InvalidOperationException(reason2); } if (!GatewayRuntime.ValidatePlan(waiting.Op, plan)) { throw new InvalidOperationException("gateway path changed before result"); } core.Scan(); foreach (Debit debit in plan.Where((Debit d) => d.Source != "player")) { Container val = ((IEnumerable<Container>)core.Pool).FirstOrDefault((Func<Container, bool>)((Container source) => Object.op_Implicit((Object)(object)source) && R.Valid(R.View((Component)(object)source)) && R.Key(R.View((Component)(object)source).GetZDO().m_uid) == debit.Source)); if (!Object.op_Implicit((Object)(object)val) || !Access.Container(val, waiting.Op.PlayerId, core, out var _, ownLease: true)) { throw new InvalidOperationException("source path/access changed before result: " + debit.Source); } } if (!Object.op_Implicit((Object)(object)waiting.Player) || (Object)(object)waiting.Player != (Object)(object)Player.m_localPlayer || ((Character)waiting.Player).IsDead() || waiting.Player.NoCostCheat()) { throw new InvalidOperationException("player context changed"); } if (waiting.Op.Build) { if (!BuildPiece(waiting.Player, waiting.Piece) || R.Get<bool>(waiting.Player, "m_noPlacementCost") || !((Character)waiting.Player).InPlaceMode() || (Object)(object)waiting.Player.GetSelectedPiece() != (Object)(object)waiting.Piece || (object)(ItemData)R.Call(waiting.Player, "GetRightItem", Type.EmptyTypes) != waiting.Tool || !BuildToolUsable(waiting.Player, waiting.Tool)) { throw new InvalidOperationException("build tool context changed"); } R.Call(waiting.Player, "UpdatePlacementGhost", new Type[1] { typeof(bool) }, false); GameObject val2 = R.Get<GameObject>(waiting.Player, "m_placementGhost"); if (!Object.op_Implicit((Object)(object)val2) || R.Get<object>(waiting.Player, "m_placementStatus").ToString() != "Valid" || Vector3.Distance(val2.transform.position, waiting.Position) > 0.05f || Quaternion.Angle(val2.transform.rotation, waiting.Rotation) > 0.5f) { throw new InvalidOperationException("placement moved/cancelled"); } goto IL_053d; } if (Object.op_Implicit((Object)(object)waiting.Gui) && InventoryGui.IsVisible()) { CraftingStation currentCraftingStation = waiting.Player.GetCurrentCraftingStation(); ZDOID? val3 = ((currentCraftingStation != null) ? new ZDOID?(((Component)currentCraftingStation).GetComponent<ZNetView>().GetZDO().m_uid) : ((ZDOID?)null)); ZDOID station = waiting.Op.Station; if (val3.HasValue && !(val3.GetValueOrDefault() != station) && !((Object)(object)R.Get<Recipe>(waiting.Gui, "m_craftRecipe") != (Object)(object)waiting.Recipe) && R.Get<ItemData>(waiting.Gui, "m_craftUpgradeItem") == waiting.Upgrade) { object obj = R.Get<object>(waiting.Gui, "m_selectedRecipe"); if ((Object)(Recipe)obj.GetType().GetProperty("Recipe").GetValue(obj, null) != (Object)(object)waiting.Recipe) { throw new InvalidOperationException("recipe selection changed"); } if (waiting.Upgrade != null && (!((Humanoid)waiting.Player).GetInventory().ContainsItem(waiting.Upgrade) || waiting.Upgrade.m_quality != waiting.UpgradeQuality)) { throw new InvalidOperationException("upgrade item changed"); } goto IL_053d; } } throw new InvalidOperationException("craft cancelled/changed"); IL_053d: waiting.Plan = plan; waiting.Delta = new InventoryDelta(((Humanoid)waiting.Player).GetInventory(), plan.Where((Debit d) => d.Source == "player"), chest: false); Active = waiting; Transport.InternalMutation++; try { waiting.Delta.Apply(); Plugin.Debug(id + " player debit confirmed"); if (waiting.Op.Build) { if (waiting.Player.TryPlacePiece(waiting.Piece)) { waiting.Output = true; FinishBuild(waiting); } } else { R.Set(waiting.Gui, "m_craftVariant", waiting.Variant); R.Set(waiting.Gui, "m_multiCrafting", waiting.Multi); waiting.Gui.m_multiCraftAmount = waiting.Op.Multiplier; R.Call(waiting.Gui, "DoCrafting", new Type[1] { typeof(Player) }, waiting.Player); } flag = waiting.Output; if (!flag) { waiting.Delta.Restore(); Plugin.Debug(id + " player delta restored: no result"); } } finally { Transport.InternalMutation--; Active = null; } } catch (Exception ex) { text = ex.Message; if (ex is InvalidOperationException) { Plugin.Debug(id + " action postponed: " + ex.Message); } else { Plugin.Error(id + " action", ex); } flag = waiting.Output; if (!flag) { InventoryDelta delta = waiting.Delta; if (delta != null && delta.Applied) { Transport.InternalMutation++; try { waiting.Delta.Restore(); } catch (Exception e2) { waiting.RollbackFailure = text; Plugin.Error(id + " local rollback unresolved", e2); return; } finally { Transport.InternalMutation--; } } } } Outcomes.Record(id, flag, flag ? "result observed" : text); if (flag) { Waiting = null; Stockroom.ClearObservations(); CraftInspection.Clear(); CraftOverview.Rescan(); Topology.Dirty(); } Transport.Instance.Result(id, flag, flag ? "result observed" : text); } internal static void FinishBuild(Pending p) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Invalid comparison between Unknown and I4 //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) R.Set(p.Player, "m_lastToolUseTime", Time.time); ((Character)p.Player).UseStamina((float)R.Call(p.Player, "GetBuildStamina", Type.EmptyTypes)); PieceTable val = R.Get<PieceTable>(p.Player, "m_buildPieces"); if ((int)val.m_skill > 0) { int num = R.Get<int>(p.Player, "m_buildRemoveDebt"); if (num > 0) { R.Set(p.Player, "m_buildRemoveDebt", num - 1); } else { ((Character)p.Player).RaiseSkill(val.m_skill, 1f); } } if (p.Tool.m_shared.m_useDurability) { ItemData tool = p.Tool; tool.m_durability -= (float)R.Call(p.Player, "GetPlaceDurability", new Type[1] { typeof(ItemData) }, p.Tool) * Game.m_durabilityRate; } p.Tool.m_shared.m_buildEffect.Create(((Component)p.Player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, ((Character)p.Player).GetZDOID()); } internal static bool PaidRequirements(Player p, Recipe recipe) { return Active != null && (Object)(object)Active.Player == (Object)(object)p && (Object)(object)Active.Recipe == (Object)(object)recipe; } internal static bool HaveCraft(Player player, Recipe recipe, int quality, int amount, out bool result) { result = false; if (PaidRequirements(player, recipe)) { result = true; return true; } Core core = Context(player, craft: true); if (!Object.op_Implicit((Object)(object)core)) { return false; } result = CraftPreparation.Available(player, recipe, quality, amount); return true; } internal static bool FirstIngredient(Player p, Recipe recipe, int quality, int multiplier, out ItemData item, out int amount, out int extra) { item = null; amount = (extra = 0); Core core = Context(p, craft: true); if (!Object.op_Implicit((Object)(object)core)) { return false; } List<Need> list = Stockroom.Requirements(recipe.m_resources, quality, multiplier); List<Stock> stock = ((!((Object)(object)Active?.Recipe == (Object)(object)recipe)) ? CraftPreparation.IngredientStock(p, recipe, quality, multiplier, list) : Active.Plan.Select((Debit d) => new Stock(d.Source, d.Item, d.Quality, d.Amount)).ToList()); foreach (Need n in list) { List<Need> needs = new List<Need> { n }; if (!Stockroom.Qualities(needs, stock, craft: true)) { continue; } Requirement val = recipe.m_resources.First((Requirement r) => Object.op_Implicit((Object)(object)r.m_resItem) && ((Object)r.m_resItem).name == n.Item); item = val.m_resItem.m_itemData.Clone(); item.m_quality = n.Quality; amount = n.Amount; extra = val.m_extraAmountOnlyOneIngredient; return true; } return false; } } internal sealed class ApiSource { internal string Token; internal ZDO Data; internal Container Container; internal InventoryDelta Delta; internal long PreviousOwner; internal long Creator; internal ushort OwnerRevision; internal uint DataRevision; internal bool Ready; internal bool Created; internal bool Denied; internal bool Released; internal bool Applied; internal byte[] Before; internal byte[] Buffer; internal byte[] Expected; internal int Received; internal int ReservedBytes; internal double LastSend; internal List<Stock> Stock; } internal static class ApiOwnership { private const string Fence = "rsn_api_fence"; private static readonly Dictionary<ZDOID, ApiSource> sources = new Dictionary<ZDOID, ApiSource>(); private static readonly Dictionary<ZDOID, ApiSource> offers = new Dictionary<ZDOID, ApiSource>(); private static readonly HashSet<Inventory> blocked = new HashSet<Inventory>(); private static ZDOID ownerTransition; private static bool transition; private static long bytes; internal static int Count => sources.Count; internal static bool Locked(Inventory inv) { return blocked.Contains(inv); } internal static bool LockedItem(ItemData item) { return blocked.Any((Inventory inv) => inv.ContainsItem(item)); } internal static bool? OwnerRule(ZDO z, long uid) { //IL_0008: 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_0038: Unknown result type (might be due to invalid IL or missing references) if (transition && ownerTransition == z.m_uid && uid == ZNet.GetUID()) { return true; } if (sources.TryGetValue(z.m_uid, out var value) && value.Ready) { return uid == z.GetOwner(); } return null; } internal static void Install(Harmony h) { //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_0071: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(typeof(ZDOMan), "RPC_ZDOData", new Type[2] { typeof(ZRpc), typeof(ZPackage) }, (Type[])null) ?? throw new MissingMethodException("ZDOMan.RPC_ZDOData"); h.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ApiOwnership), "FilterReplication", (Type[])null) { priority = 800 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); h.Patch((MethodBase)AccessTools.Method(typeof(ZNetScene), "RemoveObjects", new Type[2] { typeof(List<ZDO>), typeof(List<ZDO>) }, (Type[])null), new HarmonyMethod(typeof(ApiOwnership), "Pin", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Transport.ExtraInventoryLock = Locked; Transport.ExtraItemLock = LockedItem; Transport.ExtraOwnerRule = OwnerRule; } private static bool FilterReplication(ZDOMan __instance, ZRpc __0, ref ZPackage __1) { //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Expected O, but got Unknown //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer()) { return true; } object obj = R.Call(__instance, "FindPeer", new Type[1] { typeof(ZRpc) }, __0); if (obj == null) { return true; } long uid = R.Get<ZNetPeer>(obj, "m_peer").m_uid; byte[] array = null; ZPackage val = __1; int pos = val.GetPos(); val.SetPos(0); MemoryStream memoryStream = null; try { int num = val.ReadInt(); if (num < 0 || num > 65536) { return false; } for (int i = 0; i < num; i++) { val.ReadZDOID(); } for (int j = 0; j < 65536; j++) { int pos2 = val.GetPos(); ZDOID val2 = val.ReadZDOID(); if (val2 == ZDOID.None) { if (memoryStream != null) { memoryStream.Write(array, pos2, array.Length - pos2); __1 = new ZPackage(memoryStream.ToArray()); } return true; } ushort incomingRevision = val.ReadUShort(); val.ReadUInt(); val.ReadLong(); val.ReadVector3(); int num2 = val.ReadInt(); int pos3 = val.GetPos(); if (num2 < 0 || num2 > val.Size() - pos3) { return false; } val.SetPos(pos3 + num2); ZDO zDO = __instance.GetZDO(val2); ApiSource value; bool flag = sources.TryGetValue(val2, out value) && value.Ready; if (zDO != null && (zDO.GetInt("rsn_api_fence", 0) == 1 || flag) && !ApiRules.AcceptSourceFrame(flag, zDO.GetOwner(), zDO.OwnerRevision, uid, incomingRevision)) { if (memoryStream == null) { array = val.GetArray(); memoryStream = new MemoryStream(array.Length); memoryStream.Write(array, 0, pos2); } } else { memoryStream?.Write(array, pos2, val.GetPos() - pos2); } } return false; } catch (Exception ex) { ApiRuntime.Diagnostic("replication", ex.Message); return false; } finally { val.SetPos(pos); memoryStream?.Dispose(); } } private static void Pin(List<ZDO> currentNearObjects) { foreach (ApiSource item in sources.Values.Concat(offers.Values)) { if (Object.op_Implicit((Object)(object)item.Container) && !Object.op_Implicit((Object)(object)((Component)item.Container).GetComponent<UnloadedReplica>()) && R.Valid(R.View((Component)(object)item.Container)) && !currentNearObjects.Contains(item.Data)) { currentNearObjects.Add(item.Data); } } } private static void Block(ApiSource s, bool value) { if (Object.op_Implicit((Object)(object)s.Container)) { Inventory inventory = s.Container.GetInventory(); if (value) { blocked.Add(inventory); } else { blocked.Remove(inventory); } Integrations.Block(inventory, value); R.Set(s.Container, "m_inUse", value); } } internal static ApiSource Prepare(ZDO z, string token, long creator, double now) { //IL_0007: 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) //IL_0111: Unknown result type (might be due to invalid IL or missing references) if (sources.TryGetValue(z.m_uid, out var value)) { return (value.Token == token) ? value : null; } if (sources.Count >= 256 || z.GetString("rsn_lease", "") != "" || z.OwnerRevision == ushort.MaxValue || z.DataRevision >= 4294966271u) { return null; } ApiSource apiSource = new ApiSource { Data = z, Token = token, Creator = creator, PreviousOwner = z.GetOwner(), OwnerRevision = z.OwnerRevision, LastSend = now }; sources[z.m_uid] = apiSource; if (z.GetOwner() == ZNet.GetUID() || z.GetOwner() == 0L || ZNet.instance.GetPeer(z.GetOwner()) == null) { try { GameObject obj = ZNetScene.instance.FindInstance(z.m_uid); Container val = ((obj != null) ? obj.GetComponent<Container>() : null); if (Object.op_Implicit((Object)(object)val) && R.View((Component)(object)val).IsOwner() && !val.IsInUse() && !Transport.Locked(val.GetInventory()) && !Integrations.IsBusy(val.GetInventory())) { R.Call(val, "Save"); } Claim(apiSource, z.GetByteArray(ZDOVars.s_items, (byte[])null) ?? Array.Empty<byte>(), z.DataRevision); } catch { apiSource.Denied = true; } } else if (ApiRuntime.HasPeer(z.GetOwner())) { SendPrepare(apiSource); } else { apiSource.Denied = true; } return apiSource; } private static bool ReserveBytes(ApiSource s, int size) { int num = checked(size * 4 + 4096); if (size < 0 || size > 1048576 || bytes + num > 67108864) { return false; } bytes += num; s.ReservedBytes += num; return true; } private static void Claim(ApiSource s, byte[] content, uint revision) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) ZDO data = s.Data; if (data == null || !data.IsValid() || data.GetOwner() != s.PreviousOwner || data.OwnerRevision != s.OwnerRevision || !ApiWorld.Eligible(data, s.Creator)) { throw new InvalidOperationException("Source changed before handoff"); } if (s.ReservedBytes == 0 && !ReserveBytes(s, content.Length)) { throw new InvalidOperationException("API recovery budget exhausted"); } ApiWorld.Decode(data, content); GameObject obj = ZNetScene.instance.FindInstance(data.m_uid); Container val = ((obj != null) ? obj.GetComponent<Container>() : null); if (Object.op_Implicit((Object)(object)val) && R.View((Component)(object)val).IsOwner() && (val.IsInUse() || Integrations.IsBusy(val.GetInventory()) || Transport.Locked(val.GetInventory()))) { throw new InvalidOperationException("Source busy"); } ownerTransition = data.m_uid; transition = true; try { data.SetOwner(ZNet.GetUID()); } finally { transition = false; } if (data.GetOwner() != ZNet.GetUID()) { throw new InvalidOperationException("Ownership handoff refused"); } s.Ready = true; data.Set("rsn_api_fence", 1); data.DataRevision = Math.Max(data.DataRevision, revision); data.Set(ZDOVars.s_items, content); data.Set("rsn_lease", s.Token); data.Set(ZDOVars.s_inUse, 1, false); s.Before = (byte[])content.Clone(); s.Container = UnloadedNetworks.ApiContainer(data, out s.Created); if (!Object.op_Implicit((Object)(object)s.Container)) { throw new InvalidOperationException("Source adapter unavailable"); } Transport.InternalMutation++; try { R.Set(s.Container, "m_inUse", false); R.Set(s.Container, "m_lastRevision", uint.MaxValue); R.Call(s.Container, "Load"); } finally { Transport.InternalMutation--; } s.Stock = Stockroom.Snapshot(s.Container.GetInventory(), R.Key(data.m_uid), null, chest: true); Block(s, value: true); ZDOMan.instance.ForceSendZDO(data.m_uid); ApiWorld.Invalidate(data.m_uid); } private static void SendPrepare(ApiSource s) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_001b: Unknown result type (might be due to invalid IL or missing references) ZPackage val = new ZPackage(); val.Write(s.Token); val.Write(s.Data.m_uid); val.Write((int)s.OwnerRevision); val.Write(s.Creator); ApiRuntime.Send(s.PreviousOwner, "prepare", val); } internal static void ReceivePrepare(long sender, ZPackage p) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_02ca: Unknown result type (might be due to invalid IL or missing references) //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) if (sender != Transport.Server) { return; } string text = ApiWire.Text(p, 512); ZDOID val = p.ReadZDOID(); int num = p.ReadInt(); long num2 = p.ReadLong(); if (!ApiRuntime.Enabled) { Deny(sender, text, val); return; } if (offers.TryGetValue(val, out var value)) { if (value.Token == text) { SendOffer(value); } else { Deny(sender, text, val); } return; } ZDO val2 = RemoteContext.Data(val); object obj; if (val2 == null) { obj = null; } else { GameObject obj2 = ZNetScene.instance.FindInstance(val); obj = ((obj2 != null) ? obj2.GetComponent<Container>() : null); } Container val3 = (Container)obj; if (offers.Count >= 256 || !Object.op_Implicit((Object)(object)val3) || !R.Valid(R.View((Component)(object)val3)) || !R.View((Component)(object)val3).IsOwner() || val2.OwnerRevision != num || !ApiWorld.Eligible(val2, num2) || !(bool)R.Call(val3, "CheckAccess", new Type[1] { typeof(long) }, num2) || Transport.Reserved(val2) || val3.IsInUse() || Integrations.IsBusy(val3.GetInventory()) || Transport.Locked(val3.GetInventory())) { Deny(sender, text, val); return; } ApiSource apiSource = new ApiSource { Token = text, Data = val2, Container = val3, Creator = num2, OwnerRevision = val2.OwnerRevision, PreviousOwner = val2.GetOwner() }; try { R.Call(val3, "Load"); R.Call(val3, "Save"); byte[] array = val2.GetByteArray(ZDOVars.s_items, (byte[])null) ?? Array.Empty<byte>(); if (!ReserveBytes(apiSource, array.Length)) { throw new InvalidOperationException("Offer budget"); } apiSource.Before = (byte[])array.Clone(); apiSource.DataRevision = val2.DataRevision; Block(apiSource, value: true); val2.Set("rsn_lease", text); val2.Set(ZDOVars.s_inUse, 1, false); offers[val] = apiSource; SendOffer(apiSource); } catch { Block(apiSource, value: false); if (val2.GetString("rsn_lease", "") == text) { val2.Set("rsn_lease", ""); val2.Set(ZDOVars.s_inUse, 0, false); } offers.Remove(val); bytes -= apiSource.ReservedBytes; Deny(sender, text, val); } } private static void Deny(long peer, string token, ZDOID id) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_0010: Unknown result type (might be due to invalid IL or missing references) ZPackage val = new ZPackage(); val.Write(token); val.Write(id); val.Write(false); ApiRuntime.Send(peer, "grant", val); } private static void SendOffer(ApiSource s) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_0054: Unknown result type (might be due to invalid IL or missing references) int num = s.Before.Length; for (int i = 0; i < num || i == 0; i += 28000) { int num2 = Math.Min(28000, num - i); byte[] array = new byte[num2]; Array.Copy(s.Before, i, array, 0, num2); ZPackage val = new ZPackage(); val.Write(s.Token); val.Write(s.Data.m_uid); val.Write(true); val.Write((int)s.OwnerRevision); val.Write(s.DataRevision); val.Write(num); val.Write(i); val.Write(array); ApiRuntime.Send(Transport.Server, "grant", val); } } internal static void ReceiveGrant(long sender, ZPackage p) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (!ZNet.instance.IsServer()) { return; } string text = ApiWire.Text(p, 512); ZDOID key = p.ReadZDOID(); bool flag = p.ReadBool(); if (!sources.TryGetValue(key, out var value) || value.Token != text || value.PreviousOwner != sender || value.Ready) { return; } if (!flag) { value.Denied = true; return; } int num = p.ReadInt(); uint num2 = p.ReadUInt(); int num3 = p.ReadInt(); int num4 = p.ReadInt(); byte[] array = p.ReadByteArray(); if (num != value.OwnerRevision || num3 < 0 || num3 > 1048576 || num4 < 0 || array.Length > 28000 || num4 + array.Length > num3) { value.Denied = true; return; } if (value.Buffer == null) { if (num4 != 0 || !ReserveBytes(value, num3)) { value.Denied = true; return; } value.Buffer = new byte[num3]; value.DataRevision = num2; } if (value.Buffer.Length != num3 || value.DataRevision != num2) { value.Denied = true; } else { if (num4 < value.Received || num4 != value.Received) { return; } Array.Copy(array, 0, value.Buffer, num4, array.Length); value.Received += array.Length; if (value.Received == num3) { try { Claim(value, value.Buffer, num2); } catch (Exception ex) { value.Denied = true; ApiRuntime.Diagnostic("handoff", ex.Message); } } } } internal static void Tick(double now) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected O, but got Unknown //IL_00be: Unknown result type (might be due to invalid IL or missing references) ApiSource[] array = sources.Values.ToArray(); foreach (ApiSource apiSource in array) { if (!apiSource.Ready && !apiSource.Denied && now - apiSource.LastSend > 1.0) { apiSource.LastSend = now; SendPrepare(apiSource); } } ApiSource[] array2 = offers.Values.ToArray(); foreach (ApiSource apiSource2 in array2) { if (now - apiSource2.LastSend > 2.0) { apiSource2.LastSend = now; ZPackage val = new ZPackage(); val.Write(apiSource2.Token); val.Write(apiSource2.Data.m_uid); ApiRuntime.Send(Transport.Server, "ticket", val); } } } internal static void ReceiveTicket(long sender, ZPackage p) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_0069: Unknown result type (might be due to invalid IL or missing references) if (ZNet.instance.IsServer()) { string text = ApiWire.Text(p, 512); ZDOID val = p.ReadZDOID(); if (!sources.TryGetValue(val, out var value) || !(value.Token == text) || value.Ready) { ZPackage val2 = new ZPackage(); val2.Write(text); val2.Write(val); ApiRuntime.Send(sender, "release", val2); } } } internal static void ReceiveRelease(long sender, ZPackage p) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: 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) if (sender != Transport.Server) { return; } string text = ApiWire.Text(p, 512); ZDOID key = p.ReadZDOID(); if (offers.TryGetValue(key, out var value) && !(value.Token != text)) { Block(value, value: false); if (value.Data != null && value.Data.IsValid() && value.Data.GetOwner() == ZNet.GetUID() && value.Data.GetString("rsn_lease", "") == text) { value.Data.Set("rsn_lease", ""); value.Data.Set(ZDOVars.s_inUse, 0, false); } bytes -= value.ReservedBytes; offers.Remove(key); } } internal static bool Release(ApiSource s) { //IL_0153: 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) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Expected O, but got Unknown //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) if (s.Released) { return true; } if (s.Ready) { if (s.Data == null || !s.Data.IsValid() || s.Data.GetOwner() != ZNet.GetUID()) { return false; } string text = s.Data.GetString("rsn_lease", ""); if (text != "" && text != s.Token) { return false; } if (Object.op_Implicit((Object)(object)s.Container)) { Block(s, value: false); s.Data.Set("rsn_lease", ""); s.Data.Set(ZDOVars.s_inUse, 0, false); } else { s.Data.Set("rsn_lease", ""); s.Data.Set(ZDOVars.s_inUse, 0, false); } ZDOMan.instance.ForceSendZDO(s.Data.m_uid); ApiWorld.Invalidate(s.Data.m_uid); StorageIndex.Changed(R.Key(s.Data.m_uid)); } sources.Remove(s.Data.m_uid); bytes -= s.ReservedBytes; s.Released = true; Transport.Instance.SharedSourceGate.Released(s.Token, R.Key(s.Data.m_uid)); if (s.PreviousOwner != 0L && s.PreviousOwner != ZNet.GetUID()) { ZPackage val = new ZPackage(); val.Write(s.Token); val.Write(s.Data.m_uid); ApiRuntime.Send(s.PreviousOwner, "release", val); } if (s.Created) { UnloadedNetworks.ApiReleaseAdapter(s.Data.m_uid); } s.Before = null; s.Buffer = null; s.Expected = null; s.Delta = null; s.Stock = null; s.Container = null; return true; } internal static void Clear() { foreach (ApiSource item in offers.Values.Concat(sources.Values)) { if (Object.op_Implicit((Object)(object)item.Container)) { blocked.Remove(item.Container.GetInventory()); Integrations.Block(item.Container.GetInventory(), value: false); } } sources.Clear(); offers.Clear(); blocked.Clear(); bytes = 0L; } } internal sealed class ApiPaymentWorld : IApiPayment { private readonly ApiContext context; private readonly ApiLedger.Entry entry; private readonly List<Need> needs; private readonly List<ApiSource> sources = new List<ApiSource>(); private readonly HashSet<string> avoid = new HashSet<string>(); private ApiNetworkView view; private List<Debit> plan; private string[] keys; private int prepare; private int apply; private long creator; private bool captured; internal ApiPaymentWorld(ApiContext context, ApiLedger.Entry entry) { this.context = context; this.entry = entry; needs = entry.Request.Resources.Select((ResourceRequirement r) => new Need(r.Resource.PrefabName, r.Amount, r.Resource.Quality)).ToList(); } public ApiStatus Prepare() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_03b9: Unknown result type (might be due to invalid IL or missing references) if (!ApiRuntime.Enabled) { return ApiStatus.FeatureDisabled; } ZDO consumer; ApiStatus apiStatus = ApiWorld.Context(context, ZNet.GetUID(), receipt: true, out consumer, out creator); if (apiStatus != ApiStatus.Ready) { return apiStatus; } if (!UnloadedNetworks.Ward(consumer.GetPosition(), creator)) { return ApiStatus.AccessDenied; } view = ApiRuntime.View(context, consumer, creator); if (view == null) { return ApiStatus.CapacityExceeded; } if (view.Limited) { return ApiStatus.SnapshotLimit; } if (view.Graph == null || view.ObservedAt == 0.0 || view.Structure != UnloadedNetworks.CatalogRevision || (view.Scanning && ApiRuntime.Now - view.ObservedAt > 2.0)) { return ApiStatus.Updating; } if (string.IsNullOrEmpty(view.Network)) { return ApiStatus.NoNetwork; } if (keys == null) { entry.Network = view.Network; if (ApiRuntime.InNetwork(entry) > 8) { return ApiStatus.Busy; } plan = Planner.Plan(needs, from s in view.Stocks(needs) where !avoid.Contains(s.Source) select s, compact: true); if (plan == null) { return view.Complete ? ApiStatus.InsufficientResources : ApiStatus.SourcesUnavailable; } keys = SourceSelection.Sources(plan); if (keys == null || keys.Length > 32) { keys = null; return ApiStatus.PlanLimitExceeded; } if (Transport.Instance.PlayerWaiting(keys) || !Transport.Instance.SharedSourceGate.TryAcquire(entry.Id, keys)) { keys = null; return ApiStatus.Busy; } } if (view.Network != entry.Network) { return ApiStatus.NoNetwork; } if (prepare < keys.Length) { ZDO val = RemoteContext.Source(keys[prepare]); if (val == null || !ApiWorld.Eligible(val, creator)) { avoid.Add(keys[prepare]); return ApiStatus.SourcesUnavailable; } ApiSource apiSource = ApiOwnership.Prepare(val, entry.Id, creator, ApiRuntime.Now); if (apiSource == null) { avoid.Add(keys[prepare]); return ApiStatus.SourcesUnavailable; } sources.Add(apiSource); prepare++; return ApiStatus.Updating; } foreach (ApiSource source in sources) { if (source.Denied) { avoid.Add(R.Key(source.Data.m_uid)); return ApiStatus.SourcesUnavailable; } if (!source.Ready) { return ApiStatus.Updating; } } plan = Planner.Plan(needs, sources.SelectMany((ApiSource s) => GatewayRuntime.Filter(view.Graph, view.Network, s.Data.GetPosition(), consumer.GetPosition(), s.Stock)), compact: true); return (plan == null) ? ApiStatus.InsufficientResources : ApiStatus.Ready; } public void Repair() { view?.Repair(); view = null; keys = null; plan = null; sources.Clear(); prepare = (apply = 0); captured = false; } public void Capture() { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) if (!ApiRuntime.Enabled || view.Structure != UnloadedNetworks.CatalogRevision) { throw new InvalidOperationException("Network changed before debit"); } if (ApiWorld.Context(context, ZNet.GetUID(), receipt: true, out var consumer, out var num) != ApiStatus.Ready || num != creator || !UnloadedNetworks.Ward(consumer.GetPosition(), creator)) { throw new InvalidOperationException("Consumer access changed"); } foreach (ApiSource s in sources) { Check(s, before: true); if (!ApiWorld.Eligible(s.Data, creator) || !view.Graph.Covers(entry.Network, Topology.Position(s.Data.GetPosition()), (NetworkNode n) => true)) { throw new InvalidOperationException("Source access changed"); } if (plan.Where((Debit d) => d.Source == R.Key(s.Data.m_uid)).Any((Debit d) => !view.Graph.CanTransfer(entry.Network, Topology.Position(s.Data.GetPosition()), Topology.Position(consumer.GetPosition()), GatewayRuntime.Teleportable(d.Item)))) { throw new InvalidOperationException("Gateway path changed"); } s.Delta = new InventoryDelta(s.Container.GetInventory(), plan.Where((Debit d) => d.Source == R.Key(s.Data.m_uid)), chest: true); } captured = true; } private static void Check(ApiSource s, bool before) { if (s.Data == null || !s.Data.IsValid() || s.Data.GetOwner() != ZNet.GetUID() || s.Data.GetString("rsn_lease", "") != s.Token || !Object.op_Implicit((Object)(object)s.Container)) { throw new InvalidOperationException("Source ownership or reservation changed"); } byte[] array = (before ? s.Before : s.Expected); if (array != null && !(s.Data.GetByteArray(ZDOVars.s_items, (byte[])null) ?? Array.Empty<byte>()).SequenceEqual(array)) { throw new InvalidOperationException("Source changed outside its reservation"); } } public bool ApplyNext() { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) if (!captured) { throw new InvalidOperationException("Missing capture"); } if (apply >= sources.Count) { return true; } if (!ApiRuntime.Enabled || view.Structure != UnloadedNetworks.CatalogRevision) { throw new InvalidOperationException("Network changed before debit"); } ApiSource s = sources[apply]; Check(s, before: true); ZDO consumer = RemoteContext.Data(context.ConsumerId); if (consumer == null || plan.Where((Debit d) => d.Source == R.Key(s.Data.m_uid)).Any((Debit d) => !view.Graph.CanTransfer(entry.Network, Topology.Position(s.Data.GetPosition()), Topology.Position(consumer.GetPosition()), GatewayRuntime.Teleportable(d.Item)))) { throw new InvalidOperationException("Gateway path changed before debit"); } s.Applied = true; Transport.InternalMutation++; try { s.Delta.Apply(); R.Call(s.Container, "Save"); s.Expected = (byte[])(s.Data.GetByteArray(ZDOVars.s_items, (byte[])null) ?? Array.Empty<byte>()).Clone(); apply++; } catch { s.Expected = (byte[])(s.Data.GetByteArray(ZDOVars.s_items, (byte[])null) ?? Array.Empty<byte>()).Clone(); throw; } finally { Transport.InternalMutation--; } return apply == sources.Count; } public bool Rollback() { for (int num = sources.Count - 1; num >= 0; num--) { ApiSource apiSource = sources[num]; if (apiSource.Applied) { Check(apiSource, before: false); Transport.InternalMutation++; try { apiSource.Delta.Restore(); R.Call(apiSource.Container, "Save"); apiSource.Expected = (byte[])(apiSource.Data.GetByteArray(ZDOVars.s_items, (byte[])null) ?? Array.Empty<byte>()).Clone(); apiSource.Applied = false; } catch { apiSource.Expected = (byte[])(apiSource.Data.GetByteArray(ZDOVars.s_items, (byte[])null) ?? Array.Empty<byte>()).Clone(); throw; } finally { Transport.InternalMutation--; } return false; } } return true; } public bool Release() { foreach (ApiSource source in sources) { if (!source.Released) { if (!ApiOwnership.Release(source)) { return false; } return false; } } Transport.Instance.SharedSourceGate.Cancel(entry.Id); return true; } } internal sealed class ApiRuntime : MonoBehaviour { private sealed class Outgoing { internal long Peer; internal string Name; internal ZPackage Data; internal int Bytes; } private sealed class ReadInterest { internal ApiContext Context; internal long Peer; internal double Used; } private sealed class LocalRead { internal ApiContext Context; internal ResourceKey? Filter; internal ResourceSnapshot Snapshot; internal double Used; internal double Next; internal double ReceivedAt; internal long Serial; internal long ReceivedSerial; internal int Expected; internal int NextPage; internal int Bytes; internal readonly List<ResourceAmount> Rows = new List<ResourceAmount>(); } private sealed class LocalOperation { internal ApiContext Context; internal ConsumeRequest Request; internal ConsumeOperation Handle; internal double Next; } private const string Prefix = "RSN_API1_"; internal static ApiRuntime Instance; private readonly ApiLedger ledger = new ApiLedger(); private readonly List<ApiNetworkView> views = new List<ApiNetworkView>(); private readonly Dictionary<string, ReadInterest> reads = new Dictionary<string, ReadInterest>(); private readonly Dictionary<string, LocalRead> localReads = new Dictionary<string, LocalRead>(); private readonly Dictionary<string, LocalOperation> localOperations = new Dictionary<string, LocalOperation>(); private readonly Dictionary<long, ApiBucket> writes = new Dictionary<long, ApiBucket>(); private readonly Dictionary<long, ApiBucket> queries = new Dictionary<long, ApiBucket>(); private readonly Dictionary<long, ApiBucket> controls = new Dictionary<long, ApiBucket>(); private readonly Dictionary<long, double> peers = new Dictionary<long, double>(); private readonly Queue<ConsumeOperation> callbacks = new Queue<ConsumeOperation>(); private readonly Queue<Outgoing> outgoing = new Queue<Outgoing>(); private long outgoingBytes; private readonly Dictionary<string, double> warnings = new Dictionary<string, double>(); private ApiBucket writeRate; private ZRoutedRpc rpc; private ZDOMan world; private Harmony harmony; private int thread; private int scanCursor; private int jobCursor; private double helloAt; private double cleanupAt; private string session = ""; private bool serverEnabled; private int serverVersion; internal static double Now => Time.realtimeSinceStartupAsDouble; internal static bool Enabled => Object.op_Implicit((Object)(object)Instance) && Plugin.Enabled && UnloadedNetworks.Requested && (Server ? UnloadedNetworks.Enabled : (Instance.serverVersion == 1 && Instance.serverEnabled)); private static bool Main => Instance != null && Instance.thread == Thread.CurrentThread.ManagedThreadId; private static bool Server => Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer(); private void Awake() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown Instance = this; thread = Thread.CurrentThread.ManagedThreadId; writeRate = new ApiBucket(16.0, 16.0, Now); harmony = new Harmony("local.runicstoragenetwork.api"); try { ApiOwnership.Install(harmony); Transport.ExtraOperations = () => ledger.Entries.Count((ApiLedger.Entry e) => !e.Released); } catch (Exception ex) { harmony.UnpatchSelf(); Transport.ExtraInventoryLock = null; Transport.ExtraItemLock = null; Transport.ExtraOwnerRule = null; Transport.ExtraOperations = null; Instance = null; ((Behaviour)this).enabled = false; Plugin.Info("API unavailable: " + ex.Message); } } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } Reset(); Transport.ExtraInventoryLock = null; Transport.ExtraItemLock = null; Transport.ExtraOwnerRule = null; Transport.ExtraOperations = null; if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } private static string Consumer(ApiContext c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return R.Key(c.ConsumerId) + "/" + c.ModId; } private static bool Valid(ApiContext c) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) return c != null && c.ConsumerId != ZDOID.None && ApiRules.Text(c.ModId); } internal static bool HasPeer(long peer) { double value; return peer == ZNet.GetUID() || (Object.op_Implicit((Object)(object)Instance) && Instance.peers.TryGetValue(peer, out value) && Now - value < 15.0); } internal static void Diagnostic(string category, string message) { ApiRuntime instance = Instance; if (!Object.op_Implicit((Object)(object)instance)) { return; } double now = Now; if (!instance.warnings.TryGetValue(category, out var value) || !(now - value < 15.0)) { if (instance.warnings.Count >= 32) { instance.warnings.Clear(); } instance.warnings[category] = now; Plugin.Info("API " + category + ": " + message); } } private void Reset() { foreach (LocalOperation value in localOperations.Values) { if (!value.Handle.IsFinal) { value.Handle.Complete(new ConsumeResult(value.Handle.RequestId, "", ConsumptionOutcome.OutcomeUnknown, ApiStatus.Unavailable)); QueueCallback(value.Handle); } } if ((Object)(object)Transport.Instance != (Object)null) { foreach (ApiLedger.Entry entry in ledger.Entries) { Transport.Instance.SharedSourceGate.Cancel(entry.Id); } } ApiOwnership.Clear(); ApiWorld.Clear(); ledger.Clear(); views.Clear(); reads.Clear(); localReads.Clear(); localOperations.Clear(); writes.Clear(); queries.Clear(); controls.Clear(); peers.Clear(); outgoing.Clear(); outgoingBytes = 0L; rpc = null; session = ""; serverVersion = 0; serverEnabled = false; } private void Update() { //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Expected O, but got Unknown //IL_03dc: Unknown result type (might be due to invalid IL or missing references) //IL_03e3: Expected O, but got Unknown try { if (world != ZDOMan.instance) { Reset(); world = ZDOMan.instance; if (world != null && Server) { session = Guid.NewGuid().ToString("N"); serverVersion = 1; serverEnabled = Enabled; } } if (world == null || ZRoutedRpc.instance == null || !Object.op_Implicit((Object)(object)ZNet.instance)) { DeliverCallbacks(); return; } if (rpc != ZRoutedRpc.instance) { rpc = ZRoutedRpc.instance; Register(); } double now = Now; if (!Server && now >= helloAt && Transport.Server != 0) { helloAt = now + 3.0; ZPackage val = new ZPackage(); val.Write(1); Send(Transport.Server, "hello", val); } if (Server) { ApiLedger.Entry[] array = ledger.Entries.Where((ApiLedger.Entry e) => !e.Released).ToArray(); int num = Math.Min(8, array.Length); for (int num2 = 0; num2 < num; num2++) { ApiLedger.Entry entry = array[(jobCursor + num2) % array.Length]; ApiPayment apiPayment = (ApiPayment)entry.Work; apiPayment.Tick(now); entry.State = apiPayment.State; if (apiPayment.Outcome.HasValue) { ledger.Finish(entry, apiPayment.Outcome.Value, apiPayment.Reason, apiPayment.Released); NotifyLocal(entry); if (apiPayment.Released) { entry.Work = null; } } if (apiPayment.State == ConsumptionState.Recovering && now - entry.Started > 10.0) { Diagnostic("recovery", "Operation " + entry.Id + " is retaining its reservation until its original debit is resolved."); } } if (array.Length != 0) { jobCursor = (jobCursor + num) % array.Length; } Stopwatch stopwatch = Stopwatch.StartNew(); int num3 = 0; while (views.Count > 0 && num3++ < 128 && stopwatch.Elapsed.TotalMilliseconds < 0.5) { if (scanCursor >= views.Count) { scanCursor = 0; } ApiNetworkView apiNetworkView = views[scanCursor++]; if (now - apiNetworkView.Used < 30.0 && Enabled && UnloadedNetworks.ApiCatalogReady) { apiNetworkView.Tick(now); } } } LocalOperation[] array2 = localOperations.Values.ToArray(); foreach (LocalOperation localOperation in array2) { if (!localOperation.Handle.IsFinal && now >= localOperation.Next) { localOperation.Next = now + 1.0; if (Server) { ProcessLocal(localOperation); } else if (Transport.Server != 0) { ZPackage p = new ZPackage(); ApiWire.Context(p, localOperation.Context); ApiWire.Request(p, localOperation.Request); Send(Transport.Server, "consume", p); } } } ApiOwnership.Tick(now); for (int num5 = 0; num5 < 16; num5++) { if (this.outgoing.Count <= 0) { break; } Outgoing outgoing = this.outgoing.Dequeue(); outgoingBytes -= outgoing.Bytes; if (outgoing.Peer == ZNet.GetUID()) { Dispatch(outgoing.Name, outgoing.Peer, outgoing.Data); } else if (outgoing.Peer != 0) { rpc.InvokeRoutedRPC(outgoing.Peer, "RSN_API1_" + outgoing.Name, new object[1] { outgoing.Data }); } } if (now >= cleanupAt) { cleanupAt = now + 5.0; Clean(now); } DeliverCallbacks(); } catch (Exception ex) { Diagnostic("tick", ex.Message); } } private void Clean(double now) { string[] array = (from x in reads where now - x.Value.Used > 30.0 select x.Key).ToArray(); foreach (string key in array) { reads.Remove(key); } string[] array2 = (from x in localReads where now - x.Value.Used > 30.0 select x.Key).ToArray(); foreach (string key2 in array2) { localReads.Remove(key2); } views.RemoveAll((ApiNetworkView v) => now - v.Used > 30.0 && !ledger.Entries.Any((ApiLedger.Entry e) => !e.Released && e.Network == v.Network)); ApiWorld.Trim(now); long[] array3 = peers.Keys.Where((long p) => now - peers[p] > 30.0).ToArray(); foreach (long key3 in array3) { peers.Remove(key3); writes.Remove(key3); queries.Remove(key3); controls.Remove(key3); } } internal static int InNetwork(ApiLedger.Entry current) { return Instance.ledger.Entries.Count((ApiLedger.Entry e) => !e.Released && e.Network == current.Network && e.Order <= current.Order); } internal static bool AllowViewBytes(ApiNetworkView current, int bytes) { return Instance.views.Sum((ApiNetworkView v) => (v == current) ? bytes : v.Bytes) <= 4194304; } internal static ApiNetworkView View(ApiContext context, ZDO consumer, long creator) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) ApiRuntime instance = Instance; Point point = Topology.Position(consumer.GetPosition()); ApiNetworkView apiNetworkView = instance.views.FirstOrDefault((ApiNetworkView v) => v.Creator == creator && (v.Point.Distance2(point) < 0.001 || (v.Graph != null && v.Structure == UnloadedNetworks.CatalogRevision && !string.IsNullOrEmpty(v.Network) && v.Graph.Choose(point, (NetworkNode n) => true) == v.Network))); if (apiNetworkView == null) { if (instance.views.Count >= 128) { return null; } instance.views.Add(apiNetworkView = new ApiNetworkView { Creator = creator, Point = point }); } apiNetworkView.Used = Now; return apiNetworkView; } private ConsumerRecovery Recovery(ApiContext context) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) ZDO val = RemoteContext.Data(context.ConsumerId); ApiLedger.Entry entry = ledger.Find(Consumer(context)); return new ConsumerRecovery((uint)((val != null) ? val.OwnerRevision : 0), entry?.Request, entry?.Result, entry?.State ?? ConsumptionState.Pending); } private ResourceSnapshot Snapshot(ApiContext c, long peer, ResourceKey? filter) { //IL_00d0: Unknown result type (might be due to invalid IL or missing references) ApiStatus apiStatus = ApiWorld.Context(c, peer, receipt: false, out var z, out var creator); if (apiStatus != ApiStatus.Ready) { return new ResourceSnapshot(apiStatus, session, "", 0L); } ConsumerRecovery recovery = Recovery(c); if (!Enabled) { return new ResourceSnapshot(ApiStatus.FeatureDisabled, session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, recovery); } if (!UnloadedNetworks.ApiCatalogReady) { return new ResourceSnapshot(ApiStatus.NotReady, session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, recovery); } if (!UnloadedNetworks.Ward(z.GetPosition(), creator)) { return new ResourceSnapshot(ApiStatus.AccessDenied, session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, recovery); } if (filter.HasValue && !Known(filter.Value)) { return new ResourceSnapshot(ApiStatus.UnknownItem, session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, recovery); } ApiNetworkView apiNetworkView = View(c, z, creator); if (apiNetworkView == null) { return new ResourceSnapshot(ApiStatus.SnapshotLimit, session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, recovery); } if (apiNetworkView.Graph == null || apiNetworkView.ObservedAt == 0.0) { return new ResourceSnapshot(ApiStatus.Updating, session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, recovery); } if (apiNetworkView.Limited) { return new ResourceSnapshot(ApiStatus.SnapshotLimit, session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, recovery); } if (apiNetworkView.Structure != UnloadedNetworks.CatalogRevision) { return new ResourceSnapshot(ApiStatus.Updating, session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, recovery); } bool flag = apiNetworkView.Structure != UnloadedNetworks.CatalogRevision || Now - apiNetworkView.ObservedAt > 2.0; apiStatus = (flag ? ApiStatus.Updating : (string.IsNullOrEmpty(apiNetworkView.Network) ? ApiStatus.NoNetwork : ((!apiNetworkView.Complete) ? ApiStatus.Partial : ApiStatus.Ready))); IReadOnlyList<ResourceAmount> resources = apiNetworkView.Resources; if (filter.HasValue) { apiNetworkView.Amounts.TryGetValue(filter.Value, out var value); resources = Array.AsReadOnly(new ResourceAmount[1] { new ResourceAmount(filter.Value, value) }); } return new ResourceSnapshot(apiStatus, session, apiNetworkView.Network, apiNetworkView.Revision, apiNetworkView.ObservedAt, Math.Max(0.0, Now - apiNetworkView.ObservedAt), flag, apiNetworkView.Complete && !flag && apiStatus == ApiStatus.Ready, apiNetworkView.Unknown, resources, recovery); } private static bool Known(ResourceKey k) { return ApiRules.Text(k.PrefabName) && k.Quality > 0 && k.Quality <= 10000 && Object.op_Implicit((Object)(object)ObjectDB.instance) && Object.op_Implicit((Object)(object)ObjectDB.instance.GetItemPrefab(k.PrefabName)); } internal static ResourceSnapshot Read(ApiContext context, ResourceKey? filter) { //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_030b: Expected O, but got Unknown if (Instance == null) { return new ResourceSnapshot(ApiStatus.FeatureDisabled, "", "", 0L); } if (!Main) { return new ResourceSnapshot(ApiStatus.WrongThread, "", "", 0L); } if (!Valid(context)) { return new ResourceSnapshot(ApiStatus.InvalidRequest, "", "", 0L); } ApiRuntime instance = Instance; if (instance.world == null || !Object.op_Implicit((Object)(object)ZNet.instance)) { return new ResourceSnapshot(ApiStatus.Unavailable, "", "", 0L); } ZDO z; long creator; ApiStatus apiStatus = ApiWorld.Context(context, ZNet.GetUID(), receipt: false, out z, out creator); if (apiStatus != ApiStatus.Ready) { return new ResourceSnapshot(apiStatus, instance.session, "", 0L); } if (filter.HasValue && !Known(filter.Value)) { return new ResourceSnapshot(ApiStatus.UnknownItem, instance.session, "", 0L); } string text = Consumer(context) + (filter.HasValue ? ("/" + filter.Value) : "/*"); if (!instance.localReads.TryGetValue(text, out var value)) { if (instance.localReads.Count >= 128) { return new ResourceSnapshot(ApiStatus.SnapshotLimit, instance.session, "", 0L); } value = (instance.localReads[text] = new LocalRead { Context = context, Filter = filter }); } double num = (value.Used = Now); if (num >= value.Next) { value.Next = num + 1.0; if (Server) { value.Snapshot = instance.Snapshot(context, ZNet.GetUID(), filter); value.ReceivedAt = num; } else if (instance.serverVersion == 1 && Transport.Server != 0) { value.Serial++; ZPackage val = new ZPackage(); val.Write(text); val.Write(value.Serial); ApiWire.Context(val, context); val.Write(filter.HasValue); if (filter.HasValue) { ApiWire.Key(val, filter.Value); } Send(Transport.Server, "read", val); } } if (!Server && Plugin.Enabled && UnloadedNetworks.Requested && instance.serverVersion == 0) { return new ResourceSnapshot(ApiStatus.NotReady, instance.session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, value.Snapshot?.Recovery); } if (!Server && instance.serverVersion != 0 && instance.serverVersion != 1) { return new ResourceSnapshot(ApiStatus.IncompatibleVersion, instance.session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, value.Snapshot?.Recovery); } if (!Enabled) { return new ResourceSnapshot(ApiStatus.FeatureDisabled, instance.session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, value.Snapshot?.Recovery); } if (value.Snapshot == null) { return new ResourceSnapshot((instance.serverVersion > 1) ? ApiStatus.IncompatibleVersion : ((instance.serverVersion == 1 && !instance.serverEnabled) ? ApiStatus.FeatureDisabled : ApiStatus.NotReady), instance.session, "", 0L); } ResourceSnapshot snapshot = value.Snapshot; if (num - value.ReceivedAt > 2.0) { return new ResourceSnapshot(ApiStatus.Updating, instance.session, snapshot.NetworkId, snapshot.Revision, 0.0, snapshot.Age + num - value.ReceivedAt, stale: true, complete: false, 0, snapshot.Resources, snapshot.Recovery); } return snapshot; } internal static ResourceAmountResult Amount(ApiContext context, ResourceKey resource) { ResourceSnapshot resourceSnapshot = Read(context, resource); return new ResourceAmountResult(resourceSnapshot, resourceSnapshot.Resources.FirstOrDefault((ResourceAmount r) => r.Resource.Equals(resource))?.ObservedAmount); } private static ConsumeOperation Reject(ApiStatus status, string id = "", ConsumptionOutcome? outcome = null) { return new ConsumeOperation { AdmissionCode = status, RequestId = id, State = ConsumptionState.Completed, IsFinal = true, Result = new ConsumeResult(id, "", outcome, status), RetryAfter = ((status == ApiStatus.Busy || status == ApiStatus.Throttled) ? 1 : 0) }; } internal static ConsumeOperation Consume(ApiContext context, ConsumeRequest request, Action<ConsumeResult> completed) { if (Instance == null) { return Reject(ApiStatus.FeatureDisabled); } if (!Main) { return Reject(ApiStatus.WrongThread); } if (!Valid(context) || !ApiRules.Normalize(request, out var normalized)) { return Reject(ApiStatus.InvalidRequest); } ApiRuntime instance = Instance; string text = Consumer(context); string text2 = ApiRules.RequestId(text, normalized); if (instance.world == null) { return Reject(ApiStatus.Unavailable, text2); } if (instance.localOperations.TryGetValue(text, out var value)) { if (value.Request.SessionId == normalized.SessionId && value.Request.Sequence == normalized.Sequence) { if (!ApiRules.Same(value.Request, normalized)) { return Reject(ApiStatus.RequestConflict, text2); } if (!value.Handle.IsFinal || value.Handle.Result.Outcome.HasValue) { value.Handle.Subscribe(completed); instance.QueueCallback(value.Handle); return value.Handle; } } if (!value.Handle.IsFinal) { return Reject(ApiStatus.Busy, text2); } } if (normalized.SessionId != instance.session) { return Reject(ApiStatus.Unavailable, text2, ConsumptionOutcome.OutcomeUnknown); } if (!instance.localOperations.ContainsKey(text) && instance.localOperations.Count >= 4096) { return Reject(ApiStatus.CapacityExceeded, text2); } ZDO z; long creator; ApiStatus apiStatus = ApiWorld.Context(context, ZNet.GetUID(), Server, out z, out creator); if (apiStatus != ApiStatus.Ready) { return Reject(apiStatus, text2); } LocalOperation localOperation = new LocalOperation { Context = context, Request = normalized, Handle = new ConsumeOperation { RequestId = text2, AdmissionCode = ApiStatus.Ready, State = ConsumptionState.Pending } }; localOperation.Handle.Subscribe(completed); instance.localOperations[text] = localOperation; if (Server) { instance.ProcessLocal(localOperation); } return localOperation.Handle; } private ApiStatus Admit(ApiContext context, long peer, ConsumeRequest request, out ApiLedger.Entry entry) { entry = null; if (request.SessionId != session) { return ApiStatus.Unavailable; } ApiStatus apiStatus = ApiWorld.Context(context, peer, receipt: true, out var z, out var creator); if (apiStatus != ApiStatus.Ready) { return apiStatus; } string consumer = Consumer(context); ApiStatus apiStatus2 = ledger.Lookup(consumer, request, out entry); if (apiStatus2 != ApiStatus.NotReady) { return apiStatus2; } apiStatus = ApiWorld.Context(context, peer, receipt: false, out z, out creator); if (apiStatus != ApiStatus.Ready) { return apiStatus; } if (!Enabled) { return ApiStatus.FeatureDisabled; } if (!UnloadedNetworks.ApiCatalogReady) { return ApiStatus.NotReady; } if (Transport.Instance.OccupiedOperations + ledger.Entries.Count((ApiLedger.Entry e) => !e.Released) >= 256) { return ApiStatus.Busy; } foreach (ResourceRequirement resource in request.Resources) { if (!Known(resource.Resource)) { return ApiStatus.UnknownItem; } } if (!writes.TryGetValue(peer, out var value)) { if (writes.Count >= 128) { return ApiStatus.CapacityExceeded; } value = (writes[peer] = new ApiBucket(4.0, 4.0, Now)); } if (!value.Take(Now) || !writeRate.Take(Now)) { return ApiStatus.Throttled; } apiStatus = ledger.Admit(consumer, peer, request, Now, out entry); if (apiStatus == ApiStatus.Ready && entry.Work == null) { entry.Work = new ApiPayment(new ApiPaymentWorld(context, entry), Now, request.StartWithin); } return apiStatus; } private void ProcessLocal(LocalOperation op) { ApiLedger.Entry entry; ApiStatus apiStatus = Admit(op.Context, ZNet.GetUID(), op.Request, out entry); UpdateOperation(op, apiStatus, entry?.State ?? ConsumptionState.Pending, (apiStatus == ApiStatus.Ready) ? entry.Result : null); } private void UpdateOperation(LocalOperation local, ApiStatus admission, ConsumptionState state, ConsumeResult result) { ConsumeOperation handle = local.Handle; if (!handle.IsFinal) { handle.AdmissionCode = admission; handle.State = state; if (admission != ApiStatus.Ready) { handle.Complete(new ConsumeResult(handle.RequestId, "", (admission == ApiStatus.Unavailable && local.Request.SessionId != session) ? new ConsumptionOutcome?(ConsumptionOutcome.OutcomeUnknown) : ((ConsumptionOutcome?)null), admission)); } else if (result != null) { handle.Complete(result); } QueueCallback(handle); } } private void NotifyLocal(ApiLedger.Entry entry) { if (localOperations.TryGetValue(entry.Consumer, out var value) && value.Request.Sequence == entry.Request.Sequence) { UpdateOperation(value, ApiStatus.Ready, entry.State, entry.Result); } } private void QueueCallback(ConsumeOperation op) { if (op.IsFinal && !op.Delivered && !op.Queued && op.Handler != null) { op.Queued = true; callbacks.Enqueue(op); } } private void DeliverCallbacks() { int count = callbacks.Count; for (int i = 0; i < count; i++) { ConsumeOperation consumeOperation = callbacks.Dequeue(); consumeOperation.Queued = false; if (!consumeOperation.Delivered) { consumeOperation.Delivered = true; try { consumeOperation.Handler?.Invoke(consumeOperation.Result); } catch (Exception ex) { Diagnostic("callback", ex.Message); } consumeOperation.Handler = null; } } } internal static void Send(long peer, string name, ZPackage p) { ApiRuntime instance = Instance; if (Object.op_Implicit((Object)(object)instance) && peer != 0) { int num = p.Size(); if (num > 32768) { throw new InvalidOperationException("API packet limit"); } if (instance.outgoing.Count >= 4096 || instance.outgoingBytes + num > 4194304) { Diagnostic("backpressure", "Outgoing API queue full; requests retain their IDs for retry."); return; } instance.outgoing.Enqueue(new Outgoing { Peer = peer, Name = name, Data = p, Bytes = num }); instance.outgoingBytes += num; } } private void Register() { string[] array = new string[10] { "hello", "welcome", "read", "snapshot", "consume", "result", "prepare", "grant", "ticket", "release" }; foreach (string text in array) { string n = text; rpc.Register<ZPackage>("RSN_API1_" + n, (Action<long, ZPackage>)delegate(long sender, ZPackage p) { Dispatch(n, sender, p); }); } } private void Dispatch(string name, long sender, ZPackage p) { //IL_03d1: Unknown result type (might be due to invalid IL or missing references) //IL_03d8: Expected O, but got Unknown //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Expected O, but got Unknown try { if (p.Size() > 32768) { return; } p.SetPos(0); switch (name) { case "hello": if (Server) { int num3 = p.ReadInt(); if (num3 == 1 && peers.Count < 128) { peers[sender] = Now; } ZPackage val2 = new ZPackage(); val2.Write(1); val2.Write(session); val2.Write(Enabled); Send(sender, "welcome", val2); } break; case "welcome": { if (sender != Transport.Server || Server) { break; } int num = p.ReadInt(); string text = ApiWire.Text(p, 64); bool flag = p.ReadBool(); if (session != "" && session != text) { foreach (LocalOperation value2 in localOperations.Values) { if (!value2.Handle.IsFinal) { value2.Handle.Complete(new ConsumeResult(value2.Handle.RequestId, "", ConsumptionOutcome.OutcomeUnknown, ApiStatus.Unavailable)); QueueCallback(value2.Handle); } } localReads.Clear(); } session = text; serverVersion = num; serverEnabled = flag; break; } case "read": if (Server && RateRead(sender)) { ReceiveRead(sender, p); } break; case "snapshot": if (!Server && sender == Transport.Server) { ReceiveSnapshot(p); } break; case "consume": if (Server && RateRead(sender, control: true)) { ApiContext apiContext = ApiWire.Context(p); ConsumeRequest consumeRequest = ApiWire.Request(p); ApiLedger.Entry entry; ApiStatus apiStatus = Admit(apiContext, sender, consumeRequest, out entry); ZPackage val = new ZPackage(); ApiWire.Context(val, apiContext); val.Write(consumeRequest.SessionId); val.Write(consumeRequest.Sequence); val.Write((int)apiStatus); val.Write((int)(entry?.State ?? ConsumptionState.Pending)); ApiWire.Result(val, (apiStatus == ApiStatus.Ready) ? entry.Result : null); Send(sender, "result", val); } break; case "result": if (!Server && sender == Transport.Server) { ApiContext c = ApiWire.Context(p); string text2 = ApiWire.Text(p, 64); long num2 = p.ReadLong(); ApiStatus admission = (ApiStatus)p.ReadInt(); ConsumptionState state = (ConsumptionState)p.ReadInt(); ConsumeResult result = ApiWire.Result(p); if (localOperations.TryGetValue(Consumer(c), out var value) && value.Request.SessionId == text2 && value.Request.Sequence == num2) { UpdateOperation(value, admission, state, result); } } break; case "prepare": ApiOwnership.ReceivePrepare(sender, p); break; case "grant": ApiOwnership.ReceiveGrant(sender, p); break; case "ticket": if (Server && RateRead(sender, control: true)) { ApiOwnership.ReceiveTicket(sender, p); } break; case "release": ApiOwnership.ReceiveRelease(sender, p); break; } } catch (Exception ex) { Diagnostic("protocol", name + ": " + ex.Message); } } private bool RateRead(long peer, bool control = false) { if (!HasPeer(peer)) { return false; } Dictionary<long, ApiBucket> dictionary = (control ? controls : queries); if (!dictionary.TryGetValue(peer, out var value)) { if (dictionary.Count >= 128) { return false; } value = (dictionary[peer] = new ApiBucket(control ? 128 : 16, control ? 256 : 32, Now)); } return value.Take(Now); } private void ReceiveRead(long peer, ZPackage p) { //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Expected O, but got Unknown string text = ApiWire.Text(p, 512); long num = p.ReadLong(); ApiContext apiContext = ApiWire.Context(p); ResourceKey? filter = (p.ReadBool() ? new ResourceKey?(ApiWire.Key(p)) : ((ResourceKey?)null)); string key = peer + "/" + Consumer(apiContext); if (!reads.TryGetValue(key, out var value)) { if (reads.Count >= 128) { return; } value = (reads[key] = new ReadInterest { Context = apiContext, Peer = peer }); } value.Used = Now; ResourceSnapshot resourceSnapshot = Snapshot(apiContext, peer, filter); int num2 = resourceSnapshot.Resources.Count; int num3 = Math.Max(1, (num2 + 63) / 64); if (num2 > 4096) { resourceSnapshot = new ResourceSnapshot(ApiStatus.SnapshotLimit, session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, resourceSnapshot.Recovery); num3 = 1; num2 = 0; } for (int i = 0; i < num3; i++) { ZPackage val = new ZPackage(); val.Write(text); val.Write(num); val.Write(i); val.Write(num3); val.Write((int)resourceSnapshot.Status); val.Write(resourceSnapshot.SessionId); val.Write(resourceSnapshot.NetworkId ?? ""); val.Write(resourceSnapshot.Revision); val.Write(resourceSnapshot.ObservedAt); val.Write(resourceSnapshot.Age); val.Write(resourceSnapshot.IsStale); val.Write(resourceSnapshot.IsComplete); val.Write(resourceSnapshot.UnknownSources); ApiWire.Recovery(val, resourceSnapshot.Recovery); int num4 = i * 64; int num5 = Math.Min(64, num2 - num4); val.Write(num5); for (int j = 0; j < num5; j++) { ResourceAmount resourceAmount = resourceSnapshot.Resources[num4 + j]; ApiWire.Key(val, resourceAmount.Resource); val.Write(resourceAmount.ObservedAmount); } Send(peer, "snapshot", val); } } private void ReceiveSnapshot(ZPackage p) { string key = ApiWire.Text(p, 512); long num = p.ReadLong(); int num2 = p.ReadInt(); int num3 = p.ReadInt(); if (!localReads.TryGetValue(key, out var value) || num < value.ReceivedSerial || num > value.Serial || num3 < 1 || num3 > 64 || num2 < 0 || num2 >= num3) { return; } if (num2 == 0) { value.ReceivedSerial = num; value.NextPage = 0; value.Expected = num3; value.Rows.Clear(); value.Bytes = 0; } if (num != value.ReceivedSerial || num2 != value.NextPage || num3 != value.Expected) { return; } ApiStatus status = (ApiStatus)p.ReadInt(); string text = ApiWire.Text(p, 64); string text2 = p.ReadString(); if (text != session || text2.Length > 256) { return; } long revision = p.ReadLong(); double observedAt = p.ReadDouble(); double age = p.ReadDouble(); bool stale = p.ReadBool(); bool complete = p.ReadBool(); int unknown = p.ReadInt(); ConsumerRecovery recovery = ApiWire.Recovery(p); int num4 = p.ReadInt(); if (num4 < 0 || num4 > 64) { return; } for (int i = 0; i < num4; i++) { value.Rows.Add(new ResourceAmount(ApiWire.Key(p), p.ReadLong())); } value.Bytes += p.Size(); if (value.Bytes > 1048576) { value.Rows.Clear(); return; } value.NextPage++; if (value.NextPage == num3) { if (localReads.Values.Sum((LocalRead r) => r.Bytes) > 16777216) { value.Rows.Clear(); value.Snapshot = new ResourceSnapshot(ApiStatus.SnapshotLimit, session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, recovery); value.Bytes = 0; } else { value.Snapshot = new ResourceSnapshot(status, text, text2, revision, observedAt, age, stale, complete, unknown, Array.AsReadOnly(value.Rows.ToArray()), recovery); } value.ReceivedAt = Now; } } } internal static class ApiWire { internal static string Text(ZPackage p, int limit = 128) { string text = p.ReadString(); if (!ApiRules.Text(text, limit)) { throw new InvalidOperationException("Invalid API text"); } return text; } internal static ApiContext Context(ZPackage p) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return new ApiContext(p.ReadZDOID(), Text(p)); } internal static void Context(ZPackage p, ApiContext c) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) p.Write(c.ConsumerId); p.Write(c.ModId); } internal static ResourceKey Key(ZPackage p) { return new ResourceKey(Text(p), p.ReadInt()); } internal static void Key(ZPackage p, ResourceKey k) { p.Write(k.PrefabName); p.Write(k.Quality); } internal static ConsumeRequest Request(ZPackage p) { string sessionId = Text(p, 64); long sequence = p.ReadLong(); string cycleRevision = Text(p); ResourceMatchPolicy matchPolicy = (ResourceMatchPolicy)p.ReadInt(); double startWithin = p.ReadDouble(); int num = p.ReadInt(); if (num < 1 || num > 32) { throw new InvalidOperationException("API requirement limit"); } List<ResourceRequirement> list = new List<ResourceRequirement>(); for (int i = 0; i < num; i++) { list.Add(new ResourceRequirement(Key(p), p.ReadInt())); } ConsumeRequest request = new ConsumeRequest(sessionId, sequence, cycleRevision, list, matchPolicy, startWithin); if (!ApiRules.Normalize(request, out var normalized)) { throw new InvalidOperationException("Invalid API request"); } return normalized; } internal static void Request(ZPackage p, ConsumeRequest r) { p.Write(r.SessionId); p.Write(r.Sequence); p.Write(r.CycleRevision); p.Write((int)r.MatchPolicy); p.Write(r.StartWithin); p.Write(r.Resources.Count); foreach (ResourceRequirement resource in r.Resources) { Key(p, resource.Resource); p.Write(resource.Amount); } } internal static void Result(ZPackage p, ConsumeResult r) { p.Write(r != null); if (r == null) { return; } p.Write(r.RequestId ?? ""); p.Write(r.NetworkId ?? ""); p.Write((int)(r.Outcome.HasValue ? r.Outcome.Value : ((ConsumptionOutcome)(-1)))); p.Write((int)r.Reason); p.Write(r.Resources.Count); foreach (ResourceRequirement resource in r.Resources) { Key(p, resource.Resource); p.Write(resource.Amount); } } internal static ConsumeResult Result(ZPackage p) { if (!p.ReadBool()) { return null; } string text = p.ReadString(); string text2 = p.ReadString(); if (text.Length > 512 || text2.Length > 256) { throw new InvalidOperationException("API receipt limit"); } int num = p.ReadInt(); ApiStatus reason = (ApiStatus)p.ReadInt(); int num2 = p.ReadInt(); if (num < -1 || num > 2 || num2 < 0 || num2 > 32) { throw new InvalidOperationException("Invalid API receipt"); } List<ResourceRequirement> list = new List<ResourceRequirement>(); for (int i = 0; i < num2; i++) { list.Add(new ResourceRequirement(Key(p), p.ReadInt())); } return new ConsumeResult(text, text2, (num < 0) ? ((ConsumptionOutcome?)null) : new ConsumptionOutcome?((ConsumptionOutcome)num), reason, list); } internal static void Recovery(ZPackage p, ConsumerRecovery r) { p.Write(r != null); if (r != null) { p.Write(r.OwnerEpoch); p.Write(r.Request != null); if (r.Request != null) { Request(p, r.Request); } Result(p, r.Result); p.Write((int)r.State); } } internal static ConsumerRecovery Recovery(ZPackage p) { if (!p.ReadBool()) { return null; } long epoch = p.ReadLong(); ConsumeRequest request = (p.ReadBool() ? Request(p) : null); ConsumeResult result = Result(p); return new ConsumerRecovery(epoch, request, result, (ConsumptionState)p.ReadInt()); } } internal static class UnloadedNetworks { private static readonly HashSet<int> chestTypes = new HashSet<int>(); private static readonly HashSet<int> nodeTypes = new HashSet<int>(new string[3] { "RSN_NetworkCore", "RSN_RunicRelay", "RSN_RunicGateway" }.Select((string s) => StringExtensionMethods.GetStableHashCode(s))); private static readonly Dictionary<ZDOID, ZDO> nodes = new Dictionary<ZDOID, ZDO>(); private static readonly Dictionary<ZDOID, ZDO> chests = new Dictionary<ZDOID, ZDO>(); private static readonly Dictionary<ZDOID, ZDO> wards = new Dictionary<ZDOID, ZDO>(); private static readonly Dictionary<Vector2s, HashSet<ZDOID>> chestSectors = new Dictionary<Vector2s, HashSet<ZDOID>>(); private static readonly Dictionary<ZDOID, Vector2s> positions = new Dictionary<ZDOID, Vector2s>(); private static readonly Dictionary<ZDOID, (Vector3, string, long, int)> nodeState = new Dictionary<ZDOID, (Vector3, string, long, int)>(); private static readonly Dictionary<ZDOID, long> creators = new Dictionary<ZDOID, long>(); private static readonly Dictionary<ZDOID, long> owners = new Dictionary<ZDOID, long>(); private static readonly Dictionary<(Vector3, long), bool> wardAccess = new Dictionary<(Vector3, long), bool>(); private static readonly Dictionary<ZDOID, GameObject> replicas = new Dictionary<ZDOID, GameObject>(); private static readonly Queue<ZDOID> queue = new Queue<ZDOID>(); private static readonly HashSet<ZDOID> queued = new HashSet<ZDOID>(); private static readonly HashSet<ZDOID> requestedChests = new HashSet<ZDOID>(); private static readonly HashSet<ZDOID> ownerPending = new HashSet<ZDOID>(); private static readonly Dictionary<string, long> requestedNetworks = new Dictionary<string, long>(); private static readonly HashSet<int> wardTypes = new HashSet<int>(); private static ZDOMan world; private static bool enabled; private static bool bootstrapped; private static long structure; private static long nodesRequested = -1L; private static float demandUntil; private static Harmony patches; internal static bool ApiCatalogReady => Enabled && Authority && bootstrapped; internal static IEnumerable<ZDO> ApiNodes => nodes.Values; internal static IEnumerable<ZDO> ApiChests => chests.Values; internal static bool Installed => patches != null; internal static bool Requested => enabled && world == ZDOMan.instance && Object.op_Implicit((Object)(object)ZNet.instance); internal static bool Authority => Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer(); internal static bool Enabled => Requested && (Authority || UnloadedMultiplayer.ServerEnabled); internal static long CatalogRevision => structure; internal static bool Demand => Enabled && Time.unscaledTime < demandUntil; internal static IEnumerable<NetworkMember> Members => from g in replicas.Values where Object.op_Implicit((Object)(object)g) select g.GetComponent<NetworkMember>() into m where Object.op_Implicit((Object)(object)m) && m.Valid se