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 YggdrasilStorage v0.5.4
BepInEx/plugins/YggdrasilStorage/YggdrasilStorage.dll
Decompiled 2 months ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Security.Cryptography; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using UnityEngine; using YggdrasilStorage.Backend; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyTitle("YggdrasilStorage")] [assembly: AssemblyDescription("Independent Valheim storage and crafting prototype, inspired by BlushieMagic's Terraria Magic Storage.")] [assembly: AssemblyFileVersion("0.5.4.0")] [assembly: CompilationRelaxations(8)] [assembly: AssemblyVersion("0.5.4.0")] namespace YggdrasilStorage.Backend { public static class BulkDeposit { public static Dictionary<string, int> Parse(string manifest) { if (string.IsNullOrEmpty(manifest) || manifest.Length > 1800) { throw new ArgumentException("Invalid deposit manifest."); } string[] array = manifest.Split(new char[1] { ';' }); if (array.Length > 20) { throw new ArgumentException("Too many deposit entries."); } Dictionary<string, int> dictionary = new Dictionary<string, int>(); string[] array2 = array; foreach (string text in array2) { string[] array3 = text.Split(new char[1] { '=' }); if (array3.Length != 2 || array3[0].Length != 64 || array3[0].Any((char c) => !Uri.IsHexDigit(c)) || !int.TryParse(array3[1], out var result) || result < 1 || result > 10000 || dictionary.ContainsKey(array3[0])) { throw new ArgumentException("Invalid deposit entry."); } dictionary.Add(array3[0], result); } return dictionary; } public static MovePlan Plan(IEnumerable<StoreState> stores, string source, ISet<string> destinations, IDictionary<string, int> quantities) { List<StoreState> list = stores.Select((StoreState s) => s.Copy()).ToList(); List<StoreState> list2 = list.Select((StoreState s) => s.Copy()).ToList(); int num = 0; foreach (KeyValuePair<string, int> pair in quantities) { List<StackState> list3 = list2.Single((StoreState s) => s.Id == source).Items.Where(delegate(StackState i) { string key = i.Key; KeyValuePair<string, int> keyValuePair4 = pair; return key == keyValuePair4.Key; }).ToList(); long num2 = ((IEnumerable<StackState>)list3).Sum((Func<StackState, long>)((StackState i) => i.Count)); KeyValuePair<string, int> keyValuePair = pair; if (num2 < keyValuePair.Value) { throw new InvalidOperationException("Staged deposit changed; no transfer performed."); } int limit = list3[0].Limit; long val = list2.Where((StoreState s) => destinations.Contains(s.Id)).Sum((StoreState s) => (long)(s.Capacity - s.Items.Count) * (long)limit + s.Items.Where(delegate(StackState i) { string key = i.Key; KeyValuePair<string, int> keyValuePair4 = pair; return key == keyValuePair4.Key; }).Sum((StackState i) => (long)i.Limit - (long)i.Count)); KeyValuePair<string, int> keyValuePair2 = pair; int num3 = (int)Math.Min(keyValuePair2.Value, val); if (num3 != 0) { List<StoreState> stores2 = list2; HashSet<string> sources = new HashSet<string> { source }; ISet<string> destinations2 = destinations; KeyValuePair<string, int> keyValuePair3 = pair; list2 = Planner.Move(stores2, sources, destinations2, keyValuePair3.Key, num3).After; num += num3; } } MovePlan movePlan = new MovePlan(); movePlan.Before = list; movePlan.After = list2; movePlan.Moved = num; return movePlan; } } } namespace YggdrasilStorage { public static class Compatibility { private static readonly SortedDictionary<string, Func<ItemData, string>> identities = new SortedDictionary<string, Func<ItemData, string>>(StringComparer.Ordinal); private static readonly Dictionary<string, Func<Container, long, bool>> access = new Dictionary<string, Func<Container, long, bool>>(); public static void RegisterItemIdentity(string modId, Func<ItemData, string> provider) { if (string.IsNullOrEmpty(modId) || provider == null) { throw new ArgumentException("A mod ID and provider are required."); } if (Object.op_Implicit((Object)(object)ZNet.instance)) { throw new InvalidOperationException("Register integrations before entering a world."); } identities.Add(modId, provider); } public static void RegisterContainerAccess(string modId, Func<Container, long, bool> allowed) { if (string.IsNullOrEmpty(modId) || allowed == null) { throw new ArgumentException("A mod ID and provider are required."); } if (Object.op_Implicit((Object)(object)ZNet.instance)) { throw new InvalidOperationException("Register integrations before entering a world."); } access.Add(modId, allowed); } internal static IDictionary<string, string> Identity(ItemData item) { return identities.ToDictionary((KeyValuePair<string, Func<ItemData, string>> x) => x.Key, (KeyValuePair<string, Func<ItemData, string>> x) => x.Value(item)); } internal static bool Allowed(Container container, long playerId) { return access.Values.All((Func<Container, long, bool> check) => check(container, playerId)); } } internal sealed class RecipeRow { internal string Id; internal string Name; internal string Station; internal string Requirements; internal string Reason; internal int Amount; internal bool Ready; } internal static class Crafting { private static readonly FieldInfo StationsField = AccessTools.Field(typeof(CraftingStation), "m_allStations"); internal static List<CraftingStation> Stations(Container core, long playerId) { return ((List<CraftingStation>)StationsField.GetValue(null)).Where(delegate(CraftingStation s) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)s) && ((Behaviour)s).isActiveAndEnabled && s.m_hasCraftTab && Object.op_Implicit((Object)(object)((Component)s).GetComponent<ZNetView>()) && ((Component)s).GetComponent<ZNetView>().IsValid()) { Vector3 val = ((Component)s).transform.position - ((Component)core).transform.position; if (((Vector3)(ref val)).sqrMagnitude <= Plugin.Radius.Value * Plugin.Radius.Value) { return Registry.WardAccess(((Component)s).transform.position, playerId); } } return false; }).ToList(); } private static string Unsupported(Recipe recipe) { if (!Object.op_Implicit((Object)(object)recipe) || !recipe.m_enabled || !Object.op_Implicit((Object)(object)recipe.m_item)) { return "Recipe unavailable"; } if (recipe.m_noCraftOnlyUpgrade || (Object.op_Implicit((Object)(object)recipe.GetRequiredStation(1)) && recipe.GetRequiredStation(1).m_upgrader)) { return "Use the station for upgrades"; } if (recipe.m_requireOnlyOneIngredient) { return "Use the station for alternative-ingredient recipes"; } if (!string.IsNullOrEmpty(recipe.m_item.m_itemData.m_shared.m_dlc)) { return "Use the station for DLC recipes"; } if (recipe.m_resources == null || recipe.m_amount < 1 || recipe.m_amount > 10000) { return "Unsupported recipe"; } return ""; } private static Dictionary<CraftingStation, int> UsableStations(Container core, Player player) { return (from s in Stations(core, player.GetPlayerID()) where s.CheckUsable(player, false) select s).ToDictionary((CraftingStation s) => s, (CraftingStation s) => s.GetLevel(false)); } private static CraftingStation ResolveStation(Recipe recipe, Dictionary<CraftingStation, int> stations) { CraftingStation required = recipe.GetRequiredStation(1); if (!Object.op_Implicit((Object)(object)required)) { return null; } return (from s in stations where s.Key.m_name == required.m_name && s.Value >= recipe.GetRequiredStationLevel(1) orderby s.Value descending select s.Key).FirstOrDefault(); } private static Dictionary<string, int> Requirements(Recipe recipe) { Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal); if (recipe.m_resources == null) { return dictionary; } Requirement[] resources = recipe.m_resources; foreach (Requirement val in resources) { if (val != null) { int amount = val.GetAmount(1); if (Object.op_Implicit((Object)(object)val.m_resItem) && amount > 0) { string name = ((Object)((Component)val.m_resItem).gameObject).name; dictionary.TryGetValue(name, out var value); dictionary[name] = checked(value + amount); } } } return dictionary; } internal static List<RecipeRow> Rows(Container core, Player player, IEnumerable<Container> containers) { Dictionary<CraftingStation, int> stations = UsableStations(core, player); Dictionary<string, long> dictionary = (from i in containers.Where((Container c) => !c.IsInUse()).SelectMany((Container c) => c.GetInventory().GetAllItems()) where Object.op_Implicit((Object)(object)i.m_dropPrefab) && !i.m_equipped && !SmartCraftIntegration.IsLocked(i) && !EpicLootIntegration.IsMagic(i) group i by ((Object)i.m_dropPrefab).name).ToDictionary((IGrouping<string, ItemData> g) => g.Key, (IGrouping<string, ItemData> g) => ((IEnumerable<ItemData>)g).Sum((Func<ItemData, long>)((ItemData i) => i.m_stack))); List<RecipeRow> list = new List<RecipeRow>(); foreach (Recipe item in from r in ObjectDB.instance.m_recipes where Object.op_Implicit((Object)(object)r) && r.m_enabled && Object.op_Implicit((Object)(object)r.m_item) group r by ((Object)r).name into g where g.Count() == 1 select g.First()) { if (((Object)item).name.Length > 128) { continue; } string text = Unsupported(item); if (text.Length != 0) { continue; } CraftingStation requiredStation = item.GetRequiredStation(1); CraftingStation val = ResolveStation(item, stations); if (Object.op_Implicit((Object)(object)requiredStation) && !Object.op_Implicit((Object)(object)val)) { continue; } Dictionary<string, int> dictionary2 = Requirements(item); List<string> list2 = new List<string>(); foreach (KeyValuePair<string, int> item2 in dictionary2) { dictionary.TryGetValue(item2.Key, out var value); GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item2.Key); string text2 = ((Object.op_Implicit((Object)(object)itemPrefab) && Object.op_Implicit((Object)(object)itemPrefab.GetComponent<ItemDrop>())) ? itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_name : item2.Key); list2.Add(text2 + " " + item2.Value + " (" + value + " stored)"); if (value < item2.Value && text.Length == 0) { text = "Missing materials"; } } list.Add(new RecipeRow { Id = ((Object)item).name, Name = item.m_item.m_itemData.m_shared.m_name, Station = (Object.op_Implicit((Object)(object)requiredStation) ? (requiredStation.m_name + " level " + item.GetRequiredStationLevel(1)) : "Hand crafting"), Requirements = string.Join(", ", list2.ToArray()), Amount = item.m_amount, Ready = (text.Length == 0), Reason = text }); if (list.Count >= 2048) { break; } } return list; } internal static MovePlan Plan(List<StoreState> stores, Dictionary<string, ItemData> prototypes, Container core, Player player, string recipeId, string outputStore = null) { List<Recipe> list = ObjectDB.instance.m_recipes.Where((Recipe r) => Object.op_Implicit((Object)(object)r) && ((Object)r).name == recipeId).ToList(); if (list.Count != 1) { throw new InvalidOperationException("Recipe missing or ambiguous."); } Recipe val = list[0]; string text = Unsupported(val); if (text.Length > 0) { throw new InvalidOperationException(text); } if ((Object)(object)player == (Object)(object)Player.m_localPlayer && !player.IsRecipeKnown(val.m_item.m_itemData.m_shared.m_name)) { throw new InvalidOperationException("Recipe not yet known."); } CraftingStation val2 = ResolveStation(val, UsableStations(core, player)); if (Object.op_Implicit((Object)(object)val.GetRequiredStation(1)) && !Object.op_Implicit((Object)(object)val2)) { throw new InvalidOperationException("Required station is missing, inaccessible, under-level, or needs shelter/fire."); } Dictionary<string, long> dictionary = (from x in stores.Where((StoreState x) => outputStore == null || x.Id != outputStore).SelectMany((StoreState x) => x.Items) group x by x.Key).ToDictionary((IGrouping<string, StackState> g) => g.Key, (IGrouping<string, StackState> g) => ((IEnumerable<StackState>)g).Sum((Func<StackState, long>)((StackState x) => x.Count))); Dictionary<string, int> dictionary2 = new Dictionary<string, int>(); bool flag = false; foreach (KeyValuePair<string, int> item in Requirements(val)) { int num = item.Value; foreach (KeyValuePair<string, ItemData> item2 in prototypes.OrderBy<KeyValuePair<string, ItemData>, string>((KeyValuePair<string, ItemData> x) => x.Key, StringComparer.Ordinal)) { ItemData value = item2.Value; if (item2.Key.StartsWith("protected/", StringComparison.Ordinal) || ((Object)value.m_dropPrefab).name != item.Key || value.m_equipped || SmartCraftIntegration.IsLocked(value) || EpicLootIntegration.IsMagic(value)) { continue; } dictionary.TryGetValue(item2.Key, out var value2); int num2 = (int)Math.Min(num, value2); if (num2 > 0) { dictionary2[item2.Key] = num2; dictionary[item2.Key] -= num2; num -= num2; flag |= value.m_cheated; if (num == 0) { break; } } } if (num != 0) { throw new InvalidOperationException("Not enough " + item.Key + "; nothing consumed."); } } if (Object.op_Implicit((Object)(object)val2)) { flag |= ((Component)val2).GetComponent<ZNetView>().GetZDO().GetBool(ZDOVars.s_cheated, false); } ItemData val3 = val.m_item.m_itemData.Clone(); val3.m_dropPrefab = ((Component)val.m_item).gameObject; ItemData val4 = Items.Clone(val3); val4.m_dropPrefab = ((Component)val.m_item).gameObject; val4.m_quality = 1; val4.m_variant = 0; val4.m_crafterID = player.GetPlayerID(); val4.m_crafterName = player.GetPlayerName(); val4.m_durability = val4.GetMaxDurability(); val4.m_equipped = false; val4.m_cheated = flag; string key = Items.Key(val4); prototypes[key] = val4; return Planner.Craft(stores, dictionary2, outputStore ?? Registry.Id(core), new StackState { Key = key, Count = val.m_amount, Limit = val4.m_shared.m_maxStackSize }, (outputStore == null) ? null : new HashSet<string>(from x in stores where x.Id != outputStore select x.Id)); } } } namespace YggdrasilStorage.Backend { public sealed class DeliveryReceipts { private readonly Dictionary<string, Tuple<long, bool>> entries = new Dictionary<string, Tuple<long, bool>>(); public long LastSequence { get; private set; } public bool TryGet(string ticket, long sequence, out bool delivered) { delivered = false; if (!entries.TryGetValue(ticket, out var value)) { return false; } if (value.Item1 != sequence) { throw new InvalidOperationException("Delivery ticket was reused for a different request."); } delivered = value.Item2; return true; } public void Record(string ticket, long sequence, bool delivered) { if (sequence <= LastSequence || entries.ContainsKey(ticket)) { throw new InvalidOperationException("Delivery already decided."); } entries.Add(ticket, Tuple.Create(sequence, delivered)); LastSequence = sequence; if (entries.Count > 256) { string[] array = (from x in entries where x.Value.Item1 < sequence - 128 select x.Key).ToArray(); foreach (string key in array) { entries.Remove(key); } } } } } namespace YggdrasilStorage { internal sealed class PlayerSnapshot { internal int Width; internal int Height; internal byte[] Bytes; internal Inventory Inventory; private string identity; internal HashSet<int> Protected = new HashSet<int>(); private static string Identity(Inventory inv) { return string.Join(";", (from i in inv.GetAllItems() orderby i.m_gridPos.y, i.m_gridPos.x select i.m_gridPos.x + "," + i.m_gridPos.y + "=" + Items.Key(i)).ToArray()); } internal static PlayerSnapshot Capture(byte action) { Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory(); PlayerSnapshot playerSnapshot = new PlayerSnapshot(); playerSnapshot.Width = inventory.GetWidth(); playerSnapshot.Height = inventory.GetHeight(); playerSnapshot.Inventory = inventory; PlayerSnapshot playerSnapshot2 = playerSnapshot; Items.Read(inventory, "player", null, Items.Key); foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem.m_equipped || SmartCraftIntegration.IsLocked(allItem) || (action == 6 && allItem.m_gridPos.y == 0)) { playerSnapshot2.Protected.Add(allItem.m_gridPos.y * playerSnapshot2.Width + allItem.m_gridPos.x); } } playerSnapshot2.identity = Identity(inventory); playerSnapshot2.Bytes = Items.Bytes(inventory); return playerSnapshot2; } internal void Write(ZPackage p) { p.Write(Width); p.Write(Height); p.Write(Encode(Inventory)); p.Write(Inventory.GetAllItems().Count); foreach (ItemData allItem in Inventory.GetAllItems()) { int num = allItem.m_gridPos.y * Width + allItem.m_gridPos.x; p.Write(num); p.Write(Items.Key(allItem)); p.Write(Protected.Contains(num)); } } internal static PlayerSnapshot Read(ZPackage p) { PlayerSnapshot playerSnapshot = new PlayerSnapshot(); playerSnapshot.Width = p.ReadInt(); playerSnapshot.Height = p.ReadInt(); playerSnapshot.Bytes = p.ReadByteArray(); PlayerSnapshot playerSnapshot2 = playerSnapshot; playerSnapshot2.Inventory = Decode(playerSnapshot2.Bytes, playerSnapshot2.Width, playerSnapshot2.Height); playerSnapshot2.Bytes = Items.Bytes(playerSnapshot2.Inventory); int num = p.ReadInt(); StoreState storeState = Items.Read(playerSnapshot2.Inventory, "player", null, Items.Key); if (num != storeState.Items.Count) { throw new InvalidOperationException("Inventory serialization changed an item count."); } HashSet<int> hashSet = new HashSet<int>(); for (int i = 0; i < num; i++) { int slot = p.ReadInt(); string key = p.ReadString(); bool flag = p.ReadBool(); if (!hashSet.Add(slot) || !storeState.Items.Any((StackState stackState) => stackState.Slot == slot && stackState.Key == key)) { throw new InvalidOperationException("An item mod did not preserve its identity during synchronization. No items moved."); } if (flag) { playerSnapshot2.Protected.Add(slot); } } return playerSnapshot2; } internal static byte[] Encode(Inventory inv) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown Items.Read(inv, "snapshot", null, Items.Key); ZPackage val = new ZPackage(); val.Write(Items.Bytes(inv)); val.Write(inv.GetAllItems().Count); foreach (ItemData allItem in inv.GetAllItems()) { val.Write(allItem.m_gridPos.x); val.Write(allItem.m_gridPos.y); val.Write(allItem.m_durability); val.Write(allItem.m_crafterName ?? ""); val.Write(Items.Key(allItem)); } byte[] array = val.GetArray(); if (array.Length > 786432) { throw new InvalidOperationException("Inventory snapshot is too large."); } return array; } internal static Inventory Decode(byte[] payload, int width, int height) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown if (width < 1 || height < 1 || width > 128 || height > 128 || width * height > 4096 || payload.Length > 786432) { throw new InvalidOperationException("Invalid inventory snapshot size."); } ZPackage val = new ZPackage(payload); byte[] array = val.ReadByteArray(); if (array.Length > 262144) { throw new InvalidOperationException("Inventory save is too large."); } Inventory val2 = new Inventory("Yggdrasil transfer", (Sprite)null, width, height); val2.Load(new ZPackage(array)); int num = val.ReadInt(); if (num != val2.GetAllItems().Count) { throw new InvalidOperationException("Inventory synchronization changed an item count."); } HashSet<int> hashSet = new HashSet<int>(); for (int i = 0; i < num; i++) { int x = val.ReadInt(); int y = val.ReadInt(); float num2 = val.ReadSingle(); string crafterName = val.ReadString(); string text = val.ReadString(); if (x < 0 || y < 0 || x >= width || y >= height || !hashSet.Add(y * width + x) || float.IsNaN(num2) || float.IsInfinity(num2)) { throw new InvalidOperationException("Invalid item snapshot."); } ItemData val3 = ((IEnumerable<ItemData>)val2.GetAllItems()).SingleOrDefault((Func<ItemData, bool>)((ItemData val4) => val4.m_gridPos.x == x && val4.m_gridPos.y == y)); if (val3 == null) { throw new InvalidOperationException("Item position changed during synchronization."); } val3.m_durability = num2; val3.m_crafterName = crafterName; if (Items.Key(val3) != text) { throw new InvalidOperationException("Item metadata changed during synchronization: " + ((Object)val3.m_dropPrefab).name + ". No items moved."); } } Items.Read(val2, "snapshot", null, Items.Key); if (!Items.Bytes(val2).SequenceEqual(array)) { throw new InvalidOperationException("Item save data changed during synchronization. No transfer performed."); } return val2; } internal bool Matches(byte action) { PlayerSnapshot playerSnapshot = Capture(action); if (identity == playerSnapshot.identity && Width == playerSnapshot.Width && Height == playerSnapshot.Height && Bytes.SequenceEqual(playerSnapshot.Bytes)) { return Protected.SetEquals(playerSnapshot.Protected); } return false; } } internal static class DirectTransfers { internal sealed class Plan { internal List<Container> Chests; internal Player Player; internal PlayerSnapshot Snapshot; internal Dictionary<string, byte[]> Before; internal Dictionary<string, byte[]> After; internal Dictionary<string, List<ItemData>> Items; internal Dictionary<string, List<ItemData>> Originals; internal Dictionary<ItemData, int> Counts; internal byte[] PlayerAfter; internal byte[] PlayerDelivery; internal int Moved; internal string Journal; internal bool Committed; internal bool Resolved; } internal static Plan Prepare(List<Container> chests, Container core, Player player, PlayerSnapshot snapshot, byte action, string key, int amount) { //IL_043d: Unknown result type (might be due to invalid IL or missing references) //IL_0444: Expected O, but got Unknown Transactions.DemandWritable(); foreach (Container chest in chests) { Transactions.Demand(chest, player.GetPlayerID()); } Dictionary<string, ItemData> prototypes = new Dictionary<string, ItemData>(); List<StoreState> list = chests.Select((Container c) => Items.Read(c, prototypes)).ToList(); HashSet<string> hashSet = new HashSet<string>(list.Select((StoreState s) => s.Id)); StoreState storeState = Items.Read(snapshot.Inventory, "player", prototypes, (ItemData i) => (((action == 2 || action == 6) && snapshot.Protected.Contains(i.m_gridPos.y * snapshot.Width + i.m_gridPos.x)) ? "protected/" : "") + Items.Key(i)); list.Add(storeState); MovePlan movePlan; if (action == 6) { movePlan = Planner.QuickStack(list, "player", hashSet, matchingOnly: false, new HashSet<string>(from i in storeState.Items where !i.Key.StartsWith("protected/", StringComparison.Ordinal) select i.Key)); } else if (action == 7) { Dictionary<string, int> dictionary = BulkDeposit.Parse(key); foreach (ItemData item in chests.SelectMany((Container c) => c.GetInventory().GetAllItems())) { if (dictionary.ContainsKey(Items.Key(item)) && !Items.MaterialEligible(item)) { throw new InvalidOperationException("A required material is locked or special. Refresh and retry."); } } movePlan = Planner.BatchMove(list, hashSet, new HashSet<string> { "player" }, dictionary); } else if (action == 5) { movePlan = Crafting.Plan(list, prototypes, core, player, key, "player"); } else { if (action != 1 && action != 2) { throw new InvalidOperationException("Unsupported direct transfer action."); } movePlan = Planner.Move(list, (action == 2) ? new HashSet<string> { "player" } : hashSet, (action == 2) ? hashSet : new HashSet<string> { "player" }, key, amount); } Plan plan = new Plan { Chests = chests, Player = player, Snapshot = snapshot, Moved = movePlan.Moved, Before = chests.ToDictionary(Registry.Id, (Container c) => Items.Bytes(c.GetInventory())), After = new Dictionary<string, byte[]>(), Items = new Dictionary<string, List<ItemData>>() }; foreach (StoreState state in movePlan.After) { Inventory val = ((state.Id == "player") ? snapshot.Inventory : chests.Single((Container c) => Registry.Id(c) == state.Id).GetInventory()); List<ItemData> source = Items.Materialize(state, val.GetWidth(), prototypes); Dictionary<Vector2i, ItemData> originals = val.GetAllItems().ToDictionary((ItemData i) => i.m_gridPos); source = ((IEnumerable<ItemData>)source).Select((Func<ItemData, ItemData>)delegate(ItemData i) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) if (!originals.TryGetValue(i.m_gridPos, out var value) || Items.Key(value) != Items.Key(i)) { return i; } ItemData val3 = Items.Clone(value); val3.m_stack = i.m_stack; return val3; }).ToList(); plan.Items.Add(state.Id, source); Inventory val2 = new Inventory("Yggdrasil plan", (Sprite)null, val.GetWidth(), val.GetHeight()); val2.GetAllItems().AddRange(source); byte[] array = Items.Bytes(val2); if (array.Length > 262144) { throw new InvalidOperationException("Inventory too large for a safe transfer."); } if (state.Id == "player") { plan.PlayerAfter = array; plan.PlayerDelivery = PlayerSnapshot.Encode(val2); } else { plan.After.Add(state.Id, array); } } plan.Chests = chests.Where((Container c) => !plan.Before[Registry.Id(c)].SequenceEqual(plan.After[Registry.Id(c)])).ToList(); return plan; } internal static void Commit(Plan plan, bool local, string ticket) { Transactions.DemandWritable(); foreach (Container chest in plan.Chests) { Transactions.Demand(chest, plan.Player.GetPlayerID()); if (!Items.Bytes(chest.GetInventory()).SequenceEqual(plan.Before[Registry.Id(chest)])) { throw new InvalidOperationException("Chest changed; no transfer performed."); } } if (local && !Items.Bytes(((Humanoid)plan.Player).GetInventory()).SequenceEqual(plan.Snapshot.Bytes)) { throw new InvalidOperationException("Your inventory changed. Retry the transfer."); } plan.Originals = plan.Chests.ToDictionary(Registry.Id, (Container c) => c.GetInventory().GetAllItems().ToList()); if (local) { plan.Originals.Add("player", ((Humanoid)plan.Player).GetInventory().GetAllItems().ToList()); } plan.Counts = plan.Originals.Values.SelectMany((List<ItemData> i) => i).ToDictionary((ItemData i) => i, (ItemData i) => i.m_stack); plan.Journal = WriteJournal(ticket, plan.Player.GetPlayerID(), plan.Snapshot.Bytes, plan.PlayerAfter, plan.Chests, plan.Before, plan.After); Transactions.Writing = true; try { foreach (Container chest2 in plan.Chests) { Transactions.Replace(chest2.GetInventory(), plan.Items[Registry.Id(chest2)]); } if (local) { Transactions.Replace(((Humanoid)plan.Player).GetInventory(), plan.Items["player"]); } foreach (Container chest3 in plan.Chests) { if (!Items.Bytes(chest3.GetInventory()).SequenceEqual(plan.After[Registry.Id(chest3)])) { throw new InvalidOperationException("A mod changed a chest during transfer."); } Registry.Save.Invoke(chest3, null); } if (local && !Items.Bytes(((Humanoid)plan.Player).GetInventory()).SequenceEqual(plan.PlayerAfter)) { throw new InvalidOperationException("A mod changed the player inventory during transfer."); } plan.Committed = true; if (local) { Resolve(plan, delivered: true); } } catch { if (!plan.Resolved) { Restore(plan, local, requireAfter: false); } throw; } finally { Transactions.Writing = false; } if (local) { NotifyPlayer(plan.Player); } } internal static void Resolve(Plan plan, bool delivered) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) if (plan.Resolved) { return; } if (!delivered) { Restore(plan, local: false, requireAfter: true); return; } try { File.Move(plan.Journal, Path.ChangeExtension(plan.Journal, ".committed")); plan.Resolved = true; } catch { Transactions.Quarantine(plan.Chests); throw; } foreach (Container chest in plan.Chests) { try { Transactions.Notify(chest.GetInventory()); ZDOMan.instance.ForceSendZDO(((Component)chest).GetComponent<ZNetView>().GetZDO().m_uid); } catch (Exception ex) { Plugin.Error(ex); } } } private static void Restore(Plan plan, bool local, bool requireAfter) { try { foreach (Container chest in plan.Chests) { if (!Registry.Valid(chest) || !((Component)chest).GetComponent<ZNetView>().IsOwner()) { throw new InvalidOperationException("Cannot restore a chest whose ownership changed."); } if (requireAfter && !Items.Bytes(chest.GetInventory()).SequenceEqual(plan.After[Registry.Id(chest)])) { throw new InvalidOperationException("Chest changed before rollback."); } } foreach (KeyValuePair<ItemData, int> count in plan.Counts) { count.Key.m_stack = count.Value; } foreach (Container chest2 in plan.Chests) { RestoreInventory(chest2.GetInventory(), plan.Originals[Registry.Id(chest2)]); Registry.Save.Invoke(chest2, null); } if (local) { RestoreInventory(((Humanoid)plan.Player).GetInventory(), plan.Originals["player"]); } File.Move(plan.Journal, Path.ChangeExtension(plan.Journal, ".rolledback")); plan.Resolved = true; } catch { Transactions.Quarantine(plan.Chests); throw; } } internal static void Uncertain(Plan plan) { if (plan != null && plan.Committed && !plan.Resolved) { Transactions.Quarantine(plan.Chests); } } internal static bool ApplyPlayer(PlayerSnapshot original, byte[] delivery, string ticket) { Transactions.DemandWritable(); Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory(); Inventory val = PlayerSnapshot.Decode(delivery, original.Width, original.Height); byte[] array = Items.Bytes(val); List<ItemData> list = inventory.GetAllItems().ToList(); Dictionary<ItemData, int> dictionary = list.ToDictionary((ItemData i) => i, (ItemData i) => i.m_stack); string text = WriteJournal("client-" + ticket, Player.m_localPlayer.GetPlayerID(), original.Bytes, array, new List<Container>(), new Dictionary<string, byte[]>(), new Dictionary<string, byte[]>()); Transactions.Writing = true; try { Transactions.Replace(inventory, val.GetAllItems().ToList()); if (!Items.Bytes(inventory).SequenceEqual(array)) { throw new InvalidOperationException("A mod changed the player inventory during delivery."); } File.Move(text, Path.ChangeExtension(text, ".committed")); } catch { try { foreach (KeyValuePair<ItemData, int> item in dictionary) { item.Key.m_stack = item.Value; } RestoreInventory(inventory, list); File.Move(text, Path.ChangeExtension(text, ".rolledback")); } catch { Transactions.Quarantine((IEnumerable<Container>)(object)new Container[0]); throw; } return false; } finally { Transactions.Writing = false; } NotifyPlayer(Player.m_localPlayer); return true; } private static void RestoreInventory(Inventory inv, List<ItemData> items) { inv.GetAllItems().Clear(); inv.GetAllItems().AddRange(items); Transactions.Notify(inv); } private static void NotifyPlayer(Player player) { try { Transactions.Notify(((Humanoid)player).GetInventory()); } catch (Exception ex) { Plugin.Error(ex); } } private static string WriteJournal(string ticket, long player, byte[] beforePlayer, byte[] afterPlayer, IEnumerable<Container> chests, Dictionary<string, byte[]> before, Dictionary<string, byte[]> after) { string text = Path.Combine(Transactions.DirectJournalDirectory, ticket + ".pending"); try { using FileStream fileStream = new FileStream(text, FileMode.CreateNew, FileAccess.Write, FileShare.Read); using (StreamWriter streamWriter = new StreamWriter(fileStream, Encoding.UTF8, 4096, leaveOpen: true)) { streamWriter.WriteLine("YggdrasilStorage direct transfer; UTC=" + DateTime.UtcNow.ToString("o") + "; player=" + player); streamWriter.WriteLine("player-before=" + Convert.ToBase64String(beforePlayer)); streamWriter.WriteLine("player-after=" + Convert.ToBase64String(afterPlayer)); foreach (Container chest in chests) { string text2 = Registry.Id(chest); streamWriter.WriteLine("container=" + text2); streamWriter.WriteLine("before=" + Convert.ToBase64String(before[text2])); streamWriter.WriteLine("after=" + Convert.ToBase64String(after[text2])); } streamWriter.Flush(); } fileStream.Flush(flushToDisk: true); return text; } catch { Transactions.Quarantine(chests); throw; } } } internal static class EpicLootIntegration { internal const string ModId = "randyknapp.mods.epicloot"; internal static bool Ready = true; private static MethodInfo save; private static MethodInfo magic; private static MethodInfo displayName; internal static void Install() { if (!Chainloader.PluginInfos.ContainsKey("randyknapp.mods.epicloot")) { return; } try { Type type = AccessTools.TypeByName("EpicLoot.Data.ItemInfo"); Type type2 = AccessTools.TypeByName("EpicLoot.ItemDataExtensions"); save = AccessTools.Method(type, "SaveItem", new Type[1] { typeof(ItemData) }, (Type[])null); magic = AccessTools.Method(type2, "IsMagic", new Type[1] { typeof(ItemData) }, (Type[])null); displayName = AccessTools.Method(type2, "GetDisplayName", new Type[1] { typeof(ItemData) }, (Type[])null); if (save == null || magic == null || displayName == null) { throw new MissingMethodException("Unsupported EpicLoot item-data API."); } Plugin.Warn("EpicLoot integration active: cached item data flushed before snapshots; named enchanted items searchable; magic items protected from automatic ingredient consumption."); } catch (Exception ex) { Ready = false; Plugin.Error(ex); Plugin.Warn("EpicLoot integration could not load; Yggdrasil writes disabled to protect custom item data."); } } internal static void Prepare(ItemData item) { if (!Ready) { throw new InvalidOperationException("EpicLoot integration unavailable; item snapshots disabled."); } if (save != null) { save.Invoke(null, new object[1] { item }); } } internal static bool IsMagic(ItemData item) { if (magic != null) { return (bool)magic.Invoke(null, new object[1] { item }); } return false; } internal static string Name(ItemData item) { if (!(displayName == null)) { return (string)displayName.Invoke(null, new object[1] { item }); } return item.m_shared.m_name; } } } namespace YggdrasilStorage.Backend { public sealed class ItemIdentity { public string Prefab; public string RuntimeType; public string CrafterName; public int Quality; public int Variant; public int WorldLevel; public float Durability; public long Crafter; public bool Cheated; public IDictionary<string, string> CustomData = new Dictionary<string, string>(); public IDictionary<string, string> Extensions = new Dictionary<string, string>(); public string Key() { if (string.IsNullOrEmpty(Prefab)) { throw new ArgumentException("Item has no registered prefab identity."); } using MemoryStream memoryStream = new MemoryStream(); using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true)) { binaryWriter.Write(Prefab); binaryWriter.Write(RuntimeType ?? ""); binaryWriter.Write(Quality); binaryWriter.Write(Variant); binaryWriter.Write(WorldLevel); binaryWriter.Write(Durability); binaryWriter.Write(Crafter); binaryWriter.Write(CrafterName ?? ""); binaryWriter.Write(Cheated); WriteMap(binaryWriter, CustomData); WriteMap(binaryWriter, Extensions); } using SHA256 sHA = SHA256.Create(); return BitConverter.ToString(sHA.ComputeHash(memoryStream.ToArray())).Replace("-", ""); } private static void WriteMap(BinaryWriter w, IDictionary<string, string> data) { if (data == null) { w.Write(0); return; } w.Write(data.Count); foreach (KeyValuePair<string, string> item in data.OrderBy<KeyValuePair<string, string>, string>((KeyValuePair<string, string> x) => x.Key, StringComparer.Ordinal)) { w.Write(item.Key); w.Write(item.Value != null); if (item.Value != null) { w.Write(item.Value); } } } } public static class ItemOrdering { public static string Group(string category, string prefab, string name) { string text = category ?? ""; string text2 = ((prefab ?? "") + " " + (name ?? "")).ToLowerInvariant(); if (text == "Material") { string text3 = (prefab ?? "").ToLowerInvariant(); if (Has(text2, "nail")) { return "03 Nails"; } if ((text3.EndsWith("ore", StringComparison.Ordinal) && !text3.EndsWith("core", StringComparison.Ordinal)) || text3.StartsWith("ore", StringComparison.Ordinal) || Has(text2, " ore", "scrapiron", "ironscrap", "copperscrap", "blackmetalscrap", "scrapmetal")) { return "02 Ores and scrap"; } if (Has(text2, "wood", "roundlog", "elderbark", "ancientbark", "timber")) { return "01 Wood"; } if (Has(text2, "copper", "bronze", "iron", "silver", "blackmetal", "flametal", "ingot") || prefab == "Tin") { return "03 Ingots"; } if (Has(text2, "stone", "flint", "obsidian", "crystal", "marble", "grausten")) { return "04 Stone and minerals"; } if (Has(text2, "coal", "resin", "tar")) { return "05 Fuel and resin"; } if (Has(text2, "hide", "leather", "pelt", "scale", "feather", "bone", "chitin", "carapace")) { return "06 Hides and animal materials"; } if (Has(text2, "seed", "cone", "sapling")) { return "07 Seeds"; } if (Has(text2, "meat", "berry", "berries", "mushroom", "thistle", "dandelion", "honey", "carrot", "turnip", "onion", "barley", "flax", "sap", "egg", "fish", "flour")) { return "08 Plants and ingredients"; } if (Has(text2, "coin", "amber", "ruby", "pearl", "gem")) { return "09 Valuables"; } return "10 Other materials"; } switch (text) { case "Consumable": return "11 Food and consumables"; case "Ammo": case "AmmoNonEquipable": return "12 Ammunition"; case "OneHandedWeapon": case "TwoHandedWeapon": case "TwoHandedWeaponLeft": case "Bow": return "13 Weapons"; case "Shield": return "14 Shields"; case "Helmet": case "Chest": case "Legs": case "Shoulder": return "15 Armor"; case "Utility": return "16 Accessories"; case "Tool": case "Torch": return "17 Tools"; case "Trophy": return "18 Trophies"; case "Fish": return "08 Plants and ingredients"; default: return "19 " + ((text.Length == 0) ? "Other items" : text); } } private static bool Has(string text, params string[] values) { foreach (string value in values) { if (text.IndexOf(value, StringComparison.Ordinal) >= 0) { return true; } } return false; } } } namespace YggdrasilStorage { internal sealed class ItemRow { internal string Key; internal string Name; internal string Category; internal string Prefab; internal int Variant; internal long Count; internal long MaterialCount; internal int Limit; internal bool Tray; } internal static class Items { internal static bool MaterialEligible(ItemData item) { if (!item.m_equipped && item.m_quality == 1 && !SmartCraftIntegration.IsLocked(item)) { return !EpicLootIntegration.IsMagic(item); } return false; } internal static List<ItemRow> CarriedRows() { Player localPlayer = Player.m_localPlayer; if (!Object.op_Implicit((Object)(object)localPlayer)) { return new List<ItemRow>(); } return (from i in ((Humanoid)localPlayer).GetInventory().GetAllItems() where !i.m_equipped && !SmartCraftIntegration.IsLocked(i) select i).GroupBy(Key).Select(delegate(IGrouping<string, ItemData> g) { //IL_0095: Unknown result type (might be due to invalid IL or missing references) ItemData val = g.First(); return new ItemRow { Key = g.Key, Name = EpicLootIntegration.Name(val) + ((val.m_quality > 1) ? (" [Q" + val.m_quality + "]") : ""), Count = ((IEnumerable<ItemData>)g).Sum((Func<ItemData, long>)((ItemData i) => i.m_stack)), Limit = val.m_shared.m_maxStackSize, Category = ((object)val.m_shared.m_itemType).ToString(), Prefab = ((Object)val.m_dropPrefab).name, Variant = val.m_variant }; }).ToList(); } internal static string Key(ItemData item) { if (!Object.op_Implicit((Object)(object)item.m_dropPrefab)) { throw new InvalidOperationException("Item has no registered prefab."); } EpicLootIntegration.Prepare(item); ItemIdentity itemIdentity = new ItemIdentity(); itemIdentity.Prefab = ((Object)item.m_dropPrefab).name; itemIdentity.RuntimeType = ((object)item).GetType().FullName; itemIdentity.Quality = item.m_quality; itemIdentity.Variant = item.m_variant; itemIdentity.Durability = item.m_durability; itemIdentity.WorldLevel = item.m_worldLevel; itemIdentity.Crafter = item.m_crafterID; itemIdentity.CrafterName = item.m_crafterName; itemIdentity.Cheated = item.m_cheated; itemIdentity.CustomData = item.m_customData; itemIdentity.Extensions = Compatibility.Identity(item); return itemIdentity.Key(); } internal static ItemData Clone(ItemData item) { ItemData val = item.Clone(); if (object.ReferenceEquals(val, item)) { throw new InvalidOperationException("An item mod returned the original from Clone; unsafe transfer refused."); } val.m_customData = new Dictionary<string, string>(item.m_customData ?? new Dictionary<string, string>()); if (Key(val) != Key(item)) { throw new InvalidOperationException("An item mod did not preserve metadata when cloning; unsafe transfer refused."); } return val; } internal static byte[] Bytes(Inventory inventory) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); inventory.Save(val); return val.GetArray(); } internal static StoreState Read(Container c, Dictionary<string, ItemData> prototypes) { return Read(c.GetInventory(), Registry.Id(c), prototypes, Key); } internal static StoreState Read(Inventory inv, string id, Dictionary<string, ItemData> prototypes, Func<ItemData, string> keyFor) { StoreState storeState = new StoreState(); storeState.Id = id; storeState.Capacity = inv.GetWidth() * inv.GetHeight(); StoreState storeState2 = storeState; foreach (ItemData allItem in inv.GetAllItems()) { string key = keyFor(allItem); if (allItem.m_gridPos.x < 0 || allItem.m_gridPos.x >= inv.GetWidth() || allItem.m_gridPos.y < 0 || allItem.m_gridPos.y >= inv.GetHeight()) { throw new InvalidOperationException("Invalid inventory coordinates."); } storeState2.Items.Add(new StackState { Key = key, Count = allItem.m_stack, Limit = allItem.m_shared.m_maxStackSize, Slot = allItem.m_gridPos.y * inv.GetWidth() + allItem.m_gridPos.x }); if (prototypes != null && !prototypes.ContainsKey(key)) { prototypes.Add(key, Clone(allItem)); } } storeState2.Validate(); return storeState2; } internal static List<ItemData> Materialize(StoreState state, int width, Dictionary<string, ItemData> prototypes) { return state.Items.Select(delegate(StackState x) { //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) ItemData val = Clone(prototypes[x.Key]); val.m_stack = x.Count; val.m_gridPos = new Vector2i(x.Slot % width, x.Slot / width); return val; }).ToList(); } internal static List<ItemRow> Rows(IEnumerable<Container> containers, bool tray) { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) Dictionary<string, ItemRow> dictionary = new Dictionary<string, ItemRow>(); checked { foreach (Container container in containers) { if (container.IsInUse()) { continue; } Registry.Load.Invoke(container, null); foreach (ItemData allItem in container.GetInventory().GetAllItems()) { string key = Key(allItem); if (!dictionary.TryGetValue(key, out var value)) { ItemRow itemRow = new ItemRow(); itemRow.Key = key; itemRow.Name = EpicLootIntegration.Name(allItem) + ((allItem.m_quality > 1) ? (" [Q" + allItem.m_quality + "]") : ""); itemRow.Category = ((object)allItem.m_shared.m_itemType).ToString(); itemRow.Limit = allItem.m_shared.m_maxStackSize; itemRow.Tray = tray; itemRow.Prefab = ((Object)allItem.m_dropPrefab).name; itemRow.Variant = allItem.m_variant; value = itemRow; dictionary.Add(key, value); } value.Count += allItem.m_stack; if (MaterialEligible(allItem)) { value.MaterialCount += allItem.m_stack; } } } return dictionary.Values.OrderBy<ItemRow, string>((ItemRow x) => x.Name, StringComparer.Ordinal).ToList(); } } } internal sealed class MaterialRecipe { internal string Id; internal string Name; internal string Kind; internal int Amount; internal readonly Dictionary<string, int> Needs = new Dictionary<string, int>(StringComparer.Ordinal); internal readonly Dictionary<string, string> Labels = new Dictionary<string, string>(StringComparer.Ordinal); } internal static class MaterialRecipes { internal static List<MaterialRecipe> Catalog() { List<MaterialRecipe> list = new List<MaterialRecipe>(); if (!Object.op_Implicit((Object)(object)ObjectDB.instance)) { return list; } foreach (Recipe recipe in ObjectDB.instance.m_recipes) { if (Object.op_Implicit((Object)(object)recipe) && recipe.m_enabled && Object.op_Implicit((Object)(object)recipe.m_item) && !recipe.m_noCraftOnlyUpgrade && !recipe.m_requireOnlyOneIngredient && recipe.m_amount >= 1) { Add(list, "item:" + ((Object)recipe).name, recipe.m_item.m_itemData.m_shared.m_name, "Item recipe", recipe.m_amount, recipe.m_resources); } } HashSet<GameObject> hashSet = new HashSet<GameObject>(); foreach (GameObject item in ObjectDB.instance.m_items) { ItemDrop val = (Object.op_Implicit((Object)(object)item) ? item.GetComponent<ItemDrop>() : null); PieceTable val2 = (Object.op_Implicit((Object)(object)val) ? val.m_itemData.m_shared.m_buildPieces : null); if (!Object.op_Implicit((Object)(object)val2)) { continue; } foreach (GameObject piece in val2.m_pieces) { if (Object.op_Implicit((Object)(object)piece) && hashSet.Add(piece)) { Piece component = piece.GetComponent<Piece>(); if (Object.op_Implicit((Object)(object)component) && component.m_enabled) { Add(list, "build:" + ((Object)piece).name, component.m_name, "Building piece", 1, component.m_resources); } } } } return (from r in list group r by r.Id into g select g.First()).ToList(); } private static void Add(List<MaterialRecipe> rows, string id, string name, string kind, int output, Requirement[] requirements) { if (requirements == null) { return; } MaterialRecipe materialRecipe = new MaterialRecipe(); materialRecipe.Id = id; materialRecipe.Name = name; materialRecipe.Kind = kind; materialRecipe.Amount = output; MaterialRecipe materialRecipe2 = materialRecipe; foreach (Requirement val in requirements) { if (val == null || !Object.op_Implicit((Object)(object)val.m_resItem)) { continue; } int amount = val.GetAmount(1); if (amount > 0) { string name2 = ((Object)((Component)val.m_resItem).gameObject).name; materialRecipe2.Needs.TryGetValue(name2, out var value); long num = (long)value + (long)amount; if (num > 10000) { return; } materialRecipe2.Needs[name2] = (int)num; materialRecipe2.Labels[name2] = val.m_resItem.m_itemData.m_shared.m_name; } } if (materialRecipe2.Needs.Count > 0 && materialRecipe2.Needs.Count <= 20) { rows.Add(materialRecipe2); } } internal static Dictionary<string, int> Quantities(MaterialRecipe recipe, IEnumerable<ItemRow> rows) { Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal); foreach (KeyValuePair<string, int> need in recipe.Needs) { KeyValuePair<string, int> keyValuePair = need; int num = keyValuePair.Value; Func<ItemRow, bool> predicate = delegate(ItemRow r) { if (!r.Tray) { string prefab = r.Prefab; KeyValuePair<string, int> keyValuePair2 = need; if (prefab == keyValuePair2.Key) { return r.MaterialCount > 0; } } return false; }; foreach (ItemRow item in rows.Where(predicate).OrderBy<ItemRow, string>((ItemRow r) => r.Key, StringComparer.Ordinal)) { int num2 = (int)Math.Min(num, item.MaterialCount); if (num2 > 0) { dictionary.Add(item.Key, num2); num -= num2; } if (num == 0) { break; } } if (num > 0) { throw new InvalidOperationException("Missing materials. Nothing withdrawn."); } } if (dictionary.Count > 20) { throw new InvalidOperationException("Too many different item variants for one material set."); } return dictionary; } } internal static class Network { private sealed class PeerState { internal ZNetPeer Peer; internal readonly RequestGate Gate = new RequestGate(); internal double NextHello; internal double NextKeep; } private sealed class DeferredRequest { internal PeerState Who; internal long Sequence; internal ZDOID Core; internal PlayerSnapshot Snapshot; internal byte Action; internal string Key; internal int Amount; internal double Deadline; } private sealed class Lease { internal string Ticket; internal long Server; internal double Until; } private sealed class Expected { internal ZRpc Owner; internal byte[] Bytes; internal uint Revision; internal bool Acknowledged; } private sealed class Operation { internal PeerState Who; internal long Sequence; internal string Ticket; internal string Key; internal PlayerSnapshot Snapshot; internal byte Action; internal int Amount; internal Container Core; internal List<Container> Containers; internal Dictionary<ZDOID, Expected> Expected = new Dictionary<ZDOID, Expected>(); internal Dictionary<ZDOID, long> Owners = new Dictionary<ZDOID, long>(); internal HashSet<ZRpc> FrozenPeers = new HashSet<ZRpc>(); internal List<ZRpc> Participants; internal bool Acquiring; internal DirectTransfers.Plan Plan; internal bool AwaitingPlayer; internal double Deadline; } private const string Hello = "Yggdrasil.HelloDirect2"; private const string Welcome = "Yggdrasil.WelcomeDirect2"; private const string Request = "Yggdrasil.RequestDirect2"; private const string Reply = "Yggdrasil.ReplyDirect2"; private const string Acquire = "Yggdrasil.Acquire2"; private const string Granted = "Yggdrasil.Granted2"; private const string Release = "Yggdrasil.ReleaseDirect2"; private const string Freeze = "Yggdrasil.FreezeDirect2"; private const string Frozen = "Yggdrasil.FrozenDirect2"; private const string ApplyPlayer = "Yggdrasil.ApplyPlayer2"; private const string AppliedPlayer = "Yggdrasil.AppliedPlayer2"; private const string KeepArea = "Yggdrasil.KeepArea3"; private static PlayerSnapshot clientSnapshot; private static byte clientAction; private static DeliveryReceipts deliveries = new DeliveryReceipts(); private static readonly Dictionary<ZRpc, DeferredRequest> deferred = new Dictionary<ZRpc, DeferredRequest>(); private static double nextKeepArea; private static readonly Dictionary<ZRpc, PeerState> peers = new Dictionary<ZRpc, PeerState>(); private static readonly Dictionary<ZDOID, Lease> leases = new Dictionary<ZDOID, Lease>(); private static PeerState local = new PeerState(); private static Operation operation; private static ZNet world; private static string clientToken; private static long sequence; private static long waiting; private static double clientDeadline; private static double nextHello; private static double nextRequest; internal static bool Pending { get { if (waiting == 0) { return Now < nextRequest; } return true; } } private static double Now => Time.realtimeSinceStartup; internal static void Tick() { //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Expected O, but got Unknown //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Expected O, but got Unknown //IL_0264: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance != (Object)(object)world) { Reset(); world = ZNet.instance; } if (!Object.op_Implicit((Object)(object)world) || !Object.op_Implicit((Object)(object)ZNetScene.instance)) { return; } try { List<ZNetPeer> live = world.GetConnectedPeers(); ZRpc[] array = peers.Keys.Where((ZRpc r) => !live.Any((ZNetPeer p) => p.m_rpc == r)).ToArray(); foreach (ZRpc key in array) { peers.Remove(key); } foreach (ZNetPeer item in live) { if (!peers.ContainsKey(item.m_rpc)) { Attach(item); } } ZDOID[] array2 = (from x in leases where x.Value.Until < Now select x.Key).ToArray(); foreach (ZDOID key2 in array2) { leases.Remove(key2); } Transactions.CheckJournal(); if (world.IsServer()) { RemoteAreas.Tick(); PollDeferred(); PollOperation(); } else if (string.IsNullOrEmpty(clientToken) && Now >= nextHello) { nextHello = Now + 2.0; ZNetPeer serverPeer = world.GetServerPeer(); if (serverPeer != null) { serverPeer.m_rpc.Invoke("Yggdrasil.HelloDirect2", new object[1] { (object)new ZPackage() }); } } if (!world.IsServer() && !string.IsNullOrEmpty(clientToken) && Now >= nextKeepArea) { nextKeepArea = Now + 5.0; Container val = (Object.op_Implicit((Object)(object)PlayerTray.ActiveCore) ? PlayerTray.ActiveCore : StorageWindow.ActiveCore); if (Registry.Valid(val)) { ZPackage val2 = new ZPackage(); val2.Write(clientToken); val2.Write(((Component)val).GetComponent<ZNetView>().GetZDO().m_uid); ZNetPeer serverPeer2 = world.GetServerPeer(); if (serverPeer2 != null) { serverPeer2.m_rpc.Invoke("Yggdrasil.KeepArea3", new object[1] { val2 }); } } } if (waiting != 0 && Now > clientDeadline) { waiting = 0L; StorageWindow.Status = "Transfer response timed out. Do not repeat an uncertain transfer; check the host log."; PlayerTray.Completed(success: false, null, 0); } } catch (Exception ex) { Plugin.Error(ex); } } private static void Attach(ZNetPeer peer) { peers.Add(peer.m_rpc, new PeerState { Peer = peer }); peer.m_rpc.Register<ZPackage>("Yggdrasil.ApplyPlayer2", (Action<ZRpc, ZPackage>)OnApplyPlayer); peer.m_rpc.Register<ZPackage>("Yggdrasil.AppliedPlayer2", (Action<ZRpc, ZPackage>)OnAppliedPlayer); peer.m_rpc.Register<ZPackage>("Yggdrasil.HelloDirect2", (Action<ZRpc, ZPackage>)OnHello); peer.m_rpc.Register<ZPackage>("Yggdrasil.KeepArea3", (Action<ZRpc, ZPackage>)OnKeepArea); peer.m_rpc.Register<ZPackage>("Yggdrasil.WelcomeDirect2", (Action<ZRpc, ZPackage>)OnWelcome); peer.m_rpc.Register<ZPackage>("Yggdrasil.RequestDirect2", (Action<ZRpc, ZPackage>)OnRequest); peer.m_rpc.Register<ZPackage>("Yggdrasil.ReplyDirect2", (Action<ZRpc, ZPackage>)OnReply); peer.m_rpc.Register<ZPackage>("Yggdrasil.Acquire2", (Action<ZRpc, ZPackage>)OnAcquire); peer.m_rpc.Register<ZPackage>("Yggdrasil.Granted2", (Action<ZRpc, ZPackage>)OnGranted); peer.m_rpc.Register<ZPackage>("Yggdrasil.ReleaseDirect2", (Action<ZRpc, ZPackage>)OnRelease); peer.m_rpc.Register<ZPackage>("Yggdrasil.FreezeDirect2", (Action<ZRpc, ZPackage>)OnFreeze); peer.m_rpc.Register<ZPackage>("Yggdrasil.FrozenDirect2", (Action<ZRpc, ZPackage>)OnFrozen); } private static bool FromServer(ZRpc rpc) { if (Object.op_Implicit((Object)(object)world) && !world.IsServer() && world.GetServerPeer() != null) { return world.GetServerPeer().m_rpc == rpc; } return false; } private static void OnHello(ZRpc rpc, ZPackage ignored) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown if (Object.op_Implicit((Object)(object)world) && world.IsServer() && peers.TryGetValue(rpc, out var value) && !(Now < value.NextHello)) { value.NextHello = Now + 1.0; ZPackage val = new ZPackage(); val.Write(value.Gate.Token); rpc.Invoke("Yggdrasil.WelcomeDirect2", new object[1] { val }); } } private static void OnKeepArea(ZRpc rpc, ZPackage pkg) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)world) || !world.IsServer() || pkg.Size() > 128 || !peers.TryGetValue(rpc, out var value) || Now < value.NextKeep) { return; } value.NextKeep = Now + 1.0; try { if (!(pkg.ReadString() != value.Gate.Token)) { RemoteAreas.Touch(value.Peer, pkg.ReadZDOID()); } } catch { } } private static void PollDeferred() { //IL_004d: 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_0093: Unknown result type (might be due to invalid IL or missing references) KeyValuePair<ZRpc, DeferredRequest>[] array = deferred.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair<ZRpc, DeferredRequest> keyValuePair = array[i]; DeferredRequest value = keyValuePair.Value; try { if (Now > value.Deadline) { throw new InvalidOperationException("The host could not finish loading this storage area. No transfer was performed; retry shortly."); } RemoteAreas.Touch(value.Who.Peer, value.Core); if (RemoteAreas.Ready(value.Who.Peer, value.Core)) { deferred.Remove(keyValuePair.Key); Process(value.Who, value.Sequence, Registry.Find(value.Core), value.Action, value.Key, value.Amount, value.Snapshot); } } catch (Exception ex) { deferred.Remove(keyValuePair.Key); if (operation != null && operation.Who == value.Who && operation.Sequence == value.Sequence) { Finish(); } Error(value.Who, value.Sequence, ex.Message); } } } private static void OnWelcome(ZRpc rpc, ZPackage pkg) { if (!FromServer(rpc) || pkg.Size() > 128) { return; } try { string text = pkg.ReadString(); if (text.Length == 32) { clientToken = text; } } catch (Exception ex) { Plugin.Error(ex); } } internal static void Send(Container core, byte action, string key, int amount) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Expected O, but got Unknown if (Pending || !Object.op_Implicit((Object)(object)world) || !Registry.Valid(core)) { return; } if (!world.IsServer() && string.IsNullOrEmpty(clientToken)) { StorageWindow.Status = "Connecting to storage service. All players and the host need the same mod version."; return; } ZPackage val = new ZPackage(); val.Write(world.IsServer() ? local.Gate.Token : clientToken); val.Write(++sequence); val.Write(action); val.Write(((Component)core).GetComponent<ZNetView>().GetZDO().m_uid); val.Write(key ?? ""); val.Write(amount); clientAction = action; clientSnapshot = ((action == 0) ? null : PlayerSnapshot.Capture(action)); if (clientSnapshot != null) { clientSnapshot.Write(val); } waiting = sequence; clientDeadline = Now + 80.0; StorageWindow.Status = ((action == 0) ? "Loading storage..." : "Preparing transfer..."); if (world.IsServer()) { Handle(local, new ZPackage(val.GetArray())); return; } world.GetServerPeer().m_rpc.Invoke("Yggdrasil.RequestDirect2", new object[1] { val }); } private static void OnRequest(ZRpc rpc, ZPackage pkg) { if (Object.op_Implicit((Object)(object)world) && world.IsServer() && peers.TryGetValue(rpc, out var value)) { Handle(value, pkg); } } private static Player PlayerFor(PeerState p) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) if (p.Peer == null) { if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || ((Character)Player.m_localPlayer).IsDead()) { throw new InvalidOperationException("Player unavailable."); } return Player.m_localPlayer; } if (!world.GetConnectedPeers().Contains(p.Peer)) { throw new InvalidOperationException("Player disconnected."); } GameObject val = ZNetScene.instance.FindInstance(p.Peer.m_characterID); Player val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<Player>() : null); if (!Object.op_Implicit((Object)(object)val2)) { throw new InvalidOperationException("Your character has not finished loading on the host. Refresh and retry."); } ZNetView component = ((Component)val2).GetComponent<ZNetView>(); if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || ((Character)val2).IsDead() || val2.GetPlayerID() == 0) { throw new InvalidOperationException("Your character is still spawning or unavailable. Retry when ready."); } if (p.Peer.m_uid == 0 || component.GetZDO().GetOwner() != p.Peer.m_uid) { throw new InvalidOperationException("Your character's network ownership is not ready. Retry shortly."); } return val2; } private static void Handle(PeerState who, ZPackage pkg) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) long num = 0L; try { if (pkg.Size() > 1048576) { return; } string token = pkg.ReadString(); num = pkg.ReadLong(); if (!who.Gate.Accept(token, num, Now)) { Error(who, num, "Repeated request or too many requests; retry shortly."); return; } byte b = pkg.ReadByte(); ZDOID val = pkg.ReadZDOID(); string text = pkg.ReadString(); int num2 = pkg.ReadInt(); if ((b != 0 && b != 1 && b != 2 && b != 5 && b != 6 && b != 7) || ((b == 1 || b == 2) && (text.Length != 64 || num2 < 1 || num2 > 10000)) || (b == 5 && (text.Length < 1 || text.Length > 128))) { throw new InvalidOperationException("Invalid storage request."); } if (b == 7) { BulkDeposit.Parse(text); } PlayerSnapshot snapshot = ((b == 0) ? null : PlayerSnapshot.Read(pkg)); if (who.Peer != null) { if (deferred.ContainsKey(who.Peer.m_rpc)) { throw new InvalidOperationException("A storage request is already waiting for this area."); } RemoteAreas.Touch(who.Peer, val); if (!RemoteAreas.Ready(who.Peer, val)) { deferred[who.Peer.m_rpc] = new DeferredRequest { Who = who, Sequence = num, Core = val, Action = b, Key = text, Amount = num2, Snapshot = snapshot, Deadline = Now + 50.0 }; return; } } Process(who, num, Registry.Find(val), b, text, num2, snapshot); } catch (Exception ex) { if (operation != null && operation.Who == who && operation.Sequence == num) { Finish(); } Error(who, num, ex.Message); } } private static void Process(PeerState who, long seq, Container core, byte action, string key, int amount, PlayerSnapshot snapshot) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0249: 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_02a8: Expected O, but got Unknown //IL_02dd: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: 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) Player val = PlayerFor(who); long playerID = val.GetPlayerID(); Registry.DemandCore(core, playerID, ((Component)val).transform.position, action == 6); if (action == 0) { Respond(who, seq, core, playerID, "Ready. Counts update after transfers. Use Refresh to check other changes."); return; } if (!SmartCraftIntegration.Ready || !EpicLootIntegration.Ready) { throw new InvalidOperationException("A required optional-mod integration is unavailable; transfers disabled."); } if (Network.operation != null) { throw new InvalidOperationException("Another transfer is in progress. Retry shortly."); } List<Container> list = Registry.Discover(core, playerID); if (list.Count == 0) { throw new InvalidOperationException("No accessible chests are connected."); } if (list.Any((Container c) => c.IsInUse() || Transactions.Blocked(c))) { throw new InvalidOperationException("Close nearby chests before moving items."); } Operation operation = new Operation(); operation.Who = who; operation.Sequence = seq; operation.Core = core; operation.Containers = list; operation.Key = key; operation.Amount = amount; operation.Action = action; operation.Snapshot = snapshot; operation.Ticket = Guid.NewGuid().ToString("N"); operation.Deadline = Now + 8.0; operation.Participants = (from p in world.GetConnectedPeers() select p.m_rpc).ToList(); Operation operation2 = (Network.operation = operation); foreach (Container item in list) { ZNetView component = ((Component)item).GetComponent<ZNetView>(); ZDO zDO = component.GetZDO(); if (IsLeased(item)) { throw new InvalidOperationException("Container is reserved by another transfer."); } leases[zDO.m_uid] = new Lease { Ticket = operation2.Ticket, Server = ZNet.GetUID(), Until = Now + 30.0 }; operation2.Owners.Add(zDO.m_uid, zDO.GetOwner()); if (component.IsOwner()) { Registry.Load.Invoke(item, null); continue; } ZNetPeer val2 = liveOwner(zDO.GetOwner()); if (val2 == null) { throw new InvalidOperationException("Container owner unavailable; retry once the area has settled."); } operation2.Expected.Add(zDO.m_uid, new Expected { Owner = val2.m_rpc }); } foreach (ZRpc participant in operation2.Participants) { ZPackage val3 = new ZPackage(); val3.Write(operation2.Ticket); val3.Write(operation2.Owners.Count); foreach (ZDOID key2 in operation2.Owners.Keys) { val3.Write(key2); } participant.Invoke("Yggdrasil.FreezeDirect2", new object[1] { val3 }); } SmartCraftIntegration.Invalidate(); PollOperation(); } private static ZNetPeer liveOwner(long uid) { return ((IEnumerable<ZNetPeer>)world.GetConnectedPeers()).FirstOrDefault((Func<ZNetPeer, bool>)((ZNetPeer p) => p.m_uid == uid)); } private static void OnFreeze(ZRpc rpc, ZPackage pkg) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) if (!FromServer(rpc) || pkg.Size() > 4096 || !SmartCraftIntegration.Ready || !EpicLootIntegration.Ready) { return; } try { string text = pkg.ReadString(); int num = pkg.ReadInt(); if (text.Length != 32 || num < 1 || num > 129) { return; } ZPackage val = new ZPackage(); val.Write(text); val.Write(num); for (int i = 0; i < num; i++) { ZDOID val2 = pkg.ReadZDOID(); ZDO zDO = ZDOMan.instance.GetZDO(val2); Container val3 = Registry.Find(val2); leases[val2] = new Lease { Ticket = text, Server = world.GetServerPeer().m_uid, Until = Now + 30.0 }; val.Write(val2); val.Write(zDO != null); if (zDO != null) { val.Write(zDO.GetOwner()); val.Write(Object.op_Implicit((Object)(object)val3) && val3.IsInUse()); val.Write(Registry.Valid(val3)); } } rpc.Invoke("Yggdrasil.FrozenDirect2", new object[1] { val }); SmartCraftIntegration.Invalidate(); } catch (Exception ex) { Plugin.Error(ex); } } private static void OnFrozen(ZRpc rpc, ZPackage pkg) { //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) Operation operation = Network.operation; if (!Object.op_Implicit((Object)(object)world) || !world.IsServer() || operation == null || !operation.Participants.Contains(rpc) || pkg.Size() > 8192) { return; } try { string text = pkg.ReadString(); int num = pkg.ReadInt(); if (text != operation.Ticket || num != operation.Owners.Count || operation.FrozenPeers.Contains(rpc)) { return; } HashSet<ZDOID> hashSet = new HashSet<ZDOID>(); for (int i = 0; i < num; i++) { ZDOID val = pkg.ReadZDOID(); if (!hashSet.Add(val) || !operation.Owners.ContainsKey(val)) { throw new InvalidOperationException("Invalid freeze acknowledgement."); } if (pkg.ReadBool()) { long observedOwner = pkg.ReadLong(); bool flag = pkg.ReadBool(); bool loaded = pkg.ReadBool(); ZNetPeer val2 = ((IEnumerable<ZNetPeer>)world.GetConnectedPeers()).FirstOrDefault((Func<ZNetPeer, bool>)((ZNetPeer p) => p.m_rpc == rpc)); if (val2 == null) { throw new InvalidOperationException("Player disconnected during storage handoff."); } if (flag) { throw new InvalidOperationException("A player is using a connected chest. Close it and retry."); } if (!OwnershipAgreement.Accept(operation.Owners[val], val2.m_uid, observedOwner, loaded, flag)) { throw new InvalidOperationException("The chest owner is still synchronizing. Retry shortly."); } } } operation.FrozenPeers.Add(rpc); } catch (Exception ex) { Finish(); Error(operation.Who, operation.Sequence, ex.Message); } } private static void OnAcquire(ZRpc rpc, ZPackage pkg) { //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Expected O, but got Unknown //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) if (!FromServer(rpc) || pkg.Size() > 128) { return; } string text = ""; ZDOID val = default(ZDOID); ZPackage val2 = new ZPackage(); try { text = pkg.ReadString(); val = pkg.ReadZDOID(); if (text.Length != 32) { return; } Container val3 = Registry.Find(val); if (!Registry.Valid(val3) || val3.IsInUse() || Transactions.Blocked(val3) || !((Component)val3).GetComponent<ZNetView>().IsOwner() || !leases.TryGetValue(val, out var value) || value.Ticket != text || value.Until < Now) { throw new InvalidOperationException("Container busy or not owned."); } Piece component = ((Component)val3).GetComponent<Piece>(); if (!Object.op_Implicit((Object)(object)component) || component.GetCreator() == 0 || Object.op_Implicit((Object)(object)((Component)val3).GetComponentInParent<Ship>()) || Object.op_Implicit((Object)(object)((Component)val3).GetComponentInParent<Vagon>()) || Object.op_Implicit((Object)(object)((Component)val3).GetComponent<TombStone>())) { throw new InvalidOperationException("Ineligible container."); } Registry.Load.Invoke(val3, null); ZDO zDO = ((Component)val3).GetComponent<ZNetView>().GetZDO(); byte[] array = Items.Bytes(val3.GetInventory()); if (array.Length > 262144) { throw new InvalidOperationException("Inventory is too large for a handoff."); } Registry.Save.Invoke(val3, null); leases[val] = new Lease { Ticket = text, Server = world.GetServerPeer().m_uid, Until = Now + 30.0 }; zDO.SetOwner(world.GetServerPeer().m_uid); ZDOMan.instance.ForceSendZDO(world.GetServerPeer().m_uid, val); val2.Write(text); val2.Write(val); val2.Write(true); val2.Write(zDO.DataRevision); val2.Write(array); } catch (Exception) { val2 = new ZPackage(); val2.Write(text); val2.Write(val); val2.Write(false); } rpc.Invoke("Yggdrasil.Granted2", new object[1] { val2 }); } private static void OnGranted(ZRpc rpc, ZPackage pkg) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)world) || !world.IsServer() || Network.operation == null || pkg.Size() > 263000) { return; } try { string text = pkg.ReadString(); ZDOID key = pkg.ReadZDOID(); if (!(text != Network.operation.Ticket) && Network.operation.Expected.TryGetValue(key, out var value) && value.Owner == rpc && !value.Acknowledged) { if (!pkg.ReadBool()) { throw new InvalidOperationException("A chest is being used or changed owner. Close chests and retry."); } value.Revision = pkg.ReadUInt(); value.Bytes = pkg.ReadByteArray(); if (value.Bytes.Length > 262144) { throw new InvalidOperationException("Invalid handoff size."); } value.Acknowledged = true; } } catch (Exception ex) { Operation operation = Network.operation; Finish(); if (operation != null) { Error(operation.Who, operation.Sequence, ex.Message); } } } private static void PollOperation() { //IL_0137: 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_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Expected O, but got Unknown //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_036f: Unknown result type (might be due to invalid IL or missing references) //IL_053d: Unknown result type (might be due to invalid IL or missing references) //IL_0544: Expected O, but got Unknown if (operation == null) { return; } Operation op = operation; try { if (op.AwaitingPlayer) { if (Now > op.Deadline || !world.GetConnectedPeers().Contains(op.Who.Peer)) { throw new InvalidOperationException("Player delivery was not acknowledged. Storage is paused for recovery; do not repeat the transfer."); } return; } if (Now > op.Deadline) { throw new InvalidOperationException("Ownership handoff timed out. No transfer performed."); } if (op.Who.Peer != null) { RemoteAreas.Touch(op.Who.Peer, ((Component)op.Core).GetComponent<ZNetView>().GetZDO().m_uid); if (!RemoteAreas.Ready(op.Who.Peer, ((Component)op.Core).GetComponent<ZNetView>().GetZDO().m_uid)) { return; } } Player val = PlayerFor(op.Who); long playerID = val.GetPlayerID(); Registry.DemandCore(op.Core, playerID, ((Component)val).transform.position, op.Action == 6); if (!op.Participants.All((ZRpc r) => world.GetConnectedPeers().Any((ZNetPeer p) => p.m_rpc == r)) || world.GetConnectedPeers().Any((ZNetPeer p) => !op.Participants.Contains(p.m_rpc))) { throw new InvalidOperationException("Player connections changed; retry the transfer."); } if (op.FrozenPeers.Count != op.Participants.Count) { return; } if (!op.Acquiring) { op.Acquiring = true; foreach (KeyValuePair<ZDOID, Expected> item in op.Expected) { ZPackage val2 = new ZPackage(); val2.Write(op.Ticket); val2.Write(item.Key); item.Value.Owner.Invoke("Yggdrasil.Acquire2", new object[1] { val2 }); } } List<Container> source = Registry.Discover(op.Core, playerID); if (!new HashSet<string>(source.Select(Registry.Id)).SetEquals(op.Containers.Where(Registry.Valid).Select(Registry.Id)) || op.Containers.Any((Container c) => !Registry.Valid(c))) { throw new InvalidOperationException("Storage network changed; refresh and retry."); } foreach (Container container in op.Containers) { if (!Registry.Access(container, playerID) || container.IsInUse()) { throw new InvalidOperationException("Access or chest usage changed."); } ZDO zDO = ((Component)container).GetComponent<ZNetView>().GetZDO(); if (op.Expected.TryGetValue(zDO.m_uid, out var value)) { if (!value.Acknowledged || !((Component)container).GetComponent<ZNetView>().IsOwner() || zDO.DataRevision < value.Revision) { return; } byte[] byteArray = zDO.GetByteArray(ZDOVars.s_items, (byte[])null); if (byteArray == null || !byteArray.SequenceEqual(value.Bytes)) { throw new InvalidOperationException("Chest contents changed during handoff; retry."); } } else if (!((Component)container).GetComponent<ZNetView>().IsOwner()) { throw new InvalidOperationException("Ownership changed; retry."); } } if (op.Who.Peer == null && !clientSnapshot.Matches(op.Action)) { throw new InvalidOperationException("Your inventory changed. Retry the transfer."); } op.Plan = DirectTransfers.Prepare(op.Containers, op.Core, val, op.Snapshot, op.Action, op.Key, op.Amount); if (op.Plan.Moved == 0) { CompleteDirect(op); return; } DirectTransfers.Commit(op.Plan, op.Who.Peer == null, op.Ticket); if (op.Who.Peer == null) { CompleteDirect(op); return; } op.AwaitingPlayer = true; op.Deadline = Now + 15.0; ZPackage val3 = new ZPackage(); val3.Write(op.Ticket); val3.Write(op.Sequence); val3.Write(op.Plan.PlayerDelivery); op.Who.Peer.m_rpc.Invoke("Yggdrasil.ApplyPlayer2", new object[1] { val3 }); } catch (Exception ex) { Finish(); Error(op.Who, op.Sequence, ex.Message); Plugin.Error(ex); } } private static void CompleteDirect(Operation op) { int moved = op.Plan.Moved; string text = ((op.Action != 6) ? (((op.Action == 2) ? "Deposited " : ((op.Action == 5) ? "Crafted " : "Collected ")) + moved + " items") : ((moved == 0) ? "No room in connected chests" : ("Deposited " + moved + " items"))); Finish(); try { Respond(op.Who, op.Sequence, op.Core, op.Plan.Player.GetPlayerID(), text, "", moved); } catch (Exception ex) { Error(op.Who, op.Sequence, text + ". Refresh to reload the list."); Plugin.Error(ex); } } private static void OnApplyPlayer(ZRpc rpc, ZPackage pkg) { //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Expected O, but got Unknown if (!FromServer(rpc) || pkg.Size() > 787000) { return; } try { string text = pkg.ReadString(); long num = pkg.ReadLong(); byte[] delivery = pkg.ReadByteArray(); if (text.Length != 32) { return; } if (!deliveries.TryGet(text, num, out var delivered)) { if (num <= deliveries.LastSequence) { return; } delivered = false; if (num == waiting && clientSnapshot != null && PlayerTray.Busy && Object.op_Implicit((Object)(object)Player.m_localPlayer) && !((Character)Player.m_localPlayer).IsDead() && clientSnapshot.Matches(clientAction)) { delivered = DirectTransfers.ApplyPlayer(clientSnapshot, delivery, text); } deliveries.Record(text, num, delivered); } ZPackage val = new ZPackage(); val.Write(text); val.Write(num); val.Write(delivered); rpc.Invoke("Yggdrasil.AppliedPlayer2", new object[1] { val }); } catch (Exception ex) { StorageWindow.Status = "Delivery interrupted; storage is paused for recovery."; Plugin.Error(ex); } } private static void OnAppliedPlayer(ZRpc rpc, ZPackage pkg) { Operation operation = Network.operation; if (!Object.op_Implicit((Object)(object)world) || !world.IsServer() || operation == null || !operation.AwaitingPlayer || operation.Who.Peer == null || operation.Who.Peer.m_rpc != rpc || pkg.Size() > 128) { return; } try { if (!(pkg.ReadString() != operation.Ticket) && pkg.ReadLong() == operation.Sequence) { bool flag = pkg.ReadBool(); DirectTransfers.Resolve(operation.Plan, flag); if (flag) { CompleteDirect(operation); return; } Finish(); Error(operation.Who, operation.Sequence, "Your inventory changed before delivery. Nothing transferred; retry."); } } catch (Exception ex) { Finish(); Error(operation.Who, operation.Sequence, "Transfer needs recovery. Do not repeat it."); Plugin.Error(ex); } } private static void Finish() { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) Operation op = operation; operation = null; if (op == null) { return; } DirectTransfers.Uncertain(op.Plan); bool flag = op.Containers.Any(Transactions.Blocked); foreach (ZRpc participant in op.Participants) { try { ZPackage val = new ZPackage(); val.Write(op.Ticket); val.Write(flag); participant.Invoke("Yggdrasil.ReleaseDirect2", new object[1] { val }); } catch (Exception ex) { Plugin.Error(ex); } } ZDOID[] array = (from x in leases where x.Value.Ticket == op.Ticket select x.Key).ToArray(); foreach (ZDOID key in array) { if (flag) { leases[key].Until = double.PositiveInfinity; } else { leases.Remove(key); } } SmartCraftIntegration.Invalidate(); } private static void OnRelease(ZRpc rpc, ZPackage pkg) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) if (!FromServer(rpc) || pkg.Size() > 128) { return; } try { string ticket = pkg.ReadString(); bool flag = pkg.ReadBool(); ZDOID[] array = (from x in leases where x.Value.Ticket == ticket select x.Key).ToArray(); foreach (ZDOID key in array) { if (flag) { leases[key].Until = double.PositiveInfinity; } else { leases.Remove(key); } } SmartCraftIntegration.Invalidate(); } catch (Exception ex) { Plugin.Error(ex); } } internal static bool IsLeased(Container c) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (Transactions.Blocked(c)) { return true; } if (!Registry.Valid(c)) { return false; } if (leases.TryGetValue(((Component)c).GetComponent<ZNetView>().GetZDO().m_uid, out var value)) { return value.Until >= Now; } return false; } internal static bool AllowOwner(ZDO zdo, long owner) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)world) && world.IsServer() && Transactions.Blocked(Registry.Find(zdo.m_uid))) { return owner == ZNet.GetUID(); } if (leases.TryGetValue(zdo.m_uid, out var value) && !(value.Until < Now)) { return owner == value.Server; } return true; } private static void Respond(PeerState who, long seq, Container core, long pid, string message, string outputKey = "", int outputAmount = 0) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown List<Container> list = Registry.Discover(core, pid); List<ItemRow> list2 = Items.Rows(list, tray: false); if (list2.Count > 2048) { throw new InvalidOperationException("Too many distinct items for this prototype; reduce the Core radius."); } ZPackage val = new ZPackage(); val.Write(seq); val.Write(true); val.Write(message); val.Write(Plugin.Radius.Value); val.Write(list.Count); val.Write(list2.Count); foreach (ItemRow item in list2) { val.Write(item.Key); val.Write((item.Name.Length > 128) ? item.Name.Substring(0, 128) : item.Name); val.Write(item.Category); val.Write(item.Count); val.Write(item.Limit); val.Write(item.Tray); val.Write(item.Prefab); val.Write(item.Variant); val.Write(item.MaterialCount); } List<RecipeRow> list3 = Crafting.Rows(core, PlayerFor(who), list); val.Write(list3.Count); foreach (RecipeRow item2 in list3) { val.Write(item2.Id); val.Write(item2.Name); val.Write(item2.Station); val.Write((item2.Requirements.Length > 1024) ? item2.Requirements.Substring(0, 1024) : item2.Requirements); val.Write(item2.Amount); val.Write(item2.Ready); val.Write(item2.Reason); } val.Write(outputKey ?? ""); val.Write(outputAmount); val.Write(0u); val.Write(new byte[0]); if (val.Size() > 1048576) { throw new InvalidOperationException("Storage snapshot too large; reduce radius or recipe count."); } Deliver(who, val); } private static void Error(PeerState who, long seq, string message) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(seq); val.Write(false); val.Write((message.Length > 512) ? message.Substring(0, 512) : message); Deliver(who, val); } private static void Deliver(PeerState who, ZPackage p) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown if (who.Peer == null) { ReadReply(new ZPackage(p.GetArray())); return; } who.Peer.m_rpc.Invoke("Yggdrasil.ReplyDirect2", new object[1] { p }); } private static void OnReply(ZRpc rpc, ZPackage p) { if (FromServer(rpc)) { ReadReply(p); } } private static void ReadReply(ZPackage p) { try { if (p.Size() > 1048576 || waiting == 0 || p.ReadLong() != waiting) { return; } waiting = 0L; nextRequest = Now + 0.3; bool flag = p.ReadBool(); StorageWindow.Status = p.ReadString(); if (!flag) { PlayerTray.Completed(success: false, null, 0); StorageWindow.RefreshSoon(); return; } float range = p.ReadSingle(); int count = p.ReadInt(); int num = p.ReadInt(); if (num < 0 || num > 2048) { throw new InvalidOperationException("Invalid item count."); } List<ItemRow> list = new List<ItemRow>(); for (int i = 0; i < num; i++) { list.Add(new ItemRow { Key = p.ReadString(), Name = p.ReadString(), Category = p.ReadString(), Count = p.ReadLong(), Limit = p.ReadInt(), Tray = p.ReadBool(), Prefab = p.ReadString(), Variant = p.ReadInt(), MaterialCount = p.ReadLong() }); } int num2 = p.ReadInt(); if (num2 < 0 || num2 > 2048) { throw new InvalidOperationException("Invalid recipe count."); } List<RecipeRow> list2 = new List<RecipeRow>(); for (int j = 0; j < num2; j++) { list2.Add(new RecipeRow { Id = p.ReadString(), Name = p.ReadString(), Station = p.ReadString(), Requirements = p.ReadString(), Amount = p.ReadInt(), Ready = p.ReadBool(), Reason = p.ReadString() }); } string text = p.ReadString(); int num3 = p.ReadInt(); if (text.StartsWith("batch:", StringComparison.Ordinal)) { BulkDeposit.Parse(text.Substring(6)); } if ((!text.StartsWith("batch:", StringComparison.Ordinal) && text.Length != 0 && text.Length != 64) || num3 < 0 || num3 > 1000000) { throw new InvalidOperationException("Invalid collection output."); } uint revision = p.ReadUInt(); byte[] array = p.ReadByteArray(); if (array.Length > 262144) { throw new InvalidOperationException("Collection snapshot is too large."); } StorageWindow.SetRows(list, range, count); StorageWindow.SetRecipes(list2); PlayerTray.Completed(success: true, text, num3, revision, array); } catch (Exception ex) { waiting = 0L; StorageWindow.ClearRows(); PlayerTray.Completed(success: false, null, 0); StorageWindow.Status = "Invalid server response. Check the host log before repeating a transfer."; Plugin.Error(ex); } } internal static void Reset() { deferred.Clear(); RemoteAreas.Reset(); nextKeepArea = 0.0; peers.Clear(); leases.Clear(); operation = null; local = new PeerState(); clientToken = null; sequence = 0L; waiting = 0L; nextHello = 0.0; nextRequest = 0.0; clientSnapshot = null; deliveries = new DeliveryReceipts(); Transactions.Reset(); StorageWindow.Close(); PlayerTray.Cancel(); } } [HarmonyPatch(typeof(ZDO), "SetOwner")] internal static class HoldOwnership { private static bool Prefix(ZDO __instance, long __0) { return Network.AllowOwner(__instance, __0); } } [HarmonyPatch] internal static class LeaseContainerRequests { private static IEnumerable<MethodBase> TargetMethods() { yield return AccessTools.Method(typeof(Container), "RPC_RequestOpen", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Container), "RPC_RequestStack", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Container), "RPC_RequestTakeAll", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Container), "RPC_OpenResponse", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Container), "RPC_StackResponse", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Container), "RPC_TakeAllResponse", (Type[])null, (Type[])null); } private static bool Prefix(Container __instance) { return !Network.IsLeased(__instance); } } } namespace YggdrasilStorage.Backend { public static class OwnershipAgreement { public static bool Accept(long expectedOwner, long reportingPeer, long observedOwner, bool loaded, bool busy) { if (busy) { return false; } if (reportingPeer == expectedOwner || (loaded && observedOwner == reportingPeer)) { return observedOwner == expectedOwner; } return true; } } } namespace YggdrasilStorage { internal static class PlayerTray { internal static bool OpeningTray; private static Container target; private static bool quick; internal static int QuickUseFrame = -1; internal static Container ActiveCore => target; internal static bool Busy => (Object)(object)target != (Object)null; internal static bool BlocksMovement => false; internal static void TryQuickDeposit() { Player p = Player.m_localPlayer; if (!Object.op_Implicit((Object)(object)p) || Busy || Network.Pending || StorageWindow.Visible || InventoryGui.IsVisible() || !SmartCraftIntegration.Ready || !EpicLootIntegration.Ready || !(bool)AccessTools.Method(typeof(Player), "TakeInput", (Type[])null, (Type[])null).Invoke(p, null) || (!Input.GetKey((KeyCode)304) && !Input.GetKey((KeyCode)303)) || !ZInput.GetButtonDown("Use")) { return; } Container val = Registry.All.Where(delegate(Container c) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //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_0048: Unknown result type (might be due to invalid IL or missing references) if (Registry.Valid(c) && Object.op_Implicit((Object)(object)((Component)c).GetComponent<StorageCore>()) && Registry.Access(c, p.GetPlayerID())) { Vector3 val2 = ((Component)c).transform.position - ((Component)p).transform.position; return ((Vector3)(ref val2)).sqrMagnitude <= Plugin.Radius.Value * Plugin.Radius.Value; } return false; }).OrderBy(delegate(Container c) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) Vector3 val2 = ((Component)c).transform.position - ((Component)p).transform.position; return ((Vector3)(ref val2)).sqrMagnitude; }).ThenBy(Registry.Id) .FirstOrDefault(); if (Object.op_Implicit((Object)(object)val)) { if (!((Humanoid)p).GetInventory().GetAllItems().Any((ItemData i) => i.m_gridPos.y != 0 && !i.m_equipped && !SmartCraftIntegration.IsLocked(i))) { QuickUseFrame = Time.frameCount; StorageWindow.Status = "Nothing to store"; ((Character)p).Message((MessageType)2, StorageWindow.Status, 0, (Sprite)null, false); } else { Start(val, 6, "", 0, isQuick: true); QuickUseFrame = Time.frameCount; } } } internal static void Deposit(Container core, ItemRow row, int count) { Start(core, 2, row.Key, count, isQuick: false); } internal static void Withdraw(Container core, ItemRow row, int count) { Start(core, 1, row.Key, count, isQuick: false); } internal static void CollectMaterials(Container core, Dictionary<string, int> quantities, List<ItemRow> rows) { string text = string.Join(";", (from p in quantities.OrderBy<KeyValuePair<string, int>, string>((KeyValuePair<string, int> p) => p.Key, StringComparer.Ordinal) select p.Key + "=" + p.Value).ToArray()); BulkDeposit.Parse(text); Start(core, 7, text, 1, isQuick: false); } internal static void Craft(Container core, string recipe) { Start(core, 5, recipe, 1, isQuick: false); } private static void Start(Container core, byte action, string key, int amount, bool isQuick) { if (Busy || Network.Pending || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || !SmartCraftIntegration.Ready || !EpicLootIntegration.Ready) { return; } target = core; quick = isQuick; try { Network.Send(core, action, key, amount); if (!Network.Pending) { Cancel(); } } catch (Exception ex) { Cancel(); StorageWindow.Status = ex.Message; Plugin.Error(ex); } } internal static void Completed(bool success, string outputKey, int outputAmount, uint revision = 0u, byte[] buffer = null) { if (Busy) { bool flag = quick; Cancel(); if (flag && Object.op_Implicit((Object)(object)Player.m_localPlayer)) { ((Character)Player.m_localPlayer).Message((MessageType)2, StorageWindow.Status, 0, (Sprite)null, false); } } } internal static void Cancel() { target = null; quick = false; OpeningTray = false; } internal static void Tick() { if (Busy && !Network.Pending) { Cancel(); } } } [NetworkCompatibility(/*Could not decode attribute arguments.*/)] [BepInDependency("com.jotunn.jotunn", "2.30.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("org.yggdrasilstorage.valheim", "YggdrasilStorage", "0.5.4")] [BepInDependency(/*Could not decode attribute arguments