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.2
BepInEx/plugins/YggdrasilStorage/YggdrasilStorage.dll
Decompiled 6 hours 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: AssemblyTitle("YggdrasilStorage")] [assembly: AssemblyDescription("Independent Valheim storage and crafting prototype, inspired by BlushieMagic's Terraria Magic Storage.")] [assembly: AssemblyFileVersion("0.5.2.0")] [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.5.2.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) { 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.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 (((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, Registry.Id(core), new StackState { Key = key, Count = val.m_amount, Limit = val4.m_shared.m_maxStackSize }); } } 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 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 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 double Deadline; } private const string Hello = "Yggdrasil.Hello2"; private const string Welcome = "Yggdrasil.Welcome2"; private const string Request = "Yggdrasil.Request2"; private const string Reply = "Yggdrasil.Reply2"; private const string Acquire = "Yggdrasil.Acquire2"; private const string Granted = "Yggdrasil.Granted2"; private const string Release = "Yggdrasil.Release2"; private const string Freeze = "Yggdrasil.Freeze2"; private const string Frozen = "Yggdrasil.Frozen3"; private const string KeepArea = "Yggdrasil.KeepArea3"; 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); } if (world.IsServer()) { Transactions.CheckJournal(); 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.Hello2", 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 = "No response. Refresh and check the tray before trying another transfer."; 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.Hello2", (Action<ZRpc, ZPackage>)OnHello); peer.m_rpc.Register<ZPackage>("Yggdrasil.KeepArea3", (Action<ZRpc, ZPackage>)OnKeepArea); peer.m_rpc.Register<ZPackage>("Yggdrasil.Welcome2", (Action<ZRpc, ZPackage>)OnWelcome); peer.m_rpc.Register<ZPackage>("Yggdrasil.Request2", (Action<ZRpc, ZPackage>)OnRequest); peer.m_rpc.Register<ZPackage>("Yggdrasil.Reply2", (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.Release2", (Action<ZRpc, ZPackage>)OnRelease); peer.m_rpc.Register<ZPackage>("Yggdrasil.Freeze2", (Action<ZRpc, ZPackage>)OnFreeze); peer.m_rpc.Register<ZPackage>("Yggdrasil.Frozen3", (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.Welcome2", 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); } } 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_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0108: 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..."; 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); waiting = sequence; clientDeadline = Now + 65.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.Request2", 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_0173: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) long num = 0L; try { if (pkg.Size() > 2048) { 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 > 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 == 6 || b == 7) { BulkDeposit.Parse(text); } 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, Deadline = Now + 50.0 }; return; } } Process(who, num, Registry.Find(val), b, text, num2); } 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) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Expected O, but got Unknown //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_02d1: 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); list.Add(core); if (list.Any((Container c) => c.IsInUse() || Transactions.Blocked(c))) { throw new InvalidOperationException("Close nearby chests and the transfer tray 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.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 + 12.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.Freeze2", 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 + 12.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.Frozen3", 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 + 12.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_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: 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) //IL_01c8: Expected O, but got Unknown //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_032f: Unknown result type (might be due to invalid IL or missing references) if (operation == null) { return; } Operation op = operation; try { 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> list = Registry.Discover(op.Core, playerID); list.Add(op.Core); if (!new HashSet<string>(list.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."); } } string outputKey; int num = Transactions.Execute(op.Containers, op.Core, val, op.Action, op.Key, op.Amount, out outputKey); Finish(); string text = ((op.Action == 5) ? "Crafted " : "Moved ") + num + (((op.Action >= 2 && op.Action <= 4) || op.Action == 6) ? " into nearby chests." : "; collecting into your inventory..."); try { Respond(op.Who, op.Sequence, op.Core, playerID, text, outputKey, num); } catch (Exception ex) { Error(op.Who, op.Sequence, text + " Refresh to reload the list."); Plugin.Error(ex); } } catch (Exception ex2) { Finish(); Error(op.Who, op.Sequence, ex2.Message); Plugin.Error(ex2); } } private static void Finish() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) Operation op = operation; operation = null; if (op == null) { return; } foreach (ZRpc participant in op.Participants) { try { ZPackage val = new ZPackage(); val.Write(op.Ticket); participant.Invoke("Yggdrasil.Release2", 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) { leases.Remove(key); } SmartCraftIntegration.Invalidate(); } private static void OnRelease(ZRpc rpc, ZPackage pkg) { //IL_0073: 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_007e: Unknown result type (might be due to invalid IL or missing references) if (!FromServer(rpc) || pkg.Size() > 128) { return; } try { string ticket = pkg.ReadString(); ZDOID[] array = (from x in leases where x.Value.Ticket == ticket select x.Key).ToArray(); foreach (ZDOID key in array) { 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_0006: Unknown result type (might be due to invalid IL or missing references) 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown List<Container> list = Registry.Discover(core, pid); List<ItemRow> list2 = Items.Rows(list, tray: false); list2.AddRange(Items.Rows((IEnumerable<Container>)(object)new Container[1] { core }, tray: true)); 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.Concat((IEnumerable<Container>)(object)new Container[1] { core })); 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); ZDO zDO = ((Component)core).GetComponent<ZNetView>().GetZDO(); val.Write(zDO.DataRevision); val.Write(string.IsNullOrEmpty(outputKey) ? new byte[0] : zDO.GetByteArray(ZDOVars.s_items, 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.Reply2", 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 || 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) { 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 Core contents before retrying."; 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; 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 int stage; private static Container target; private static string key; private static int amount; private static bool granted; private static float deadline; private static uint expectedRevision; private static byte[] expectedBuffer; private static bool depositing; private static float distributeAt; private static bool quick; private static int deposited; internal static int QuickUseFrame = -1; internal static Container ActiveCore { get { if (!Busy) { return null; } return target; } } internal static bool Busy => stage != 0; internal static bool BlocksMovement { get { if (Busy) { return !quick; } return 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); return; } Start(val); quick = true; depositing = true; deposited = 0; stage = 2; QuickUseFrame = Time.frameCount; } } internal static void Deposit(Container core, ItemRow row, int count) { if (!Busy && !Network.Pending && Object.op_Implicit((Object)(object)Player.m_localPlayer) && SmartCraftIntegration.Ready && EpicLootIntegration.Ready) { Start(core); depositing = true; key = row.Key; amount = count; stage = 2; } } internal static void Withdraw(Container core, ItemRow row, int count) { if (Busy || Network.Pending || !Object.op_Implicit((Object)(object)Player.m_localPlayer)) { return; } try { Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory(); StoreState storeState = Items.Read(inventory, "player", null, Items.Key); long num = (long)(storeState.Capacity - storeState.Items.Count) * (long)row.Limit + storeState.Items.Where((StackState x) => x.Key == row.Key).Sum((StackState x) => (long)x.Limit - (long)x.Count); if (num < count) { StorageWindow.Status = "Not enough inventory space for that quantity."; return; } Start(core); key = row.Key; amount = count; if (row.Tray) { stage = 2; } else { Network.Send(core, 1, key, count); } } catch (Exception ex) { Cancel(); StorageWindow.Status = ex.Message; Plugin.Error(ex); } } internal static void CollectMaterials(Container core, Dictionary<string, int> quantities, List<ItemRow> rows) { if (Busy || Network.Pending || !Object.op_Implicit((Object)(object)Player.m_localPlayer)) { return; } try { string manifest = 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(manifest); StoreState storeState = new StoreState(); storeState.Id = "materials"; storeState.Capacity = 4096; StoreState storeState2 = storeState; foreach (KeyValuePair<string, int> pair in quantities) { Func<ItemRow, bool> predicate = delegate(ItemRow r) { if (!r.Tray) { string text = r.Key; KeyValuePair<string, int> keyValuePair3 = pair; return text == keyValuePair3.Key; } return false; }; ItemRow itemRow = rows.First(predicate); KeyValuePair<string, int> keyValuePair = pair; int num = keyValuePair.Value; while (num > 0) { int num2 = Math.Min(num, itemRow.Limit); List<StackState> items = storeState2.Items; StackState stackState = new StackState(); KeyValuePair<string, int> keyValuePair2 = pair; stackState.Key = keyValuePair2.Key; stackState.Count = num2; stackState.Limit = itemRow.Limit; stackState.Slot = storeState2.Items.Count; items.Add(stackState); num -= num2; } } StoreState storeState3 = Items.Read(((Humanoid)Player.m_localPlayer).GetInventory(), "player", null, Items.Key); Planner.BatchMove(new StoreState[2] { storeState2, storeState3 }, new HashSet<string> { "materials" }, new HashSet<string> { "player" }, quantities); Start(core); Network.Send(core, 7, manifest, 1); } catch (Exception ex) { Cancel(); StorageWindow.Status = ex.Message; } } internal static void Craft(Container core, string recipe) { if (!Busy && !Network.Pending) { Start(core); Network.Send(core, 5, recipe, 1); } } private static void Start(Container core) { target = core; key = null; amount = 0; granted = false; stage = 1; expectedBuffer = null; expectedRevision = 0u; depositing = false; quick = false; deadline = Time.unscaledTime + 80f; } internal static void Completed(bool success, string outputKey, int outputAmount, uint revision = 0u, byte[] buffer = null) { if (stage == 5) { if (quick && success && outputAmount > 0) { deposited += outputAmount; stage = 2; granted = false; deadline = Time.unscaledTime + 80f; return; } bool flag = quick; Cancel(); StorageWindow.RefreshSoon(); if (!success) { StorageWindow.Status += " Staged deposit items remain in Core contents; check there before retrying."; } if (flag && Object.op_Implicit((Object)(object)Player.m_localPlayer)) { ((Character)Player.m_localPlayer).Message((MessageType)2, success ? "Storage full. Remaining items are inside the Core (Alt+Use)." : StorageWindow.Status, 0, (Sprite)null, false); } } else if (stage == 1) { if (!success) { Cancel(); return; } if (string.IsNullOrEmpty(outputKey) || outputAmount <= 0) { Cancel(); StorageWindow.Status = "Output remains in Core contents. Use Alt+Use on the Core to collect it."; return; } key = outputKey; amount = outputAmount; expectedRevision = revision; expectedBuffer = buffer; stage = 2; } } internal static bool CaptureOpen(Container core) { if (stage != 3 || (Object)(object)core != (Object)(object)target) { return false; } granted = true; return true; } internal static void Cancel() { stage = 0; target = null; key = null; amount = 0; granted = false; OpeningTray = false; expectedBuffer = null; expectedRevision = 0u; } internal static void Tick() { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) if (!Busy) { return; } Player localPlayer = Player.m_localPlayer; try { float num = (quick ? Plugin.Radius.Value : 5f); string text = " Any staged items remain inside the Core (Alt+Use)."; if (!Object.op_Implicit((Object)(object)localPlayer) || ((Character)localPlayer).IsDead()) { throw new InvalidOperationException("Transfer stopped: character unavailable." + text); } if (!Registry.Valid(target)) { throw new InvalidOperationException("Transfer stopped: Core unavailable." + text); } if (!quick && !StorageWindow.Visible) { throw new InvalidOperationException("Transfer stopped: storage menu closed." + text); } Vector3 val = ((Component)target).transform.position - ((Component)localPlayer).transform.position; if (((Vector3)(ref val)).sqrMagnitude > num * num) { throw new InvalidOperationException("Transfer stopped: you moved out of Core range." + text); } if (Time.unscaledTime > deadline) { throw new InvalidOperationException("Transfer timed out while waiting for the server or chest ownership." + text); } if (stage == 1 || stage == 5 || Network.Pending || Network.IsLeased(target)) { return; } if (!Registry.Access(target, localPlayer.GetPlayerID())) { throw new InvalidOperationException("Core access changed. Output remains in Core contents."); } if (stage == 4) { if (!(Time.unscaledTime < distributeAt)) { stage = 5; Network.Send(target, (byte)(quick ? 6 : 2), key, amount); } return; } if (stage == 2) { stage = 3; OpeningTray = true; try { target.Interact((Humanoid)(object)localPlayer, false, false); } finally { OpeningTray = false; } StorageWindow.Status = (depositing ? "Depositing from your inventory..." : "Collecting into your inventory..."); } if (!granted || !((Component)target).GetComponent<ZNetView>().IsOwner()) { return; } if (target.IsInUse()) { throw new InvalidOperationException("Core is in use. Output remains in Core contents."); } ZDO zDO = ((Component)target).GetComponent<ZNetView>().GetZDO(); if (expectedBuffer != null) { if (zDO.DataRevision < expectedRevision) { return; } byte[] byteArray = zDO.GetByteArray(ZDOVars.s_items, new byte[0]); if (!byteArray.SequenceEqual(expectedBuffer)) { throw new InvalidOperationException("Core contents changed before collection. Check Core contents; no automatic retry."); } } Registry.Load.Invoke(target, null); target.SetInUse(true); int num2; try { num2 = MovePlayerItems(localPlayer, target, key, amount, depositing); } finally { target.SetInUse(false); } if (quick && num2 == 0) { int num3 = deposited; bool flag = ((Humanoid)localPlayer).GetInventory().GetAllItems().Any((ItemData i) => i.m_gridPos.y != 0 && !i.m_equipped && !SmartCraftIntegration.IsLocked(i)); Cancel(); StorageWindow.Status = ((num3 > 0) ? ("Deposited " + num3 + " items") : (flag ? "Storage full" : "Nothing to store")); ((Character)localPlayer).Message((MessageType)2, StorageWindow.Status, 0, (Sprite)null, false); } else if (depositing) { stage = 4; distributeAt = Time.unscaledTime + 0.4f; StorageWindow.Status = "Sending deposited items to connected storage..."; } else { Cancel(); StorageWindow.Status = "Collected " + num2 + " into your inventory."; StorageWindow.RefreshSoon(); } } catch (Exception ex) { bool flag2 = quick; Cancel(); StorageWindow.Status = ex.Message; if (flag2 && Object.op_Implicit((Object)(object)localPlayer)) { ((Character)localPlayer).Message((MessageType)2, ex.Message, 0, (Sprite)null, false); } Plugin.Error(ex); } } private static int MovePlayerItems(Player player, Container core, string itemKey, int count, bool deposit) { Inventory inventory = ((Humanoid)player).GetInventory(); Inventory inventory2 = core.GetInventory(); Dictionary<string, ItemData> prototypes = new Dictionary<string, ItemData>(); StoreState from = Items.Read(inventory2, "buffer", prototypes, Items.Key); StoreState storeState = Items.Read(inventory, "player", prototypes, (ItemData item) => ((deposit && (item.m_equipped || SmartCraftIntegration.IsLocked(item) || (quick && item.m_gridPos.y == 0))) ? "protected/" : "") + Items.Key(item)); MovePlan movePlan; try { movePlan = (quick ? Planner.QuickStack(new StoreState[2] { from, storeState }, "player", new HashSet<string> { "buffer" }, matchingOnly: false, new HashSet<string>(from i in storeState.Items where !i.Key.StartsWith("protected/", StringComparison.Ordinal) select i.Key)) : ((deposit || itemKey == null || !itemKey.StartsWith("batch:", StringComparison.Ordinal)) ? Planner.Move(new StoreState[2] { from, storeState }, new HashSet<string> { deposit ? "player" : "buffer" }, new HashSet<string> { deposit ? "buffer" : "player" }, itemKey, count) : Planner.BatchMove(new StoreState[2] { from, storeState }, new HashSet<string> { "buffer" }, new HashSet<string> { "player" }, BulkDeposit.Parse(itemKey.Substring(6))))); } catch (Exception ex) { throw new InvalidOperationException("Could not transfer: " + ex.Message + " Items remain in their current inventory."); } if (movePlan.Moved == 0) { return 0; } List<ItemData> items = Items.Materialize(movePlan.After.Single((StoreState x) => x.Id == "player"), inventory.GetWidth(), prototypes); List<ItemData> items2 = Items.Materialize(movePlan.After.Single((StoreState x) => x.Id == "buffer"), inventory2.GetWidth(), prototypes); List<ItemData> list = inventory.GetAllItems().ToList(); List<ItemData> list2 = inventory2.GetAllItems().ToList(); Dictionary<ItemData, int> dictionary = list.Concat(list2).ToDictionary((ItemData x) => x, (ItemData x) => x.m_stack); try { Transactions.Replace(inventory, items); Transactions.Replace(inventory2, items2); Registry.Save.Invoke(core, null); } catch { foreach (KeyValuePair<ItemData, int> item in dictionary) { item.Key.m_stack = item.Value; } inventory.GetAllItems().Clear(); inventory.GetAllItems().AddRange(list); inventory2.GetAllItems().Clear(); inventory2.GetAllItems().AddRange(list2); Transactions.Notify(inventory); Transactions.Notify(inventory2); Registry.Save.Invoke(core, null); throw; } try { Transactions.Notify(inventory); Transactions.Notify(inventory2); } catch (Exception ex2) { Plugin.Error(ex2); } if (quick) { key = string.Join(";", (from i in movePlan.After.Single((StoreState s) => s.Id == "buffer").Items group i by i.Key into g select new { Key = g.Key, Count = g.Sum((StackState i) => i.Count) - @from.Items.Where((StackState i) => i.Key == g.Key).Sum((StackState i) => i.Count) } into g where g.Count > 0 select g.Key + "=" + g.Count).ToArray()); } return movePlan.Moved; } } [HarmonyPatch(typeof(InventoryGui), "Show")] internal static class AutomaticCollectionPanel { private static bool Prefix(Container __0) { return !PlayerTray.CaptureOpen(__0); } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency("com.jotunn.jotunn", "2.30.0")] [BepInPlugin("org.yggdrasilstorage.valheim", "YggdrasilStorage", "0.5.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string Id = "org.yggdrasilstorage.valheim"; public const string Version = "0.5.2"; public const string CorePrefab = "YggdrasilStorage_Core"; internal static Plugin Instance; internal static ConfigEntry<float> Radius; internal static ConfigEntry<string> Eligible; private Harmony harmony; private void Awake() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Expected O, but got Unknown Instance = this; Radius = ((BaseUnityPlugin)this).Config.Bind<float>("Server", "Radius", 20f, new ConfigDescription("Storage and crafting-station radius in metres. Server value is authoritative.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 50f), new object[0])); ConfigEntry<bool> val = ((BaseUnityPlugin)this).Config.Bind<bool>("Migration", "Radius20Applied", false, "Internal: old default radius migrated once to 20 m."); if (!val.Value) { if (Mathf.Approximately(Radius.Value, 15f)) { Radius.Value = 20f; } val.Value = true; } Eligible = ((BaseUnityPlugin)this).Config.Bind<string>("Server", "EligiblePrefabs", "piece_chest_wood,piece_chest,piece_chest_blackmetal,piece_chest_private", "Exact prefab allowlist. Only placed stationary containers with a creator are eligible."); harmony = new Harmony("org.yggdrasilstorage.valheim"); harmony.PatchAll(); SmartCraftIntegration.Install(harmony); EpicLootIntegration.Install(); PrefabManager.OnVanillaPrefabsAvailable += RegisterCore; ((Component)this).gameObject.AddComponent<StorageWindow>(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Inspired by Magic Storage for Terraria, created by BlushieMagic. Independent; not affiliated with or endorsed by BlushieMagic."); } private void RegisterCore() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown PieceConfig val = new PieceConfig(); val.Name = "Storage Core"; val.Description = "Yggdrasil storage network and transfer tray"; val.PieceTable = "Hammer"; val.Category = "Furniture"; val.CraftingStation = "piece_workbench"; val.Requirements = (RequirementConfig[])(object)new RequirementConfig[2] { new RequirementConfig("Wood", 10, 0, true), new RequirementConfig("SurtlingCore", 1, 0, true) }; CustomPiece val2 = new CustomPiece("YggdrasilStorage_Core", "piece_chest_wood", val); Container component = val2.PiecePrefab.GetComponent<Container>(); component.m_name = "Storage Core transfer tray"; component.m_width = 5; component.m_height = 4; val2.PiecePrefab.AddComponent<StorageCore>(); RuneforgedModel.Install(val2.PiecePrefab); RuneforgedModel.InstallIcon(val2.PiecePrefab); if (!PieceManager.Instance.AddPiece(val2)) { ((BaseUnityPlugin)this).Logger.LogError((object)"Could not register Storage Core."); } PrefabManager.OnVanillaPrefabsAvailable -= RegisterCore; } private void Update() { Network.Tick(); PlayerTray.Tick(); } private void OnDestroy() { PrefabManager.OnVanillaPrefabsAvailable -= RegisterCore; if (harmony != null) { harmony.UnpatchSelf(); } Network.Reset(); } internal static void Error(Exception ex) { ((BaseUnityPlugin)Instance).Logger.LogError((object)ex); } internal static void Warn(string text) { ((BaseUnityPlugin)Instance).Logger.LogWarning((object)text); } } public sealed class StorageCore : MonoBehaviour { } [HarmonyPatch(typeof(Container), "Awake")] internal static class TrackContainer { private static void Postfix(Container __instance) { if ((Object)(object)((Component)__instance).GetComponent<ContainerTracker>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<ContainerTracker>(); } } } public sealed class ContainerTracker : MonoBehaviour { private Container container; private void Start() { container = ((Component)this).GetComponent<Container>(); Registry.All.Add(container); } private void OnDestroy() { if (!object.ReferenceEquals(container, null)) { Registry.All.Remove(container); } } } [HarmonyPatch(typeof(Container), "GetHoverText")] internal static class CoreHover { private static void Postfix(Container __instance, ref string __result) { if ((Object)(object)((Component)__instance).GetComponent<StorageCore>() != (Object)null) { __result = Localization.instance.Localize("Storage Core\n[<color=yellow><b>$KEY_Use</b></color>] Storage & crafting\n[<color=yellow>Alt + $KEY_Use</color>] Core contents"); } } } [HarmonyPatch(typeof(Container), "Interact")] internal static class CoreInteract { private static bool Prefix(Container __instance, Humanoid character, bool hold, bool alt, ref bool __result) { if (!PlayerTray.OpeningTray && PlayerTray.QuickUseFrame == Time.frameCount) { __result = false; return false; } if (Network.IsLeased(__instance)) { __result = false; return false; } if ((Object)(object)((Component)__instance).GetComponent<StorageCore>() == (Object)null || Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307) || PlayerTray.OpeningTray) { return true; } if (alt || Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303) || Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305)) { __result = false; return false; } __result = false; if (!hold && (Object)(object)character == (Object)(object)Player.m_localPlayer) { StorageWindow.Open(__instance); __result = true; } return false; } } [HarmonyPatch(typeof(Player), "Update")] internal static class CoreQuickDepositInput { [HarmonyPriority(800)] private static void Prefix(Player __instance) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { PlayerTray.TryQuickDeposit(); } } } [HarmonyPatch(typeof(Player), "TakeInput")] internal static class BlockMovement { [HarmonyPriority(0)] private static void Postfix(ref bool __result) { if (StorageWindow.BlockGameInput) { __result = false; } } } [HarmonyPatch(typeof(GameCamera), "UpdateMouseCapture")] internal static class WindowCursor { private static bool Prefix() { if (!StorageWindow.BlockGameInput) { return true; } Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; return false; } } [HarmonyPatch(typeof(PlayerController), "TakeInput")] internal static class BlockControllerInput { [HarmonyPriority(0)] private static void Postfix(ref bool __result) { if (StorageWindow.BlockGameInput) { __result = false; } } } [HarmonyPatch(typeof(ZInput), "GetMouseScrollWheel")] internal static class BlockGameScroll { [HarmonyPriority(0)] private static void Postfix(ref float __result) { if (StorageWindow.BlockGameInput) { __result = 0f; } } } [HarmonyPatch(typeof(ZInput), "GetMouseDelta")] internal static class BlockGameMouseLook { [HarmonyPriority(0)] private static void Postfix(ref Vector2 __result) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (StorageWindow.BlockGameInput) { __result = Vector2.zero; } } } [HarmonyPatch(typeof(Menu), "Update")] internal static class StorageEscape { private static bool Prefix() { if (StorageWindow.Visible && StorageWindow.EscapePressed()) { StorageWindow.Close(); } return !StorageWindow.BlockGameInput; } } internal static class Registry { internal static readonly HashSet<Container> All = new HashSet<Container>(); internal static readonly MethodInfo Load = AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null); internal static readonly MethodInfo Save = AccessTools.Method(typeof(Container), "Save", (Type[])null, (Type[])null); private static readonly MethodInfo CheckAccess = AccessTools.Method(typeof(Container), "CheckAccess", (Type[])null, (Type[])null); private static readonly MethodInfo IsPermitted = AccessTools.Method(typeof(PrivateArea), "IsPermitted", (Type[])null, (Type[])null); private static readonly MethodInfo IsEnabled = AccessTools.Method(typeof(PrivateArea), "IsEnabled", (Type[])null, (Type[])null); private static readonly MethodInfo IsInside = AccessTools.Method(typeof(PrivateArea), "IsInside", (Type[])null, (Type[])null); private static readonly FieldInfo Wards = AccessTools.Field(typeof(PrivateArea), "m_allAreas"); internal static string Id(Container c) { return ((object)Unsafe.As<ZDOID, ZDOID>(ref ((Component)c).GetComponent<ZNetView>().GetZDO().m_uid)/*cast due to .constrained prefix*/).ToString(); } internal static string Prefab(Container c) { ZNetView component = ((Component)c).GetComponent<ZNetView>(); GameObject val = ((Object.op_Implicit((Object)(object)component) && component.GetZDO() != null && Object.op_Implicit((Object)(object)ZNetScene.instance)) ? ZNetScene.instance.GetPrefab(component.GetZDO().GetPrefab()) : null); if (!Object.op_Implicit((Object)(object)val)) { return ""; } return ((Object)val).name; } internal static bool Valid(Container c) { if (!Object.op_Implicit((Object)(object)c) || !((Behaviour)c).isActiveAndEnabled) { return false; } ZNetView component = ((Component)c).GetComponent<ZNetView>(); if (Object.op_Implicit((Object)(object)component) && component.IsValid()) { return c.GetInventory() != null; } return false; } internal static bool Access(Container c, long playerId) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (Valid(c) && playerId != 0 && (bool)CheckAccess.Invoke(c, new object[1] { playerId }) && WardAccess(((Component)c).transform.position, playerId)) { return Compatibility.Allowed(c, playerId); } return false; } internal static bool WardAccess(Vector3 point, long playerId) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) List<PrivateArea> list = (List<PrivateArea>)Wards.GetValue(null); foreach (PrivateArea item in list) { if (Object.op_Implicit((Object)(object)item) && (bool)IsEnabled.Invoke(item, null) && (bool)IsInside.Invoke(item, new object[2] { point, 0f })) { Piece component = ((Component)item).GetComponent<Piece>(); if ((!Object.op_Implicit((Object)(object)component) || component.GetCreator() != playerId) && !(bool)IsPermitted.Invoke(item, new object[1] { playerId })) { return false; } } } return true; } internal static List<Container> Discover(Container core, long playerId) { //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) HashSet<string> hashSet = new HashSet<string>(from x in Plugin.Eligible.Value.Split(new char[1] { ',' }) select x.Trim(), StringComparer.Ordinal); float value = Plugin.Radius.Value; List<Container> list = new List<Container>(); Container[] array = All.ToArray(); foreach (Container val in array) { if (!Valid(val) || (Object)(object)val == (Object)(object)core || Object.op_Implicit((Object)(object)((Component)val).GetComponent<StorageCore>()) || Object.op_Implicit((Object)(object)((Component)val).GetComponentInParent<Ship>()) || Object.op_Implicit((Object)(object)((Component)val).GetComponentInParent<Vagon>()) || Object.op_Implicit((Object)(object)((Component)val).GetComponent<TombStone>())) { continue; } Piece component = ((Component)val).GetComponent<Piece>(); if (!Object.op_Implicit((Object)(object)component) || component.GetCreator() == 0 || !hashSet.Contains(Prefab(val))) { continue; } Vector3 val2 = ((Component)val).transform.position - ((Component)core).transform.position; if (!(((Vector3)(ref val2)).sqrMagnitude > value * value) && Access(val, playerId)) { list.Add(val); if (list.Count > 128) { throw new InvalidOperationException("More than 128 eligible chests; reduce the radius."); } } } return list.OrderBy<Container, string>(Id, StringComparer.Ordinal).ToList(); } internal static Container Find(ZDOID id) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) GameObject val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.FindInstance(id) : null); if (!Object.op_Implicit((Object)(object)val)) { return null; } return val.GetComponent<Container>(); } internal static void DemandCore(Container core