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 Player Traders v0.6.5
plugins/PlayerTraders/PlayerTraders.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using PlayerTraders.Core; using Splatform; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Rendering; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PlayerTraders")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.6.5.0")] [assembly: AssemblyInformationalVersion("0.6.5")] [assembly: AssemblyProduct("PlayerTraders")] [assembly: AssemblyTitle("PlayerTraders")] [assembly: AssemblyVersion("0.6.5.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PlayerTraders { internal static class DurableSave { public static void Save() { Check.That(!SaveSystem.HasSessionFlag((SaveSystemSessionFlags)1) && !ZNet.instance.HardSaveBlock(), "The game blocked character saving; the transaction remains in the journal"); PlayerProfile playerProfile = Game.instance.GetPlayerProfile(); playerProfile.SavePlayerData(Player.m_localPlayer); Check.That(playerProfile.Save(), "Could not save the character: the transaction is awaiting recovery"); } } [HarmonyPatch(typeof(Humanoid), "Pickup")] internal static class PausePickup { private static bool Prefix(Humanoid __instance, ref bool __result) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || !Network.Busy) { return true; } __result = false; return false; } } internal static class Items { public static Item Encode(ItemData i, int width = 10) { //IL_0108: Unknown result type (might be due to invalid IL or missing references) return new Item { Prefab = (Object.op_Implicit((Object)(object)i.m_dropPrefab) ? ((Object)i.m_dropPrefab).name : ((Object)ObjectDB.instance.GetItemPrefab(i.m_shared)).name), Count = i.m_stack, Max = i.m_shared.m_maxStackSize, Quality = i.m_quality, Variant = i.m_variant, WorldLevel = i.m_worldLevel, Durability = i.m_durability, Crafter = i.m_crafterID, CrafterName = (i.m_crafterName ?? ""), PickedUp = i.m_pickedUp, Cheated = i.m_cheated, Equipped = i.m_equipped, Data = new SortedDictionary<string, string>(i.m_customData, StringComparer.Ordinal), Slot = i.m_gridPos.y * width + i.m_gridPos.x, EquipSlot = EquipmentSlot(i.m_shared.m_itemType) }; } public static string EquipmentSlot(ItemType type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Invalid comparison between Unknown and I4 if ((int)type <= 7) { if ((int)type == 6) { return "Голова"; } if ((int)type == 7) { return "Торс"; } } else { if ((int)type == 11) { return "Ноги"; } if ((int)type == 17) { return "Плечи"; } if ((int)type == 18) { return "Аксессуар"; } } return ""; } public static bool Tradeable(string prefab) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Invalid comparison between Unknown and I4 GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefab); ItemDrop val = (Object.op_Implicit((Object)(object)itemPrefab) ? itemPrefab.GetComponent<ItemDrop>() : null); if (Object.op_Implicit((Object)(object)val) && prefab != "Coins" && !val.m_itemData.m_shared.m_questItem) { return (int)val.m_itemData.m_shared.m_itemType != 10; } return false; } public static List<Item> KnownItems() { return (from go in ObjectDB.instance.m_items where Object.op_Implicit((Object)(object)go) && Tradeable(((Object)go).name) && Player.m_localPlayer.IsKnownMaterial(go.GetComponent<ItemDrop>().m_itemData.m_shared.m_name) select Factory(((Object)go).name)).OrderBy(Name).ToList(); } public static string Description(Item item) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item.Prefab); if (!Object.op_Implicit((Object)(object)itemPrefab)) { return ""; } return Localization.instance.Localize(itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_description); } public static Item Factory(string prefab) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefab); Check.That(Object.op_Implicit((Object)(object)itemPrefab) && Object.op_Implicit((Object)(object)itemPrefab.GetComponent<ItemDrop>()), "Unknown item: " + prefab); ItemData obj = itemPrefab.GetComponent<ItemDrop>().m_itemData.Clone(); obj.m_dropPrefab = itemPrefab; obj.m_stack = 1; return Encode(obj).Part(1); } public static ItemData Decode(Item i, int width = 10) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) i.Validate(); GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(i.Prefab); Check.That(Object.op_Implicit((Object)(object)itemPrefab) && Object.op_Implicit((Object)(object)itemPrefab.GetComponent<ItemDrop>()), "Game item missing: " + i.Prefab); ItemData val = itemPrefab.GetComponent<ItemDrop>().m_itemData.Clone(); Check.That(i.Max == val.m_shared.m_maxStackSize && i.EquipSlot == EquipmentSlot(val.m_shared.m_itemType), "Item properties do not match the game"); val.m_dropPrefab = itemPrefab; val.m_stack = i.Count; val.m_quality = i.Quality; val.m_variant = i.Variant; val.m_worldLevel = i.WorldLevel; val.m_durability = i.Durability; val.m_crafterID = i.Crafter; val.m_crafterName = i.CrafterName; val.m_pickedUp = i.PickedUp; val.m_cheated = i.Cheated; val.m_equipped = i.Equipped; val.m_customData = new Dictionary<string, string>(i.Data); val.m_gridPos = new Vector2i(Math.Max(0, i.Slot) % width, Math.Max(0, i.Slot) / width); return val; } public static Bag Snapshot(Inventory inventory) { Bag bag = new Bag(); bag.Width = inventory.GetWidth(); bag.Height = inventory.GetHeight(); bag.Items = (from i in inventory.GetAllItems() select Encode(i, inventory.GetWidth()) into i orderby i.Slot select i).ToList(); bag.Validate(); return bag; } public static bool Same(Bag a, Bag b) { return Json.Write(a) == Json.Write(b); } public static void Validate(Bag bag) { bag.Validate(); foreach (Item item in bag.Items) { Decode(item, bag.Width); } } public static void Apply(Bag before, Bag after) { Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory(); Check.That(Same(Snapshot(inventory), before), "Inventory changed during the transaction; recovery required"); Check.That(after.Width == inventory.GetWidth() && after.Height == inventory.GetHeight(), "Inventory size changed"); List<ItemData> source = inventory.GetAllItems().ToList(); List<ItemData> list = new List<ItemData>(); foreach (Item i in after.Items.OrderBy((Item x) => x.Slot)) { if (i.Equipped) { ItemData val = ((IEnumerable<ItemData>)source).SingleOrDefault((Func<ItemData, bool>)((ItemData x) => x.m_equipped && x.m_gridPos.y * after.Width + x.m_gridPos.x == i.Slot)); Check.That(val != null && Json.Write(Encode(val, after.Width)) == Json.Write(i), "Player equipment changed"); list.Add(val); } else { list.Add(Decode(i, after.Width)); } } foreach (ItemData item in source.Where((ItemData x) => x.m_equipped)) { Check.That(list.Contains(item), "Cannot remove an item the player is wearing"); } inventory.GetAllItems().Clear(); inventory.GetAllItems().AddRange(list); AccessTools.Method(typeof(Inventory), "Changed", (Type[])null, (Type[])null).Invoke(inventory, new object[2] { InventoryNotice.ReceivedCheatedItem(before, after), true }); } public static string Name(Item i) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(i.Prefab); if (!Object.op_Implicit((Object)(object)itemPrefab)) { return i.Prefab; } return Localization.instance.Localize(itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_name); } public static Sprite Icon(Item i) { return Decode(i).GetIcon(); } } internal static class NativeInventoryMove { public static Inventory Mirror(Bag bag, bool strictStacks = false) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected O, but got Unknown Inventory val = new Inventory("PlayerTraders", (Sprite)null, bag.Width, bag.Height); AccessTools.Field(typeof(Inventory), "m_temoraryInventory").SetValue(val, true); foreach (Item item in bag.Items) { ItemData val2 = Items.Decode(item, bag.Width); if (strictStacks) { val2.m_shared = (SharedData)AccessTools.Method(typeof(object), "MemberwiseClone", (Type[])null, (Type[])null).Invoke(val2.m_shared, null); SharedData shared = val2.m_shared; shared.m_name = shared.m_name + "#" + item.Key; } val.GetAllItems().Add(val2); } return val; } public static void Apply(Bag player, Bag stock, Request r, bool fillFromTop = false) { //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Expected O, but got Unknown //IL_025f: Unknown result type (might be due to invalid IL or missing references) string text = Json.Write(new Bag[2] { player, stock }); Inventory val = Mirror(player, strictStacks: true); Inventory val2 = Mirror(stock, strictStacks: true); Inventory val3 = ((r.Source == "player") ? val : val2); Inventory val4 = ((r.Target == "player") ? val : val2); ItemData itemAt = val3.GetItemAt(r.Slot % val3.GetWidth(), r.Slot / val3.GetWidth()); Check.That(val3 == val4 || !itemAt.m_shared.m_questItem, "This item cannot be given to a trader"); if (r.TargetSlot < 0) { Check.That(val3 != val4, "Choose another slot"); Check.That(r.Count == itemAt.m_stack, "Not enough items"); if (!fillFromTop) { val4.MoveItemToThis(val3, itemAt); } else { for (int i = 0; i < 2; i++) { if (itemAt.m_stack <= 0) { break; } for (int j = 0; j < val4.GetHeight(); j++) { if (itemAt.m_stack <= 0) { break; } for (int k = 0; k < val4.GetWidth() && itemAt.m_stack > 0; k++) { ItemData itemAt2 = val4.GetItemAt(k, j); bool num; if (i != 0) { num = itemAt2 == null; } else { if (itemAt2 == null || !(itemAt2.m_shared.m_name == itemAt.m_shared.m_name)) { continue; } num = itemAt2.m_stack < itemAt2.m_shared.m_maxStackSize; } if (num) { val4.MoveItemToThis(val3, itemAt, itemAt.m_stack, k, j); } } } } } } else { Vector2i val5 = default(Vector2i); ((Vector2i)(ref val5))..ctor(r.TargetSlot % val4.GetWidth(), r.TargetSlot / val4.GetWidth()); ItemData itemAt3 = val4.GetItemAt(val5.x, val5.y); Check.That(val3 == val4 || itemAt3 == null || !itemAt3.m_shared.m_questItem, "This item cannot be given to a trader"); GameObject val6 = new GameObject("PT_InventoryCalculation"); val6.SetActive(false); try { InventoryGrid val7 = val6.AddComponent<InventoryGrid>(); AccessTools.Field(typeof(InventoryGrid), "m_inventory").SetValue(val7, val4); val7.DropItem(val3, itemAt, r.Count, val5); } finally { Object.Destroy((Object)(object)val6); } } player.Items = Items.Snapshot(val).Items; stock.Items = Items.Snapshot(val2).Items; Check.That(text != Json.Write(new Bag[2] { player, stock }), "No space to move the item"); } public static void Place(Bag target, Item item, int slot) { Check.That(slot >= 0 && slot < target.Capacity, "Duplicate inventory slot"); Check.That(target.Items.All((Item i) => i.Slot != slot), "Choose an empty slot"); Bag bag = new Bag { Width = 1, Height = 1 }; Item item2 = Json.Copy(item); item2.Slot = 0; bag.Items.Add(item2); Bag beforePlayer = Json.Copy(target); Bag beforeStock = Json.Copy(bag); Apply(target, bag, new Request { Source = "stock", Target = "player", Slot = 0, TargetSlot = slot, Count = 1 }); Check.That(bag.Items.Count == 0, "Not enough space"); MoveGuard.Verify(beforePlayer, beforeStock, target, bag); } } internal sealed class Wire { public string Kind; public string Text = ""; public string Session = ""; public string Actor = ""; public string Id = ""; public long World; public long Sequence; public Dictionary<string, int> Cost; public ShopView View; public ClientTransfer Transfer; public float[] Position; } internal sealed class ClientTransfer { public string Id; public string Phase; public string Trader; public long Sequence; public Bag Before; public Bag After; } internal sealed class ClientJournal { public long World; public Request Request; public ClientTransfer Transfer; public bool Applied; } internal static class Network { private const string Method = "PT_Request_v1"; private const string Reply = "PT_Reply_v1"; public static Ledger Host; private static Sessions sessions = new Sessions(); private static ZNet current; private static string hostError; private static readonly Dictionary<string, double> requests = new Dictionary<string, double>(); private static readonly Queue<Action> localQueue = new Queue<Action>(); private static readonly HashSet<string> actions = new HashSet<string> { "hello", "ready", "applied", "abort", "close", "open", "heartbeat", "create", "dismantle", "move", "prices", "order", "removeOrder", "sell", "fund", "buy", "cash", "look", "equipPlayer", "unequip" }; private static double nextTick; private static double nextHello; private static double nextHeartbeat; private static double lastReply; private static double nextPrune; public static long World; public static long Sequence; public static string Actor = ""; public static string Session = ""; public static string OpenId = ""; private static string pendingOpenId = ""; private static Store clientStore; private static ClientJournal journal; private static bool clientBlocked; public static bool Busy { get { if (journal != null) { if (journal.Request == null) { return journal.Transfer != null; } return true; } return false; } } public static bool BlockInput { get { if (Busy && !clientBlocked && Now - lastReply < 12.0 && Object.op_Implicit((Object)(object)Player.m_localPlayer) && !((Character)Player.m_localPlayer).IsDead() && Object.op_Implicit((Object)(object)ZNet.instance)) { if (!ZNet.instance.IsServer()) { ZRpc serverRPC = ZNet.instance.GetServerRPC(); if (serverRPC == null || !serverRPC.IsConnected()) { goto IL_006c; } } return true; } goto IL_006c; IL_006c: return TraderUI.Visible; } } public static string LocalActor => Canonical(((IUser)PlatformManager.DistributionPlatform.LocalUser).PlatformUserID); private static double Now => Time.realtimeSinceStartupAsDouble; private static string DataRoot => Path.Combine(Paths.ConfigPath, "PlayerTraders"); private unsafe static string Canonical(PlatformUserID id) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) Check.That(((PlatformUserID)(ref id)).IsValid, "Invalid platform ID"); Platform platform = id.m_platform; return ((object)(*(Platform*)(&platform))/*cast due to .constrained prefix*/).ToString().ToLowerInvariant() + ":" + id.m_userID; } private static string PeerActor(ZNetPeer peer) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) string hostName = peer.m_socket.GetHostName(); return Canonical((peer.m_socket is ZSteamSocket) ? new PlatformUserID("Steam", hostName) : new PlatformUserID(hostName)); } public static void Register(ZNetPeer peer) { peer.m_rpc.Register<ZPackage>("PT_Request_v1", (Action<ZRpc, ZPackage>)delegate(ZRpc rpc, ZPackage package) { Receive(rpc, package); }); peer.m_rpc.Register<ZPackage>("PT_Reply_v1", (Action<ZRpc, ZPackage>)delegate(ZRpc rpc, ZPackage package) { if (!Object.op_Implicit((Object)(object)ZNet.instance) || ZNet.instance.IsServer() || ZNet.instance.GetServerRPC() != rpc) { return; } try { Accept(Parse<Wire>(package)); } catch (Exception e) { Fail(e); } }); } private static T Parse<T>(ZPackage package) { return PacketCodec.Read<T>(package); } private static ZPackage Package(object value) { return PacketCodec.Create(value); } private static void SendTo(ZRpc rpc, Wire value) { ZPackage p = Package(value); if (rpc == null) { localQueue.Enqueue(delegate { Accept(Parse<Wire>(p)); }); } else { rpc.Invoke("PT_Reply_v1", new object[1] { p }); } } public static void Send(Request request) { if (!Object.op_Implicit((Object)(object)ZNet.instance)) { return; } if (ZNet.instance.IsServer()) { localQueue.Enqueue(delegate { Receive(null, Package(request)); }); return; } ZRpc serverRPC = ZNet.instance.GetServerRPC(); if (serverRPC == null || !serverRPC.IsConnected()) { OpenId = ""; Session = ""; TraderUI.Close(send: false); TraderUI.Notify("Connection lost"); } else { serverRPC.Invoke("PT_Request_v1", new object[1] { Package(request) }); } } public static void Reset() { TraderUI.Close(send: false); current = null; Host = null; hostError = null; sessions = new Sessions(); requests.Clear(); localQueue.Clear(); World = 0L; Sequence = 0L; Actor = (Session = (OpenId = (pendingOpenId = ""))); journal = null; clientStore = null; clientBlocked = false; nextTick = (nextHello = (nextHeartbeat = (lastReply = (nextPrune = 0.0)))); WorldDrops.Reset(); WorldObjects.Reset(); Plugin.ResetConfiguration(); } public static void Tick() { //IL_02d3: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Unknown result type (might be due to invalid IL or missing references) if (current != ZNet.instance) { Reset(); current = ZNet.instance; } if (localQueue.Count > 0) { try { localQueue.Dequeue()(); } catch (Exception e) { Fail(e); } } if (!Object.op_Implicit((Object)(object)current) || Now < nextTick) { return; } nextTick = Now + 1.0; if (Now >= nextPrune) { nextPrune = Now + 60.0; string[] array = (from p in requests where Now - p.Value > 60.0 select p.Key).ToArray(); foreach (string key in array) { requests.Remove(key); } } if (current.IsServer() && Host == null && hostError == null) { if (!Object.op_Implicit((Object)(object)ZNetScene.instance) || ZDOMan.instance == null || !Object.op_Implicit((Object)(object)ObjectDB.instance) || !Object.op_Implicit((Object)(object)WorldObjects.TraderPrefab)) { return; } try { Plugin.LoadServerConfiguration(); long worldUID = current.GetWorldUID(); Check.That(worldUID != 0, "World ID is unavailable"); string text = Path.Combine(DataRoot, "worlds", worldUID + ".json"); Store store = new Store(text); Host = new Ledger(store, worldUID); if (!File.Exists(text)) { store.Save(Host.Data); } WorldObjects.Reconcile(Host.Data); Plugin.LogInfo("Trader registry ready: " + text + "; traders: " + Host.Data.Traders.Count); } catch (Exception ex) { Host = null; hostError = "Trader storage is locked: " + ex.Message; Plugin.Error(ex); } } if (Host != null) { WorldDrops.Tick(); sessions.Expire(Now); sessions.Prune((string id, string actor) => Host.Data.Traders.TryGetValue(id, out var value) && AliveNear(actor, value.Position)); } if (Plugin.Headless) { return; } if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || ((Character)Player.m_localPlayer).IsDead()) { TraderUI.Close(); return; } TraderMap.Tick(); if (OpenId.Length > 0 && TraderUI.Visible) { TraderNPC traderNPC = TraderNPC.Find(OpenId); if (!Object.op_Implicit((Object)(object)traderNPC) || Vector3.Distance(((Component)traderNPC).transform.position, ((Component)Player.m_localPlayer).transform.position) > 5f || Now - lastReply > 12.0) { TraderUI.Close(); TraderUI.Notify("Connection lost or trader too far away"); } else if (Now >= nextHeartbeat) { nextHeartbeat = Now + 3.0; Send(new Request { Action = "heartbeat", Trader = OpenId, Session = Session }); } } if ((World == 0L || Busy) && Now >= nextHello) { nextHello = Now + 5.0; Send(new Request { Action = "hello", Id = (journal?.Request?.Id ?? journal?.Transfer?.Id ?? "") }); } } private static (string actor, string name, Vector3 position, long character) Identity(ZRpc rpc) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) if (rpc == null) { Check.That(ZNet.instance.IsServer() && Object.op_Implicit((Object)(object)Player.m_localPlayer) && !((Character)Player.m_localPlayer).IsDead(), "Player unavailable"); return (actor: LocalActor, name: Player.m_localPlayer.GetPlayerName(), position: ((Component)Player.m_localPlayer).transform.position, character: CharacterAccess.Require(Player.m_localPlayer.GetPlayerID())); } ZNetPeer val = ((IEnumerable<ZNetPeer>)ZNet.instance.GetPeers()).SingleOrDefault((Func<ZNetPeer, bool>)((ZNetPeer x) => x.m_rpc == rpc)); Check.That(val != null && val.IsReady() && rpc.IsConnected(), "Connection not confirmed"); string text = PeerActor(val); Check.That(!string.IsNullOrWhiteSpace(text) && text.Length <= 256, "Connection owner is unavailable"); ZDO zDO = ZDOMan.instance.GetZDO(val.m_characterID); Check.That(zDO != null && !zDO.GetBool("dead", false), "Character unavailable"); long item = CharacterAccess.Require(zDO.GetLong(ZDOVars.s_playerID, 0L)); return (actor: text, name: val.m_playerName, position: zDO.GetPosition(), character: item); } private static bool AliveNear(string actor, float[] position) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) try { if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && actor == LocalActor) { return !((Character)Player.m_localPlayer).IsDead() && Vector3.Distance(((Component)Player.m_localPlayer).transform.position, Vec(position)) <= 5f; } ZNetPeer val = ((IEnumerable<ZNetPeer>)ZNet.instance.GetPeers()).FirstOrDefault((Func<ZNetPeer, bool>)((ZNetPeer p) => p.IsReady() && PeerActor(p) == actor)); if (val == null) { return false; } ZDO zDO = ZDOMan.instance.GetZDO(val.m_characterID); return zDO != null && !zDO.GetBool("dead", false) && Vector3.Distance(zDO.GetPosition(), Vec(position)) <= 5f; } catch { return false; } } public static Vector3 Vec(float[] p) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) Check.That(p != null && p.Length == 3 && p.All((float x) => !float.IsNaN(x) && !float.IsInfinity(x)), "Invalid position"); return new Vector3(p[0], p[1], p[2]); } private static ClientTransfer ClientPart(Transfer p) { return new ClientTransfer { Id = p.Id, Sequence = p.Sequence, Phase = p.Phase, Before = p.PlayerBefore, After = p.PlayerAfter, Trader = p.After.Id }; } private static ShopView View(Trader t, string actor, long character) { return ShopView.For(t, actor, character); } private static void Receive(ZRpc rpc, ZPackage package) { //IL_034e: Unknown result type (might be due to invalid IL or missing references) //IL_035a: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_02d7: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_05fa: Unknown result type (might be due to invalid IL or missing references) //IL_062c: Unknown result type (might be due to invalid IL or missing references) //IL_09df: Unknown result type (might be due to invalid IL or missing references) //IL_09ef: Unknown result type (might be due to invalid IL or missing references) //IL_0a18: Unknown result type (might be due to invalid IL or missing references) //IL_0aa2: Unknown result type (might be due to invalid IL or missing references) //IL_0aae: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer()) { return; } string text = ""; string key = ""; try { (string, string, Vector3, long) tuple = Identity(rpc); key = tuple.Item1; Request r = Parse<Request>(package); text = r.Id; Check.That(hostError == null && Host != null, hostError ?? "The world is still loading"); Host.Data.Pending.TryGetValue(key, out var value); CharacterAccess.CheckPending(value, tuple.Item4); Check.That(r.Action != null && actions.Contains(r.Action) && (r.Trader?.Length ?? 0) <= 64 && (r.Session?.Length ?? 0) <= 64 && (r.Id?.Length ?? 0) <= 64 && (r.Key?.Length ?? 0) <= 128, "Invalid request"); string key2 = tuple.Item1 + ":" + r.Action; Check.That(!requests.TryGetValue(key2, out var value2) || Now - value2 >= 0.12, "Too many requests"); requests[key2] = Now; if (r.Action == "dismantle") { if (rpc == null && !Host.Data.Traders.ContainsKey(r.Trader) && !Host.Data.Drops.ContainsKey(r.Trader) && WorldObjects.RemoveOrphan(r.Trader, tuple.Item3, out var removedPosition)) { SendTo(rpc, new Wire { Kind = "removed", Id = r.Trader, Position = new float[3] { removedPosition.x, removedPosition.y, removedPosition.z }, Text = "Removed a trader with missing data. Its belongings are not in the registry." }); return; } bool flag = false; DropPlan dropPlan; if (Host.Data.Drops.TryGetValue(r.Trader, out var value3)) { Check.That(value3.Owner == tuple.Item1 && value3.OwnerCharacter == tuple.Item4 && Vector3.Distance(tuple.Item3, Vec(value3.Position)) <= 5f, "Only the owner can do this"); dropPlan = value3; } else { Check.That(Host.Data.Traders.TryGetValue(r.Trader, out var value4), "Trader not found"); Check.That(value4.IsOwner(tuple.Item1, tuple.Item4) && Vector3.Distance(tuple.Item3, Vec(value4.Position)) <= 5f, "Only the owner can do this"); string token = sessions.Open(r.Trader, tuple.Item1, Now); sessions.Check(r.Trader, tuple.Item1, token, Now); WorldObjects.ValidateDrops(DropPlan.From(value4)); dropPlan = Host.Dismantle(r.Trader, tuple.Item1, r.Revision, tuple.Item4); flag = true; } WorldObjects.ApplyDrops(dropPlan); sessions.Close(r.Trader, tuple.Item1); SendTo(rpc, new Wire { Kind = "removed", Id = r.Trader, Position = (flag ? dropPlan.Position : null) }); return; } if (r.Action == "hello") { Host.Data.Receipts.TryGetValue(tuple.Item1, out var value5); Host.Data.Pending.TryGetValue(tuple.Item1, out var value6); SendTo(rpc, new Wire { Kind = "hello", Actor = tuple.Item1, World = Host.Data.World, Sequence = (value5?.Sequence ?? 0), Id = (value5?.Id ?? ""), Cost = Plugin.Cost, Transfer = ((value6 == null) ? null : ClientPart(value6)) }); return; } if (r.Action == "ready" || r.Action == "applied" || r.Action == "abort") { if (r.Action == "ready") { Check.That(Host.Data.Pending.TryGetValue(tuple.Item1, out var value7) && value7.Id == r.Id, "Transaction not found"); if (value7.Phase == "prepared" && Vector3.Distance(tuple.Item3, Vec((value7.Action == "create") ? value7.After.Position : value7.Before.Position)) > 5f) { Host.Abort(tuple.Item1, r.Id); SendTo(rpc, new Wire { Kind = "aborted", Id = r.Id, Sequence = Host.Data.Receipts[tuple.Item1].Sequence }); } else { Transfer transfer = Host.Commit(tuple.Item1, r.Id); WorldObjects.Apply(transfer.After); SendTo(rpc, new Wire { Kind = "commit", Transfer = ClientPart(transfer) }); } } else if (r.Action == "abort") { Host.Abort(tuple.Item1, r.Id); SendTo(rpc, new Wire { Kind = "done", Id = r.Id, Sequence = Host.Data.Receipts[tuple.Item1].Sequence }); } else { Host.Data.Pending.TryGetValue(tuple.Item1, out var value8); Host.Applied(tuple.Item1, r.Id); Wire wire = new Wire { Kind = "done", Id = r.Id, Sequence = Host.Data.Receipts[tuple.Item1].Sequence }; if (value8 != null && value8.After.Place == PlaceState.Placed) { wire.View = View(value8.After, tuple.Item1, tuple.Item4); } SendTo(rpc, wire); } return; } if (r.Action == "close") { sessions.Close(r.Trader, tuple.Item1); return; } if (!string.IsNullOrEmpty(r.Id) && Host.Data.Pending.TryGetValue(tuple.Item1, out var value9) && value9.Id == r.Id) { SendTo(rpc, new Wire { Kind = ((value9.Phase == "prepared") ? "prepare" : "commit"), Transfer = ClientPart(value9) }); return; } if (!string.IsNullOrEmpty(r.Id) && Host.Data.Receipts.TryGetValue(tuple.Item1, out var value10) && value10.Id == r.Id) { SendTo(rpc, new Wire { Kind = "done", Id = r.Id, Sequence = value10.Sequence }); return; } Trader value11 = null; if (r.Action != "create") { Check.That(Host.Data.Traders.TryGetValue(r.Trader, out value11), "Trader not found"); } if (r.Action == "create") { Check.That(Vector3.Distance(tuple.Item3, Vec(r.Position)) <= 5f, "Too far away to build"); Check.That(!Location.IsInsideNoBuildLocation(Vec(r.Position)), "Building is not allowed here"); Check.That(Math.Abs(r.Position[0]) < 20000f && Math.Abs(r.Position[2]) < 20000f && r.Position[1] > -1000f && r.Position[1] < 10000f, "Invalid placement"); } else { Check.That(value11.Place == PlaceState.Placed && Vector3.Distance(tuple.Item3, Vec(value11.Position)) <= 5f, "Too far from the trader"); } if (r.Action == "open") { Check.That(!Host.Data.Pending.Values.Any((Transfer p) => p.After.Id == r.Trader), "The trader has an unfinished transaction"); string session = sessions.Open(r.Trader, tuple.Item1, Now); SendTo(rpc, new Wire { Kind = "view", Session = session, View = View(value11, tuple.Item1, tuple.Item4) }); return; } if (r.Action != "create") { sessions.Check(r.Trader, tuple.Item1, r.Session, Now); } if (r.Action == "heartbeat") { SendTo(rpc, new Wire { Kind = "heartbeat" }); return; } Items.Validate(r.Inventory); if (r.Action == "look") { WorldObjects.ValidateLook(r.Look); } if (r.Action == "order") { Check.That(Items.Tradeable(r.Key), "This item cannot be purchased"); } if (r.Action == "sell") { Check.That(Items.Tradeable(r.Key), "This item cannot be given to a trader"); } Transfer transfer2 = new Rules(Items.Factory) { NativeMove = NativeInventoryMove.Apply, NativePlace = NativeInventoryMove.Place, ConstructionCost = Plugin.Cost }.Prepare(Host.Data, tuple.Item1, tuple.Item2, r, (r.Action == "create") ? WorldObjects.RandomLook() : null, tuple.Item4); Package(new Wire { Kind = "prepare", Transfer = ClientPart(transfer2) }); Host.Prepare(transfer2); SendTo(rpc, new Wire { Kind = "prepare", Transfer = ClientPart(transfer2) }); } catch (Exception ex) { if (!(ex is RuleException)) { Plugin.Error(ex); } Receipt value12; bool flag2 = !string.IsNullOrEmpty(text) && Host != null && !Host.Data.Pending.ContainsKey(key) && (!Host.Data.Receipts.TryGetValue(key, out value12) || value12.Id != text); SendTo(rpc, new Wire { Kind = (flag2 ? "rejected" : "error"), Id = text, Text = ((ex is RuleException) ? ex.Message : "Operation failed. Check the log; no duplicate items were delivered.") }); } } public static void Open(string id) { if (Busy || clientBlocked) { TraderUI.Notify("Recover the unfinished transaction first"); return; } pendingOpenId = id; Send(new Request { Action = "open", Trader = id }); } private static void RefreshOpenView(ShopView updated = null) { if (TraderUI.Visible && !Busy && OpenId.Length != 0) { if (updated != null && updated.Id == OpenId) { TraderUI.Show(updated); } else { Open(OpenId); } } } public static void Close() { if (OpenId.Length > 0) { Send(new Request { Action = "close", Trader = OpenId, Session = Session }); } OpenId = ""; Session = ""; pendingOpenId = ""; } public static void Operate(Request r) { try { Check.That(World != 0L && clientStore != null, "The trader is still loading. Please try again in a moment."); Check.That(!clientBlocked, "Wait for the connection or transaction recovery"); Check.That(!Busy, "Wait for the operation to finish"); r.Id = Guid.NewGuid().ToString("N"); r.Sequence = checked(Sequence + 1); r.Session = Session; r.Inventory = Items.Snapshot(((Humanoid)Player.m_localPlayer).GetInventory()); Package(r); journal = new ClientJournal { World = World, Request = r }; clientStore.Save(journal); Send(r); } catch (Exception e) { Fail(e); } } private static void Accept(Wire wire) { lastReply = Now; try { if (wire.Kind == "hello") { if (World == 0L) { World = wire.World; Actor = wire.Actor; clientStore = new Store(Path.Combine(DataRoot, "clients", Json.Hash(Actor + ":" + Player.m_localPlayer.GetPlayerID()) + "_" + World + ".json")); if (File.Exists(clientStore.Path)) { journal = clientStore.Load<ClientJournal>(); Check.That(journal.World == World, "The journal belongs to another world"); } else { Check.That(!File.Exists(clientStore.Path + ".bak") && !File.Exists(clientStore.Path + ".tmp"), "Client journal missing; a backup was found"); } } Check.That(World == wire.World && Actor == wire.Actor, "The world or connection changed"); Sequence = wire.Sequence; if (wire.Cost != null) { WorldObjects.SetBuildCost(wire.Cost); } if (wire.Transfer != null) { ProcessTransfer(wire.Transfer); } else if (Busy) { string text = journal.Request?.Id ?? journal.Transfer?.Id; if (wire.Id == text) { Finish(text, wire.Sequence); RefreshOpenView(); } else { if (journal.Transfer != null) { throw new RuleException("The host does not recognise the recorded transaction: a save rollback may have occurred. Automatic recovery blocked."); } Check.That(Items.Same(Items.Snapshot(((Humanoid)Player.m_localPlayer).GetInventory()), journal.Request.Inventory), "Inventory changed before confirmation. Transaction blocked for inspection."); Send(journal.Request); } } } else if (wire.Kind == "view") { if (wire.View.Id != pendingOpenId && wire.View.Id != OpenId) { return; } pendingOpenId = ""; OpenId = wire.View.Id; Session = wire.Session; TraderUI.Show(wire.View); } else if (wire.Kind == "removed") { if (OpenId == wire.Id) { TraderUI.Close(); } if (wire.Position != null) { WorldObjects.HammerEffects(wire.Position, 0f, removed: true); } TraderUI.Notify(string.IsNullOrEmpty(wire.Text) ? "Trader dismantled" : wire.Text); } else if (wire.Kind == "prepare" || wire.Kind == "commit") { ProcessTransfer(wire.Transfer); } else if (wire.Kind == "done") { Finish(wire.Id, wire.Sequence); RefreshOpenView(wire.View); } else if (wire.Kind == "aborted") { if (Busy && (journal.Request?.Id ?? journal.Transfer?.Id) == wire.Id) { journal = new ClientJournal { World = World }; Sequence = wire.Sequence; clientStore.Save(journal); TraderUI.Notify("You moved away from the trader. The operation was cancelled; your items are unchanged."); } } else if (wire.Kind == "rejected") { if (Busy && journal.Transfer == null && journal.Request.Id == wire.Id) { Request request = journal.Request; Plugin.Warn("Operation rejected: " + request.Action + " " + request.Source + ":" + request.Slot + " → " + request.Target + ":" + request.TargetSlot + "; " + wire.Text); journal = new ClientJournal { World = World }; clientStore.Save(journal); RefreshOpenView(); } TraderUI.Notify(wire.Text); } else if (wire.Kind == "error") { TraderUI.Notify(wire.Text); if (!Busy && OpenId.Length > 0 && wire.Text.Contains("Session")) { TraderUI.Close(); } if (Busy && journal.Transfer == null && wire.Id == journal.Request.Id) { Send(new Request { Action = "hello", Id = wire.Id }); } } } catch (Exception e) { if (wire.Kind == "prepare" || wire.Kind == "commit" || wire.Kind == "hello" || wire.Kind == "done") { clientBlocked = true; } Fail(e); } TraderUI.RefreshInput(); } private static void ProcessTransfer(ClientTransfer tx) { if (tx.Sequence > Sequence) { Check.That(!clientBlocked, "Recovery blocked; see the log"); Check.That(!Busy || (journal.Request?.Id ?? journal.Transfer?.Id) == tx.Id, "Another transaction is awaiting recovery"); if (journal == null) { journal = new ClientJournal { World = World }; } journal.Transfer = tx; clientStore.Save(journal); string key = "PT.receipt." + World; Player localPlayer = Player.m_localPlayer; Bag a = Items.Snapshot(((Humanoid)localPlayer).GetInventory()); string value; switch (Recovery.Decide(tx.Phase, localPlayer.m_customData.TryGetValue(key, out value) && value == tx.Id, Items.Same(a, tx.Before))) { case RecoveryStep.Acknowledge: Check.That(tx.Phase == "committed", "The character receipt does not match the host journal"); DurableSave.Save(); journal.Applied = true; clientStore.Save(journal); Send(new Request { Action = "applied", Id = tx.Id }); break; case RecoveryStep.Abort: journal.Transfer = null; journal.Request = new Request { Id = tx.Id, Sequence = tx.Sequence, Inventory = tx.Before, Action = "abort" }; clientStore.Save(journal); Send(new Request { Action = "abort", Id = tx.Id }); break; case RecoveryStep.Ready: Send(new Request { Action = "ready", Id = tx.Id }); break; default: Items.Apply(tx.Before, tx.After); localPlayer.m_customData[key] = tx.Id; DurableSave.Save(); journal.Applied = true; clientStore.Save(journal); Send(new Request { Action = "applied", Id = tx.Id }); break; } } } private static void Finish(string id, long sequence) { if (Busy && sequence <= Sequence && (journal.Request?.Id ?? journal.Transfer?.Id) != id) { return; } if (!Busy) { Sequence = Math.Max(Sequence, sequence); return; } Check.That((journal.Request?.Id ?? journal.Transfer?.Id) == id, "Transaction receipt mismatch"); if (journal.Transfer != null) { Check.That(Player.m_localPlayer.m_customData.TryGetValue("PT.receipt." + World, out var value) && value == id, "Character receipt missing; recovery required"); } if (journal.Request != null) { TraderUI.Traded(journal.Request.Trader, journal.Request.Action); } Request request = ((journal.Applied && journal.Request != null && journal.Request.Action == "create") ? journal.Request : null); Sequence = sequence; journal = new ClientJournal { World = World }; clientStore.Save(journal); TraderUI.Notify("Done"); if (request != null) { WorldObjects.HammerEffects(request.Position, request.Yaw, removed: false); } } public static void Fail(Exception e) { if (!(e is RuleException)) { Plugin.Error(e); } TraderUI.Notify((e is RuleException) ? e.Message : ("Error: " + e.Message)); } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] internal static class RegisterRpc { private static void Postfix(ZNetPeer peer) { Network.Register(peer); } } internal static class PacketCodec { public const int Limit = 1048576; public static ZPackage Create(object value) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown string text = Json.Write(value); Check.That(Encoding.UTF8.GetByteCount(text) < 1048544, "Too much custom item data"); ZPackage val = new ZPackage(); val.Write(text); val.SetPos(0); return val; } public static T Read<T>(ZPackage package) { Check.That(package.Size() > 0 && package.Size() <= 1048576, "The network message is too large"); package.SetPos(0); try { return Json.Read<T>(package.ReadString()); } catch (EndOfStreamException) { throw new RuleException("Could not read trader network data. Please try again."); } } } internal static class TraderAppearance { public static void Write(ZDO z, Look look, Dictionary<string, Item> equipment) { Publish(look, equipment, delegate(int key, int value) { z.Set(key, value, false); }, delegate(int key, Vector3 value) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) z.Set(key, value); }); } internal static void Publish(Look look, Dictionary<string, Item> equipment, Action<int, int> number, Action<int, Vector3> color) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) number(ZDOVars.s_modelIndex, look.Body); color(ZDOVars.s_skinColor, new Vector3(look.Skin[0], look.Skin[1], look.Skin[2])); color(ZDOVars.s_hairColor, new Vector3(look.HairColor[0], look.HairColor[1], look.HairColor[2])); number(ZDOVars.s_hairItem, Hash(look.Hair)); number(ZDOVars.s_beardItem, Hash(look.Beard)); number(ZDOVars.s_helmetItem, Hash(Slot("Голова")?.Prefab)); number(ZDOVars.s_chestItem, Hash(Slot("Торс")?.Prefab)); number(ZDOVars.s_legItem, Hash(Slot("Ноги")?.Prefab)); number(ZDOVars.s_utilityItem, Hash(Slot("Аксессуар")?.Prefab)); Item item = Slot("Плечи"); number(ZDOVars.s_shoulderItem, Hash(item?.Prefab)); number(ZDOVars.s_shoulderItemVariant, item?.Variant ?? 0); number(ZDOVars.s_shoulderItemQuality, item?.Quality ?? 1); static int Hash(string prefab) { if (!string.IsNullOrEmpty(prefab)) { return StringExtensionMethods.GetStableHashCode(prefab); } return 0; } Item Slot(string slot) { if (equipment == null || !equipment.TryGetValue(slot, out var value)) { return null; } return value; } } } internal static class TraderMap { private sealed class Entry { public ZDOID Id; public string Name; public Vector3 Position; public PinData Pin; } private static readonly Dictionary<ZDOID, Entry> known = new Dictionary<ZDOID, Entry>(); private static Minimap map; private static Sprite icon; public static void Observe(ZDO z, string name) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) if (z == null || z.GetBool("PT.dropped", false) || !Object.op_Implicit((Object)(object)Player.m_localPlayer)) { return; } if (!known.TryGetValue(z.m_uid, out var value)) { Dictionary<ZDOID, Entry> dictionary = known; ZDOID uid = z.m_uid; Entry obj = new Entry { Id = z.m_uid }; value = obj; dictionary.Add(uid, obj); } if (value.Name != name && value.Pin != null) { if (Object.op_Implicit((Object)(object)map)) { map.RemovePin(value.Pin); } value.Pin = null; } value.Name = name; value.Position = z.GetPosition(); } public static void Tick() { //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)Minimap.instance) || !Object.op_Implicit((Object)(object)Player.m_localPlayer)) { return; } if ((Object)(object)map != (Object)(object)Minimap.instance) { if (Object.op_Implicit((Object)(object)map)) { Reset(); } map = Minimap.instance; } if (known.Count == 0) { return; } if (!Object.op_Implicit((Object)(object)icon)) { icon = map.m_locationIcons.Select((LocationSpriteData x) => x.m_icon).FirstOrDefault((Func<Sprite, bool>)((Sprite s) => Object.op_Implicit((Object)(object)s) && ((Object)s).name == "mapicon_trader")); } if (!Object.op_Implicit((Object)(object)icon)) { return; } foreach (Entry value in known.Values) { if (value.Pin == null) { value.Pin = map.AddPin(value.Position, (PinType)8, value.Name, false, false, 0L, default(PlatformUserID)); value.Pin.m_icon = icon; value.Pin.m_doubleSize = true; } } } public static void Remove(ZDOID id) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (known.TryGetValue(id, out var value)) { if (Object.op_Implicit((Object)(object)map) && value.Pin != null) { map.RemovePin(value.Pin); } known.Remove(id); } } public static void Reset() { if (Object.op_Implicit((Object)(object)map)) { foreach (Entry value in known.Values) { if (value.Pin != null) { map.RemovePin(value.Pin); } } } known.Clear(); map = null; icon = null; } } [HarmonyPatch(typeof(ZNetScene), "OnZDODestroyed")] internal static class RemoveTraderMapPin { private static void Prefix(ZDO __0) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) TraderMap.Remove(__0.m_uid); } } public sealed class TraderMaterials : MonoBehaviour { private Material[] owned; internal void Initialize(Renderer body) { if (owned == null && Object.op_Implicit((Object)(object)body)) { owned = body.materials; } } private void OnDestroy() { if (owned != null) { Material[] array = owned; foreach (Material val in array) { if (Object.op_Implicit((Object)(object)val)) { Object.Destroy((Object)(object)val); } } } owned = null; } } [HarmonyPatch(typeof(VisEquipment), "Awake")] internal static class TraderMaterialOwnership { private static bool Prefix(VisEquipment __instance) { if (!Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<TraderNPC>())) { return true; } if (Plugin.Headless) { return false; } if (!Object.op_Implicit((Object)(object)__instance.m_bodyModel)) { return true; } (((Component)__instance).gameObject.GetComponent<TraderMaterials>() ?? ((Component)__instance).gameObject.AddComponent<TraderMaterials>()).Initialize((Renderer)(object)__instance.m_bodyModel); return true; } } public sealed class TraderOfferMarker : MonoBehaviour { private const float VisibilityDistance = 50f; private static Sprite sprite; private Transform face; private void Awake() { ((Behaviour)this).enabled = false; } internal void Refresh(Appearance appearance) { //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Expected O, but got Unknown //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; bool flag = false; if (Object.op_Implicit((Object)(object)localPlayer) && !((Character)localPlayer).IsDead() && Network.World != 0L && appearance != null) { Vector3 val = ((Component)localPlayer).transform.position - ((Component)this).transform.position; if (((Vector3)(ref val)).sqrMagnitude <= 2500f) { localPlayer.m_customData.TryGetValue(OfferNews.Key(Network.World, appearance.Id), out var value); flag = OfferNews.Unread(appearance.OfferRevision, value); } } if (flag && !Object.op_Implicit((Object)(object)face)) { if (!Object.op_Implicit((Object)(object)sprite)) { Texture2D val2 = NativeWidgets.LoadTexture("offer-new.png"); sprite = Sprite.Create(val2, new Rect(0f, 0f, (float)((Texture)val2).width, (float)((Texture)val2).height), new Vector2(0.5f, 0.5f), (float)((Texture)val2).height / 0.7f); } GameObject val3 = new GameObject("NewTradeOffers"); face = val3.transform; face.SetParent(((Component)this).transform, false); face.localPosition = new Vector3(0f, 2.55f, 0f); SpriteRenderer obj = val3.AddComponent<SpriteRenderer>(); obj.sprite = sprite; ((Renderer)obj).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)obj).receiveShadows = false; } if (Object.op_Implicit((Object)(object)face)) { ((Component)face).gameObject.SetActive(flag); } ((Behaviour)this).enabled = flag; } private void LateUpdate() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) Camera main = Camera.main; if (Object.op_Implicit((Object)(object)face) && Object.op_Implicit((Object)(object)main)) { face.rotation = ((Component)main).transform.rotation; } } internal static void Viewed(ShopView view) { Player localPlayer = Player.m_localPlayer; if (view == null || !Object.op_Implicit((Object)(object)localPlayer) || Network.World == 0L) { return; } string key = OfferNews.Key(Network.World, view.Id); localPlayer.m_customData.TryGetValue(key, out var value); if (OfferNews.Unread(view.OfferRevision, value)) { localPlayer.m_customData[key] = OfferNews.Remember(view.OfferRevision, value); TraderNPC traderNPC = TraderNPC.Find(view.Id); if (Object.op_Implicit((Object)(object)traderNPC)) { ((Component)traderNPC).GetComponent<TraderOfferMarker>()?.Refresh(traderNPC.Current); } } } internal static void ReleaseAssets() { if (Object.op_Implicit((Object)(object)sprite)) { Texture2D texture = sprite.texture; Object.Destroy((Object)(object)sprite); if (Object.op_Implicit((Object)(object)texture)) { Object.Destroy((Object)(object)texture); } } sprite = null; } } internal static class TraderPose { public static void Stand(Animator animator) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Invalid comparison between Unknown and I4 //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Invalid comparison between Unknown and I4 //IL_00f1: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)animator)) { return; } AnimatorCullingMode cullingMode = animator.cullingMode; animator.cullingMode = (AnimatorCullingMode)0; animator.fireEvents = false; animator.applyRootMotion = false; animator.Update(0f); AnimatorControllerParameter[] parameters = animator.parameters; foreach (AnimatorControllerParameter val in parameters) { if ((int)val.type == 9) { animator.ResetTrigger(val.nameHash); } if ((int)val.type == 4 && (val.name == "wakeup" || val.name == "intro" || val.name == "sleeping" || val.name == "sitting" || val.name == "attached")) { animator.SetBool(val.nameHash, false); } } for (int j = 0; j < 4; j++) { animator.Update(5f); } animator.cullingMode = cullingMode; } } internal static class WorldDrops { private sealed class Snapshot { public WeakReference<Ledger> Ledger; public string[] Ids; } private static volatile Snapshot prepared; [ThreadStatic] private static Snapshot saving; private static readonly ConcurrentQueue<Snapshot> completed = new ConcurrentQueue<Snapshot>(); public static void Capture() { Ledger host = Network.Host; prepared = ((host == null) ? null : new Snapshot { Ledger = new WeakReference<Ledger>(host), Ids = host.Data.Drops.Keys.Where(WorldObjects.DropDelivered).ToArray() }); } public static void Begin() { saving = prepared; } public static void End(bool success) { if (success && saving != null && saving.Ids.Length != 0) { completed.Enqueue(saving); } saving = null; } public static void ClearThread() { saving = null; } public static void Reset() { prepared = null; saving = null; Snapshot result; while (completed.TryDequeue(out result)) { } } public static void Tick() { Snapshot result; while (completed.TryPeek(out result)) { if (result.Ledger.TryGetTarget(out var target) && target == Network.Host) { target.CompleteDrops(result.Ids); string[] ids = result.Ids; for (int i = 0; i < ids.Length; i++) { WorldObjects.RemoveDropMarker(ids[i]); } } completed.TryDequeue(out var _); } } } [HarmonyPatch(typeof(ZDOMan), "PrepareSave")] internal static class CaptureDropSave { private static void Postfix() { WorldDrops.Capture(); } } [HarmonyPatch(typeof(ZNet), "SaveWorldThread")] internal static class WatchDropSave { private static void Prefix() { WorldDrops.Begin(); } private static Exception Finalizer(Exception __exception) { WorldDrops.ClearThread(); return __exception; } } [HarmonyPatch(typeof(SaveSystem), "EndSave")] internal static class ConfirmDropSave { private static void Postfix(bool __1) { WorldDrops.End(__1); } } internal sealed class Appearance { public string Id; public string OwnerName; [JsonIgnore] public long Revision; public Look Look; public Dictionary<string, Item> Equipment; public long OfferRevision; } internal static class WorldObjects { public const string Prefab = "PT_Trader"; public static GameObject TraderPrefab; public static Sprite ScalesIcon; public static string[] Hairs = Array.Empty<string>(); public static string[] Beards = Array.Empty<string>(); private static string[] allHairs = Array.Empty<string>(); private static readonly Dictionary<string, ZDO> objects = new Dictionary<string, ZDO>(); private static readonly Dictionary<string, ZDO> orphans = new Dictionary<string, ZDO>(); private static readonly string[] Men = new string[6] { "Bjorn", "Eirik", "Torstein", "Ulf", "Harald", "Leif" }; private static readonly string[] Women = new string[6] { "Sigrid", "Ingrid", "Astrid", "Freydis", "Helga", "Gudrun" }; public static void Register() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Expected O, but got Unknown try { L.Select(Localization.instance); if (!Plugin.Headless && !Object.op_Implicit((Object)(object)ScalesIcon)) { ScalesIcon = Icon(); } TraderPrefab = BuildNpc("PT_Trader"); PieceConfig val = new PieceConfig { Name = "$pt_S080", Description = "$pt_Recipe", PieceTable = "Hammer", Category = "$pt_S134", Icon = ScalesIcon, Requirements = ((IEnumerable<KeyValuePair<string, int>>)Plugin.Cost).Select((Func<KeyValuePair<string, int>, RequirementConfig>)((KeyValuePair<string, int> p) => new RequirementConfig(p.Key, p.Value, 0, false))).ToArray() }; PieceManager.Instance.AddPiece(new CustomPiece(TraderPrefab, true, val)); PrefabManager.OnVanillaPrefabsAvailable -= Register; } catch (Exception e) { Plugin.Error(e); } } private static GameObject BuildNpc(string name) { //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) GameObject val = PrefabManager.Instance.CreateClonedPrefab(name, "Player"); Check.That((Object)(object)val != (Object)null && !val.activeInHierarchy, "Could not safely prepare the player visual model"); VisEquipment component = val.GetComponent<VisEquipment>(); Check.That(Object.op_Implicit((Object)(object)component), "The player model has no VisEquipment"); MonoBehaviour[] array = (from b in val.GetComponentsInChildren<MonoBehaviour>(true) where !(b is Player) && !(b is VisEquipment) && !(b is ZNetView) select b).ToArray(); for (int num = 0; num < array.Length; num++) { Object.DestroyImmediate((Object)(object)array[num]); } Player[] componentsInChildren = val.GetComponentsInChildren<Player>(true); for (int num = 0; num < componentsInChildren.Length; num++) { Object.DestroyImmediate((Object)(object)componentsInChildren[num]); } Rigidbody[] componentsInChildren2 = val.GetComponentsInChildren<Rigidbody>(true); for (int num = 0; num < componentsInChildren2.Length; num++) { Object.DestroyImmediate((Object)(object)componentsInChildren2[num]); } Collider[] componentsInChildren3 = val.GetComponentsInChildren<Collider>(true); for (int num = 0; num < componentsInChildren3.Length; num++) { Object.DestroyImmediate((Object)(object)componentsInChildren3[num]); } AudioSource[] componentsInChildren4 = val.GetComponentsInChildren<AudioSource>(true); for (int num = 0; num < componentsInChildren4.Length; num++) { Object.DestroyImmediate((Object)(object)componentsInChildren4[num]); } component.m_playerComponent = null; component.m_isPlayer = true; component.m_randomModelIndex = false; component.m_clothColliders.Clear(); ZNetView component2 = val.GetComponent<ZNetView>(); component2.m_persistent = true; component2.m_type = (ObjectType)2; component2.m_distant = false; val.layer = LayerMask.NameToLayer("piece"); CapsuleCollider obj = val.AddComponent<CapsuleCollider>(); obj.center = new Vector3(0f, 1f, 0f); obj.height = 2f; obj.radius = 0.35f; Piece obj2 = val.AddComponent<Piece>(); obj2.m_canBeRemoved = true; obj2.m_canRotate = true; obj2.m_resources = Array.Empty<Requirement>(); obj2.m_noInWater = true; obj2.m_clipGround = true; obj2.m_spaceRequirement = 0.4f; ((StaticTarget)obj2).m_primaryTarget = false; ((StaticTarget)obj2).m_randomTarget = false; obj2.m_targetNonPlayerBuilt = false; val.AddComponent<TraderNPC>(); if (Plugin.Headless) { ((Behaviour)component).enabled = false; Renderer[] componentsInChildren5 = val.GetComponentsInChildren<Renderer>(true); for (int num = 0; num < componentsInChildren5.Length; num++) { componentsInChildren5[num].enabled = false; } Animator[] componentsInChildren6 = val.GetComponentsInChildren<Animator>(true); for (int num = 0; num < componentsInChildren6.Length; num++) { ((Behaviour)componentsInChildren6[num]).enabled = false; } Cloth[] componentsInChildren7 = val.GetComponentsInChildren<Cloth>(true); for (int num = 0; num < componentsInChildren7.Length; num++) { componentsInChildren7[num].enabled = false; } } return val; } public static void PopulateHair() { if (!((Object)(object)ObjectDB.instance == (Object)null)) { allHairs = (from i in ObjectDB.instance.GetAllItems((ItemType)10, "Hair") select ((Object)((Component)i).gameObject).name into n orderby n select n).ToArray(); Hairs = HairstyleChoices.Current(allHairs); Beards = (from i in ObjectDB.instance.GetAllItems((ItemType)10, "Beard") select ((Object)((Component)i).gameObject).name into n orderby n select n).ToArray(); } } public static void SetBuildCost(Dictionary<string, int> cost) { if (Object.op_Implicit((Object)(object)TraderPrefab) && Object.op_Implicit((Object)(object)ObjectDB.instance)) { Requirement[] resources = ((IEnumerable<KeyValuePair<string, int>>)cost).Select((Func<KeyValuePair<string, int>, Requirement>)delegate(KeyValuePair<string, int> p) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(p.Key); ItemDrop val = (Object.op_Implicit((Object)(object)itemPrefab) ? itemPrefab.GetComponent<ItemDrop>() : null); Check.That(Object.op_Implicit((Object)(object)val), "Building resource not found: " + p.Key); return new Requirement { m_resItem = val, m_amount = p.Value, m_recover = false }; }).ToArray(); Piece component = TraderPrefab.GetComponent<Piece>(); component.m_resources = resources; component.m_description = "$pt_Recipe"; } } public static void ReleaseAssets() { Reset(); if (Object.op_Implicit((Object)(object)ScalesIcon)) { Texture2D texture = ScalesIcon.texture; Object.Destroy((Object)(object)ScalesIcon); if (Object.op_Implicit((Object)(object)texture)) { Object.Destroy((Object)(object)texture); } } ScalesIcon = null; Hairs = (Beards = (allHairs = Array.Empty<string>())); } public static Look RandomLook() { if (Hairs.Length == 0) { PopulateHair(); } int num = Random.Range(0, 2); string[] array = ((num == 0) ? Men : Women); return new Look { Body = num, Name = L.Text(array[Random.Range(0, array.Length)]), Hair = ((Hairs.Length != 0) ? Hairs[Random.Range(0, Hairs.Length)] : ""), Beard = ((num == 0 && Beards.Length != 0) ? Beards[Random.Range(0, Beards.Length)] : "") }; } public static void ValidateLook(Look look) { Check.That(look != null, "No appearance selected"); look.Validate(); if (Hairs.Length == 0) { PopulateHair(); } Check.That((look.Hair == "" || allHairs.Contains(look.Hair)) && (look.Beard == "" || Beards.Contains(look.Beard)), "Unknown hairstyle or beard"); } public static void Reset() { objects.Clear(); orphans.Clear(); TraderMap.Reset(); } public static void Reconcile(Registry registry) { List<ZDO> list = new List<ZDO>(); int num = 0; while (!ZDOMan.instance.GetAllZDOsWithPrefabIterative("PT_Trader", list, ref num)) { } foreach (ZDO item in list) { string text = item.GetString("PT.id", ""); Trader value; if (item.GetBool("PT.dropped", false)) { Network.Host.CompleteDrops(new string[1] { text }); item.SetOwner(ZNet.GetUID()); ZDOMan.instance.DestroyZDO(item); } else if (registry.Drops.ContainsKey(text)) { objects[text] = item; } else if (!registry.Traders.TryGetValue(text, out value)) { Plugin.Warn("Trader without registry data left in the world: " + text + ". Other traders remain available."); if (!string.IsNullOrEmpty(text)) { orphans[text] = item; } } else if (value.Place != PlaceState.Placed || objects.ContainsKey(text)) { ZDOMan.instance.DestroyZDO(item); } else { objects[text] = item; } } foreach (Trader value2 in registry.Traders.Values) { Apply(value2); } DropPlan[] array = Network.Host.Data.Drops.Values.ToArray(); for (int i = 0; i < array.Length; i++) { ApplyDrops(array[i]); } } public static bool RemoveOrphan(string id, Vector3 playerPosition, out Vector3 removedPosition) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) removedPosition = Vector3.zero; if (!orphans.TryGetValue(id, out var value)) { return false; } Check.That(Vector3.Distance(playerPosition, value.GetPosition()) <= 5f, "Too far from the trader"); Check.That(!Network.Host.Data.Traders.ContainsKey(id) && !Network.Host.Data.Drops.ContainsKey(id), "Data changed: open the trader again"); removedPosition = value.GetPosition(); value.SetOwner(ZNet.GetUID()); ZDOMan.instance.DestroyZDO(value); orphans.Remove(id); return true; } public static void HammerEffects(float[] position, float yaw, bool removed) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) try { Player localPlayer = Player.m_localPlayer; if (Object.op_Implicit((Object)(object)localPlayer) && Object.op_Implicit((Object)(object)ZNetScene.instance)) { Vector3 val = Network.Vec(position); GameObject prefab = ZNetScene.instance.GetPrefab("wood_floor"); Piece val2 = (Object.op_Implicit((Object)(object)prefab) ? prefab.GetComponent<Piece>() : null); if (Object.op_Implicit((Object)(object)val2)) { val2.m_placeEffect.Create(val, Quaternion.Euler(0f, yaw, 0f), (Transform)null, 1f, -1, ((Character)localPlayer).GetZDOID()); } if (removed) { localPlayer.m_removeEffects.Create(val, Quaternion.identity, (Transform)null, 1f, -1, ((Character)localPlayer).GetZDOID()); } } } catch (Exception e) { Plugin.Error(e); } } public static bool DropDelivered(string id) { if (objects.TryGetValue(id, out var value)) { return value.GetBool("PT.dropped", false); } return false; } public static void RemoveDropMarker(string id) { if (DropDelivered(id)) { ZDO val = objects[id]; val.SetOwner(ZNet.GetUID()); ZDOMan.instance.DestroyZDO(val); objects.Remove(id); } } public static void ValidateDrops(DropPlan plan) { plan.Validate(); foreach (Item item in plan.Items.Concat(new Item[1] { Items.Factory("Coins") })) { Check.That(Object.op_Implicit((Object)(object)Items.Decode(item).m_dropPrefab.GetComponent<ZNetView>()), "The item has no network component: " + item.Prefab); } } public static void ApplyDrops(DropPlan plan) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) if (DropDelivered(plan.Id)) { return; } ValidateDrops(plan); if (!objects.TryGetValue(plan.Id, out var value)) { value = ZDOMan.instance.CreateNewZDO(Network.Vec(plan.Position), StringExtensionMethods.GetStableHashCode("PT_Trader")); value.Persistent = true; value.SetPrefab(StringExtensionMethods.GetStableHashCode("PT_Trader")); value.Set("PT.id", plan.Id); objects[plan.Id] = value; } value.SetOwner(ZNet.GetUID()); List<ZDO> list = new List<ZDO>(); try { int num = 0; foreach (Item item in plan.Contents(Items.Factory("Coins"))) { ItemData obj = Items.Decode(item); ZNetView component = obj.m_dropPrefab.GetComponent<ZNetView>(); Check.That(Object.op_Implicit((Object)(object)component), "The item has no network component: " + item.Prefab); float num2 = (float)num++ * 2.399963f; Vector3 val = Network.Vec(plan.Position) + new Vector3(Mathf.Cos(num2) * 0.5f, 0.7f + (float)(num % 4) * 0.1f, Mathf.Sin(num2) * 0.5f); ZDO val2 = ZDOMan.instance.CreateNewZDO(val, StringExtensionMethods.GetStableHashCode(item.Prefab)); list.Add(val2); val2.SetOwner(ZNet.GetUID()); val2.Persistent = true; val2.Type = component.m_type; val2.SetPrefab(StringExtensionMethods.GetStableHashCode(item.Prefab)); val2.SetRotation(Quaternion.identity); ItemDrop.SaveToZDO(obj, val2, -1); } value.Set("PT.dropped", true); } catch { foreach (ZDO item2 in list) { item2.SetOwner(ZNet.GetUID()); ZDOMan.instance.DestroyZDO(item2); } throw; } TraderNPC traderNPC = TraderNPC.Find(plan.Id); if (Object.op_Implicit((Object)(object)traderNPC)) { traderNPC.Sync(); } } public static void Apply(Trader trader) { //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) if (trader.Place != PlaceState.Placed) { if (objects.TryGetValue(trader.Id, out var value)) { value.SetOwner(ZNet.GetUID()); ZDOMan.instance.DestroyZDO(value); objects.Remove(trader.Id); } return; } if (!objects.TryGetValue(trader.Id, out var value2)) { value2 = ZDOMan.instance.CreateNewZDO(Network.Vec(trader.Position), StringExtensionMethods.GetStableHashCode("PT_Trader")); value2.Persistent = true; value2.Type = (ObjectType)2; value2.SetPrefab(StringExtensionMethods.GetStableHashCode("PT_Trader")); objects[trader.Id] = value2; } value2.SetOwner(ZNet.GetUID()); value2.SetPosition(Network.Vec(trader.Position)); value2.SetRotation(Quaternion.Euler(0f, trader.Yaw, 0f)); TraderAppearance.Write(value2, trader.Look, trader.Equipment); value2.Set("PT.id", trader.Id); value2.Set("PT.revision", trader.Revision); value2.Set("PT.appearance", Json.Write(new Appearance { Id = trader.Id, OwnerName = trader.OwnerName, OfferRevision = OfferNews.Version(trader), Look = trader.Look, Equipment = trader.Equipment.ToDictionary((KeyValuePair<string, Item> p) => p.Key, (KeyValuePair<string, Item> p) => new Item { Prefab = p.Value.Prefab, Quality = p.Value.Quality, Variant = p.Value.Variant }) })); TraderNPC traderNPC = TraderNPC.Find(trader.Id); if (Object.op_Implicit((Object)(object)traderNPC)) { traderNPC.Sync(); } } public static Sprite Icon() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) Texture2D val = NativeWidgets.LoadTexture("merchant-icon.png"); return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)); } } public sealed class TraderNPC : MonoBehaviour, Hoverable, Interactable { private static readonly HashSet<TraderNPC> active = new HashSet<TraderNPC>(); public string Id = ""; public long Revision; internal Appearance Current; private ZNetView view; private VisEquipment visual; private long revision = -1L; private Animator animator; private readonly HashSet<int> triggers = new HashSet<int>(); private readonly GreetingGate greeting = new GreetingGate(); private readonly HashSet<int> presentPlayers = new HashSet<int>(); private float nextGesture; private bool wavePending; private TraderOfferMarker offerMarker; private void Awake() { view = ((Component)this).GetComponent<ZNetView>(); visual = ((Component)this).GetComponent<VisEquipment>(); } private void Start() { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Invalid comparison between Unknown and I4 if (Plugin.Headless) { ((Behaviour)this).enabled = false; return; } animator = ((Component)this).GetComponentInChildren<Animator>(); TraderPose.Stand(animator); if (!Object.op_Implicit((Object)(object)view) || !view.IsValid()) { return; } active.Add(this); offerMarker = ((Component)this).gameObject.AddComponent<TraderOfferMarker>(); if (Object.op_Implicit((Object)(object)animator)) { animator.fireEvents = false; AnimatorControllerParameter[] parameters = animator.parameters; foreach (AnimatorControllerParameter val in parameters) { if ((int)val.type == 9) { triggers.Add(val.nameHash); } } } Sync(); ((MonoBehaviour)this).InvokeRepeating("Refresh", 1f, 1f); } private void Refresh() { //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) Sync(); if (!Object.op_Implicit((Object)(object)view) || !view.IsValid() || Current == null || !((Component)this).gameObject.activeInHierarchy) { return; } offerMarker?.Refresh(Current); TraderMap.Observe(view.GetZDO(), Current.Look.Name); presentPlayers.Clear(); foreach (Player allPlayer in Player.GetAllPlayers()) { if (Object.op_Implicit((Object)(object)allPlayer) && !((Character)allPlayer).IsDead()) { int instanceID = ((Object)allPlayer).GetInstanceID(); presentPlayers.Add(instanceID); if (greeting.Enter(instanceID, Vector3.Distance(((Component)allPlayer).transform.position, ((Component)this).transform.position))) { wavePending = true; } } } greeting.Prune(presentPlayers); if (!greeting.HasNearby) { wavePending = false; } if (wavePending && Time.unscaledTime >= nextGesture) { wavePending = false; Gesture("wave"); } } private void Gesture(string name, bool interrupt = false) { if (!Object.op_Implicit((Object)(object)animator) || (!interrupt && Time.unscaledTime < nextGesture)) { return; } int num = Animator.StringToHash("emote_" + name); if (triggers.Contains(num)) { int num2 = Animator.StringToHash("emote_stop"); if (triggers.Contains(num2)) { animator.ResetTrigger(num2); } animator.SetTrigger(num); nextGesture = Time.unscaledTime + 4f; } } internal void Farewell(string gesture) { wavePending = false; Gesture(gesture, interrupt: true); } private void OnDestroy() { active.Remove(this); ((MonoBehaviour)this).CancelInvoke(); } public static TraderNPC Find(string id) { return active.FirstOrDefault((TraderNPC n) => Object.op_Implicit((Object)(object)n) && n.Id == id); } public void Sync() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)view) || !view.IsValid()) { return; } ZDO zDO = view.GetZDO(); if (zDO.GetBool("PT.dropped", false)) { TraderMap.Remove(zDO.m_uid); ((Component)this).gameObject.SetActive(false); return; } long num = zDO.GetLong("PT.revision", -1L); if (num == revision) { return; } string text = zDO.GetString("PT.appearance", ""); if (text.Length == 0 || text.Length > 262144) { return; } try { Appearance appearance = Json.Read<Appearance>(text); Id = appearance.Id; Revision = num; appearance.Revision = num; Current = appearance; ApplyLook(appearance); revision = num; TraderMap.Observe(zDO, appearance.Look.Name); } catch (Exception e) { Plugin.Error(e); ((MonoBehaviour)this).CancelInvoke(); } } internal void ApplyLook(Appearance a) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)visual)) { visual = ((Component)this).GetComponent<VisEquipment>(); } if (Object.op_Implicit((Object)(object)visual)) { visual.SetModel(a.Look.Body); visual.SetSkinColor(Network.Vec(a.Look.Skin)); visual.SetHairColor(Network.Vec(a.Look.HairColor)); visual.SetHairItem((!string.IsNullOrEmpty(a.Look.Hair)) ? StringExtensionMethods.GetStableHashCode(a.Look.Hair) : 0); visual.SetBeardItem((!string.IsNullOrEmpty(a.Look.Beard)) ? StringExtensionMethods.GetStableHashCode(a.Look.Beard) : 0); visual.SetHelmetItem(Hash("Голова")); visual.SetChestItem(Hash("Торс")); visual.SetLegItem(Hash("Ноги")); visual.SetUtilityItem(Hash("Аксессуар")); Item value; Item item = ((a.Equipment != null && a.Equipment.TryGetValue("Плечи", out value)) ? value : null); visual.SetShoulderItem(Hash("Плечи"), item?.Variant ?? 0, item?.Quality ?? 1); Animator componentInChildren = ((Component)this).GetComponentInChildren<Animator>(); if (Object.op_Implicit((Object)(object)componentInChildren)) { componentInChildren.applyRootMotion = false; componentInChildren.SetFloat("forward_speed", 0f); componentInChildren.SetFloat("sideway_speed", 0f); } } int Hash(string slot) { if (a.Equipment == null || !a.Equipment.TryGetValue(slot, out var value2)) { return 0; } return StringExtensionMethods.GetStableHashCode(value2.Prefab); } } public string GetHoverName() { return Current?.Look.Name ?? L.Text("Trader"); } public float GetHoverOffset() { return 1.5f; } public string GetHoverText() { return GetHoverName() + L.Text("\nOwner: ") + (Current?.OwnerName ?? "…") + "\n" + Localization.instance.Localize(L.Text("[<color=yellow><b>$KEY_Use</b></color>] Open trader")); } public bool Interact(Humanoid user, bool hold, bool alt) { if (hold || Id.Length == 0) { return false; } Network.Open(Id); return true; } public bool UseItem(Humanoid user, ItemData item) { return false; } } [HarmonyPatch(typeof(Player), "TryPlacePiece")] internal static class PlaceTrader { private static bool Prefix(Player __instance, Piece piece, ref bool __result) { //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)((Component)piece).GetComponent<TraderNPC>())) { return true; } __result = false; try { Check.That(!Network.Busy, "Wait for the operation to finish"); AccessTools.Method(typeof(Player), "UpdatePlacementGhost", (Type[])null, (Type[])null).Invoke(__instance, new object[1] { true }); Check.That(AccessTools.Field(typeof(Player), "m_placementStatus").GetValue(__instance).ToString() == "Valid", "Cannot place a trader here"); GameObject val = (GameObject)AccessTools.Field(typeof(Player), "m_placementGhost").GetValue(__instance); Check.That(Object.op_Implicit((Object)(object)val) && Vector3.Distance(val.transform.position, ((Component)__instance).transform.position) <= 5f, "Too far away to build"); Vector3 position = val.transform.position; Request request = new Request(); request.Action = "create"; request.Position = new float[3] { position.x, position.y, position.z }; request.Yaw = val.transform.eulerAngles.y; Network.Operate(request); } catch (Exception e) { Network.Fail(e); } return false; } } [HarmonyPatch(typeof(Player), "RemovePiece")] internal static class DismantleTrader { private static bool Prefix(Player __instance, ref bool __result) { Piece hoveringPiece = __instance.GetHoveringPiece(); TraderNPC traderNPC = (Object.op_Implicit((Object)(object)hoveringPiece) ? ((Component)hoveringPiece).GetComponent<TraderNPC>() : null); if (!Object.op_Implicit((Object)(object)traderNPC)) { return true; } __result = false; if (!Network.Busy) { Network.Send(new Request { Action = "dismantle", Trader = traderNPC.Id, Revision = traderNPC.Revision }); } return false; } } internal static class L { private static TranslationCatalog catalog; private static string directory; private static string language; private static readonly MethodInfo addWord = AccessTools.Method(typeof(Localization), "AddWord", (Type[])null, (Type[])null); internal static void Initialize() { directory = Path.GetDirectoryName(typeof(L).Assembly.Location); using Stream stream = typeof(L).Assembly.GetManifestResourceStream("PlayerTraders.Localization.catalog.json"); using StreamReader streamReader = new StreamReader(stream); catalog = new TranslationCatalog(streamReader.ReadToEnd()); try { TranslationCatalog.WriteTemplateIfMissing(Path.Combine(directory, "english_PlayerTraders.json"), catalog.EnglishTemplate()); } catch (Exception ex) { Plugin.Warn("Could not create the English translation template: " + ex.Message); } } internal static void Select(Localization localization, string selected = null) { if (catalog != null && localization != null) { selected = selected ?? localization.GetSelectedLanguage(); if (language != selected) { language = selected; string json = TranslationFiles.Read(directory, Paths.PluginPath, selected, Plugin.Warn); catalog.Load(json, Plugin.Warn); } string[] array = new string[3] { "S080", "Recipe", "S134" }; foreach (string text in array) { addWord.Invoke(localization, new object[2] { "pt_" + text, catalog.ByKey(text) }); } } } internal static string Text(string source) { return catalog?.Text(source) ?? source; } internal static string Message(string source) { return catalog?.Message(source) ?? source; } internal static string Format(string source, params object[] args) { return string.Format(CultureInfo.CurrentCulture, Text(source), args); } internal static void Reset() { catalog = null; directory = (language = null); } } [HarmonyPatch(typeof(Localization), "SetupLanguage")] internal static class TraderLanguage { private static void Postfix(Localization __instance, string __0) { L.Select(__instance, __0); } } [BepInPlugin("ru.playertraders", "Player Traders", "0.6.5")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string Id = "ru.playertraders"; public const string ModVersion = "0.6.5"; public static Plugin Instance; public static ConfigEntry<string> ConstructionCost; internal static Dictionary<string, int> Cost = BuildCost.Parse("Coins:50"); public static ConfigEntry<bool> Diagnostics; private Harmony patches; internal static bool Headless { get { if (!GUIManager.IsHeadless()) { if (Object.op_Implicit((Object)(object)ZNet.instance)) { return ZNet.instance.IsDedicated(); } return false; } return true; } } private void Awake() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown Instance = this; L.Initialize(); patches = new Harmony("ru.playertraders"); patches.PatchAll(); PrefabManager.OnVanillaPrefabsAvailable += WorldObjects.Register; ItemManager.OnItemsRegistered += WorldObjects.PopulateHair; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Player Traders: loading 0.6.5"); } internal static void LoadServerConfiguration() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown Check.That(Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer(), "Only the server can load trader settings"); ConfigFile val = new ConfigFile(Path.Combine(Paths.ConfigPath, "PlayerTraders.config"), true); ConstructionCost = val.Bind<string>("Construction", "Resources", "Coins:50", "Construction cost as Prefab:Amount pairs, separated by commas; up to 6 resources. Default: Coins:50 (50 coins). Restart the game/server after changing this setting."); Cost = BuildCost.Parse(ConstructionCost.Value); Diagnostics = val.Bind<bool>("Diagnostics", "VerboseLog", false, "Detailed operation messages"); WorldObjects.SetBuildCost(Cost); LogInfo("Server trader recipe: " + ConstructionCost.Value + "; config: " + val.ConfigFilePath); } internal static void ResetConfiguration() { Cost = BuildCost.Parse("Coins:50"); Diagnostics = null; ConstructionCost = null; if (Object.op_Implicit((Object)(object)WorldObjects.TraderPrefab) && Object.op_Implicit((Object)(object)ObjectDB.instance)) { WorldObjects.SetBuildCost(Cost); } } public static void Error(Exception e) { ((BaseUnityPlugin)Instance).Logger.LogError((object)e); } public static void LogInfo(string value) { ((BaseUnityPlugin)Instance).Logger.LogInfo((object)value); } public static void Warn(string value) { ((BaseUnityPlugin)Instance).Logger.LogWarning((object)value); } public static void Trace(string value) { ConfigEntry<bool> diagnostics = Diagnostics; if (diagnostics != null && diagnostics.Value) { ((BaseUnityPlugin)Instance).Logger.LogInfo((object)value); } } private void Update() { Network.Tick(); if (!Headless) { TraderUI.Update(); } } private void OnDestroy() { TraderUI.Close(send: false); TraderUI.ReleaseInput(); TraderOfferMarker.ReleaseAssets(); NativeWidgets.ReleaseAssets(); Network.Reset(); WorldObjects.ReleaseAssets(); PrefabManager.OnVanillaPrefabsAvailable -= WorldObjects.Register; ItemManager.OnItemsRegistered -= WorldObjects.PopulateHair; Harmony obj = patches; if (obj != null) { obj.UnpatchSelf(); } L.Reset(); Instance = null; } } public static class Texts { public const string InvalidPacket = "Could not read trader network data. Please try again."; public const string Connecting = "The trader is still loading. Please try again in a moment."; public const string Aborted = "You moved away from the trader. The operation was cancelled; your items are unchanged."; public const string S001 = "Empty data"; public const string S002 = "Enter a positive whole number"; public const string S003 = "The number must be greater than zero"; public const string S004 = "The name must contain 1 to 32 characters"; public const string S005 = "Invalid item data"; public const string S006 = "Invalid inventory"; public const string S007 = "Duplicate inventory slot"; public const string S008 = "The item has already been moved"; public const string S009 = "Unequip the item first"; public const string S010 = "Not enough items"; public const string S011 = "Not enough space"; public const string S012 = "Out of stock"; public const string S013 = "Not enough coins"; public const string S014 = "Bjorn"; public const string S015 = "Unknown body type"; public const string S016 = "Invalid colour"; public const string S017 = "Invalid appearance"; public const string S018 = "Invalid trader data"; public const string S019 = "The warehouse must have 100 slots"; public const string S021 = "Data size limit exceeded"; public const string S022 = "Invalid equipment"; public const string S023 = "Invalid price"; public const string S024 = "Unsupported format or different world"; public const string S025 = "Invalid ID"; public const string S026 = "Corrupted journal"; public const string S027 = "Unknown transaction stage"; public const string S028 = "The character receipt does not match the host journal"; public const string S029 = "Inventory does not match the journal; duplicate delivery blocked. Saved data needs checking."; public const string S030 = "Голова"; public const string S031 = "Торс"; public const string S032 = "Ноги"; public const string S033 = "Плечи"; public const string S034 = "Аксессуар"; public const string S035 = "Invalid transaction number"; public const string S036 = "The previous transaction needs recovery"; public const string S037 = "Duplicate or outdated transaction"; public const string S038 = "Inventory confirmation missing"; public const string S039 = "Trader not found"; public const string S040 = "The trader has an unfinished transaction"; public const string S041 = "Data changed: open the trader again"; public const string S042 = "Only the owner can do this"; public const string S043 = "The trader is not placed in the world"; public const string S045 = "The unit price must be a positive whole number"; public const string S047 = "This item is no longer for sale"; public const string S048 = "Choose an item quantity"; public const string S049 = "The total is too large; choose a smaller quantity"; public const string S050 = "Not enough coins in the till"; public const string S051 = "Appearance is not set"; public const string S052 = "Only head, chest, legs, shoulders and accessory slots are supported"; public const string S053 = "The slot is empty"; public const string S055 = "Unknown action"; public const string S056 = "Choose where to return the clothing"; public const string S057 = "Not enough materials to create a trader"; public const string S058 = "The trader is busy"; public const string S059 = "Close the other trader first"; public const string S060 = "Session ended: open the trader again"; public const string S061 = "The data file is too large"; public const string S062 = "Corrupted data; the original is preserved and operations are blocked"; public const string S063 = "Storage is full: operations stopped"; public const string S064 = "The main file is missing, but a backup or pending file exists: recovery required"; public const string S065 = "An operation is already in progress"; public const string S066 = "Transaction not found"; public const string S067 = "Receipt not found"; public const string S068 = "Transaction not confirmed"; public const string S069 = "A confirmed transaction cannot be cancelled"; public const string S070 = "The game blocked character saving; the transaction remains in the journal"; public const string S071 = "Could not save the character: the transaction is awaiting recovery"; public const string S072 = "Unknown item: "; public const string S073 = "Game item missing: "; public const string S074 = "Item properties do not match the game"; public const string S075 = "Inventory changed during the transaction; recovery required"; public const string S076 = "Inventory size changed"; public const string S077 = "Player equipment changed"; public const string S078 = "Cannot remove an item the player is wearing"; public const string S080 = "Trader"; public const string S086 = "Invalid platform ID"; public const string S087 = "The network message is too large"; public const string S088 = "Too much custom item data"; public const string S089 = "Connection lost"; public const string S090 = "World ID is unavailable"; public const string S091 = "Trader storage is locked: "; public const string S092 = "Connection lost or trader too far away"; public const string S093 = "Player unavailable"; public const string S094 = "Connection not confirmed"; public const string S095 = "Connection owner is unavailable"; public const string S096 = "Character unavailable"; public const string S097 = "Invalid position"; public const string S098 = "The world is still loading"; public const string S099 = "Invalid request"; public const string S100 = "Too many requests"; public const string S101 = "Too far away to build"; public const string S102 = "Building is not allowed here"; public const string S103 = "Invalid placement"; public const string S105 = "Too far from the trader"; public const string S107 = "Operation failed. Check the log; no duplicate items were delivered."; public const string S108 = "Recover the unfinished transaction first"; public const string S109 = "Wait for the connection or transaction recovery"; public const string S110 = "The journal belongs to another world"; public const string S111 = "Client journal missing; a backup was found"; public const string S112 = "The world or connection changed"; public const string S113 = "Inventory changed before confirmation. Transaction blocked for inspection."; public const string S114 = "The host does not recognise the recorded transaction: a save rollback may have occurred. Automatic recovery blocked."; public const string S115 = "Session"; public const string S116 = "Recovery blocked; see the log"; public const string S117 = "Another transaction is awaiting recovery"; public const string S118 = "Transaction receipt mismatch"; public const string S119 = "Character receipt missing; recovery required"; public const string S120 = "Done"; public const string S121 = "Error: "; public const string S122 = "Eirik"; public const string S123 = "Torstein"; public const string S124 = "Ulf"; public const string S125 = "Harald"; public const string S126 = "Leif"; public const string S127 = "Sigrid"; public const string S128 = "Ingrid"; public const string S129 = "Astrid"; public const string S130 = "Freydis"; public const string S131 = "Helga"; public const string S132 = "Gudrun"; public const string S139 = "Could not safely prepare the player visual model"; public const string S140 = "The player model has no VisEquipment";