Decompiled source of Player Traders v0.6.5

plugins/PlayerTraders/PlayerTraders.dll

Decompiled 2 weeks ago
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";