Decompiled source of ValheimMarket v1.1.3

BepInEx/plugins/ValheimMarket/ValheimMarket.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
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 BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyVersion("0.0.0.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 ValheimMarket
{
	internal sealed class MarketClient
	{
		private sealed class Pending
		{
			internal Guid Id;

			internal byte[] Request;

			internal ItemBlob Refund;

			internal bool RefundOnly;

			internal float LastSent;
		}

		private readonly Dictionary<Guid, Pending> pending = new Dictionary<Guid, Pending>();

		private readonly HashSet<Guid> receivedClaims = new HashSet<Guid>();

		private string journalPath;

		private string receiptPath;

		private bool loaded;

		private byte[] snapshotRequest;

		private long snapshotOwner;

		private float snapshotLastSent;

		internal MarketSnapshot Snapshot { get; private set; }

		internal void Reset()
		{
			pending.Clear();
			receivedClaims.Clear();
			Snapshot = null;
			journalPath = null;
			receiptPath = null;
			loaded = false;
			snapshotRequest = null;
		}

		internal void Update()
		{
			if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			EnsureLoaded();
			if (snapshotRequest != null)
			{
				MarketUI uI = MarketPlugin.UI;
				if (uI != null && uI.IsOpen && Time.realtimeSinceStartup - snapshotLastSent > 5f)
				{
					MarketTransport.Send(ServerPeer(), snapshotRequest);
					snapshotLastSent = Time.realtimeSinceStartup;
					MarketPlugin.UI.SetMessage("Retrying market...");
				}
			}
			foreach (Pending item in pending.Values.ToList())
			{
				if (item.RefundOnly)
				{
					TryRefund(item);
				}
				else if (Time.realtimeSinceStartup - item.LastSent > 8f)
				{
					MarketTransport.Send(ServerPeer(), item.Request);
					item.LastSent = Time.realtimeSinceStartup;
				}
			}
		}

		private static long ServerPeer()
		{
			if (ZNet.instance.IsServer())
			{
				return ZNet.GetUID();
			}
			List<ZNetPeer> peers = ZNet.instance.GetPeers();
			if (peers.Count != 0)
			{
				return peers[0].m_uid;
			}
			return 0L;
		}

		private void EnsureLoaded()
		{
			if (loaded)
			{
				return;
			}
			string text = Path.Combine(Paths.ConfigPath, "ValheimMarket");
			Directory.CreateDirectory(text);
			string text2 = ZNet.instance.GetWorldUID() + "_" + Player.m_localPlayer.GetPlayerID();
			journalPath = Path.Combine(text, text2 + ".pending");
			receiptPath = Path.Combine(text, text2 + ".receipts");
			try
			{
				if (File.Exists(journalPath))
				{
					using FileStream input = File.OpenRead(journalPath);
					using BinaryReader binaryReader = new BinaryReader(input);
					int num = binaryReader.ReadInt32();
					if (num < 0 || num > 1000)
					{
						throw new InvalidDataException("Invalid pending actions");
					}
					for (int i = 0; i < num; i++)
					{
						Pending pending = new Pending
						{
							Id = MarketCodec.ReadGuid(binaryReader),
							RefundOnly = binaryReader.ReadBoolean(),
							Refund = (binaryReader.ReadBoolean() ? ItemBlob.Read(binaryReader) : null),
							Request = binaryReader.ReadBytes(binaryReader.ReadInt32()),
							LastSent = -100f
						};
						this.pending[pending.Id] = pending;
					}
				}
				if (File.Exists(receiptPath))
				{
					using FileStream input2 = File.OpenRead(receiptPath);
					using BinaryReader binaryReader2 = new BinaryReader(input2);
					int num2 = binaryReader2.ReadInt32();
					if (num2 < 0 || num2 > 100000)
					{
						throw new InvalidDataException("Invalid receipts");
					}
					for (int j = 0; j < num2; j++)
					{
						receivedClaims.Add(MarketCodec.ReadGuid(binaryReader2));
					}
				}
			}
			catch (Exception ex)
			{
				MarketPlugin.Instance.Log.LogError((object)("Market client journal could not be read: " + ex));
			}
			loaded = true;
		}

		private void SavePending()
		{
			if (journalPath == null)
			{
				return;
			}
			using (FileStream fileStream = new FileStream(journalPath + ".tmp", FileMode.Create, FileAccess.Write))
			{
				using BinaryWriter binaryWriter = new BinaryWriter(fileStream);
				binaryWriter.Write(pending.Count);
				foreach (Pending value in pending.Values)
				{
					MarketCodec.WriteGuid(binaryWriter, value.Id);
					binaryWriter.Write(value.RefundOnly);
					binaryWriter.Write(value.Refund != null);
					value.Refund?.Write(binaryWriter);
					binaryWriter.Write(value.Request.Length);
					binaryWriter.Write(value.Request);
				}
				binaryWriter.Flush();
				fileStream.Flush(flushToDisk: true);
			}
			AtomicSwap(journalPath);
		}

		private void SaveReceipts()
		{
			if (receiptPath == null)
			{
				return;
			}
			using (FileStream fileStream = new FileStream(receiptPath + ".tmp", FileMode.Create, FileAccess.Write))
			{
				using BinaryWriter binaryWriter = new BinaryWriter(fileStream);
				binaryWriter.Write(receivedClaims.Count);
				foreach (Guid receivedClaim in receivedClaims)
				{
					MarketCodec.WriteGuid(binaryWriter, receivedClaim);
				}
				binaryWriter.Flush();
				fileStream.Flush(flushToDisk: true);
			}
			AtomicSwap(receiptPath);
		}

		private static void AtomicSwap(string path)
		{
			string sourceFileName = path + ".tmp";
			if (!File.Exists(path))
			{
				File.Move(sourceFileName, path);
				return;
			}
			try
			{
				File.Replace(sourceFileName, path, path + ".bak");
			}
			catch (PlatformNotSupportedException)
			{
				File.Copy(path, path + ".bak", overwrite: true);
				File.Delete(path);
				File.Move(sourceFileName, path);
			}
		}

		internal void RequestSnapshot(ZDOID stall, long shopOwner)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			snapshotOwner = shopOwner;
			snapshotRequest = BuildRequest(Guid.NewGuid(), MarketAction.Snapshot, stall, shopOwner, Guid.Empty, 0, null);
			snapshotLastSent = Time.realtimeSinceStartup;
			MarketTransport.Send(ServerPeer(), snapshotRequest);
		}

		internal void Submit(MarketAction action, ZDOID stall, long shopOwner, Guid entityId, int number = 0, ItemData inventoryItem = null, int quantity = 0)
		{
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			EnsureLoaded();
			ItemBlob itemBlob = null;
			ItemBlob itemBlob2 = null;
			Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory();
			switch (action)
			{
			case MarketAction.CreateListing:
			case MarketAction.MakeOffer:
			case MarketAction.SellToNpc:
				if (inventoryItem == null || inventoryItem.m_equipped || quantity < 1 || quantity > inventoryItem.m_stack)
				{
					throw new InvalidDataException("Select an unequipped item and valid quantity");
				}
				itemBlob = ItemBlob.FromItem(inventoryItem, quantity);
				itemBlob.Validate();
				itemBlob2 = itemBlob;
				break;
			case MarketAction.Buy:
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Coins");
				ItemDrop val = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val == (Object)null)
				{
					throw new InvalidDataException("Coins are unavailable");
				}
				if (number < 1 || inventory.CountItems(val.m_itemData.m_shared.m_name, -1, false) < number)
				{
					throw new InvalidDataException("Not enough coins");
				}
				ItemData obj = val.m_itemData.Clone();
				obj.m_dropPrefab = ((Component)val).gameObject;
				itemBlob2 = ItemBlob.FromItem(obj, 1);
				itemBlob2.Stack = number;
				break;
			}
			}
			Guid guid = Guid.NewGuid();
			byte[] array = BuildRequest(guid, action, stall, shopOwner, entityId, number, itemBlob);
			if (itemBlob2 != null)
			{
				if (action == MarketAction.Buy)
				{
					ItemDrop component = ObjectDB.instance.GetItemPrefab("Coins").GetComponent<ItemDrop>();
					inventory.RemoveItem(component.m_itemData.m_shared.m_name, number, -1, false);
				}
				else if (!inventory.RemoveItem(inventoryItem, quantity))
				{
					throw new InvalidDataException("Item changed before deposit");
				}
				pending[guid] = new Pending
				{
					Id = guid,
					Request = array,
					Refund = itemBlob2,
					LastSent = Time.realtimeSinceStartup
				};
				SavePending();
				Game.instance.SavePlayerProfile(false, false);
			}
			MarketTransport.Send(ServerPeer(), array);
		}

		private static byte[] BuildRequest(Guid id, MarketAction action, ZDOID stall, long shopOwner, Guid entityId, int number, ItemBlob item)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			return MarketCodec.Encode(delegate(BinaryWriter w)
			{
				w.Write((byte)1);
				w.Write(3);
				MarketCodec.WriteGuid(w, id);
				w.Write((byte)action);
				w.Write(((ZDOID)(ref stall)).UserID);
				w.Write(((ZDOID)(ref stall)).ID);
				w.Write(shopOwner);
				MarketCodec.WriteGuid(w, entityId);
				w.Write(number);
				w.Write(item != null);
				item?.Write(w);
			});
		}

		internal void Handle(long sender, byte[] data)
		{
			if (sender != ServerPeer())
			{
				return;
			}
			try
			{
				using MemoryStream input = new MemoryStream(data);
				using BinaryReader binaryReader = new BinaryReader(input);
				WireKind wireKind = (WireKind)binaryReader.ReadByte();
				if (binaryReader.ReadInt32() != 3)
				{
					MarketPlugin.UI?.SetMessage("Incompatible market mod version");
					return;
				}
				switch (wireKind)
				{
				case WireKind.Snapshot:
					Snapshot = MarketSnapshot.Read(binaryReader);
					if (Snapshot.ShopOwner == snapshotOwner)
					{
						snapshotRequest = null;
					}
					MarketPlugin.UI?.ShowSnapshot(Snapshot);
					break;
				case WireKind.Result:
				{
					Guid key = MarketCodec.ReadGuid(binaryReader);
					bool flag = binaryReader.ReadBoolean();
					string message = binaryReader.ReadString();
					if (pending.TryGetValue(key, out var value))
					{
						if (flag)
						{
							pending.Remove(key);
						}
						else
						{
							value.RefundOnly = true;
							TryRefund(value);
						}
						SavePending();
					}
					MarketPlugin.UI?.SetMessage(message);
					break;
				}
				case WireKind.ClaimGrant:
				{
					Guid claimId = MarketCodec.ReadGuid(binaryReader);
					ItemBlob itemBlob = ItemBlob.Read(binaryReader);
					EnsureLoaded();
					if (!receivedClaims.Contains(claimId))
					{
						Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory();
						ItemData val = itemBlob.ToItem();
						if (!inventory.CanAddItem(val, -1) || !inventory.AddItem(val))
						{
							MarketPlugin.UI?.SetMessage("Inventory is full");
							break;
						}
						receivedClaims.Add(claimId);
						Game.instance.SavePlayerProfile(false, false);
						SaveReceipts();
					}
					MarketTransport.Send(ServerPeer(), MarketCodec.Encode(delegate(BinaryWriter w)
					{
						w.Write((byte)5);
						w.Write(3);
						MarketCodec.WriteGuid(w, claimId);
					}));
					break;
				}
				}
			}
			catch (Exception ex)
			{
				MarketPlugin.Instance.Log.LogError((object)("Market response failed: " + ex));
				MarketPlugin.UI?.SetMessage(ex.Message);
			}
		}

		private void TryRefund(Pending p)
		{
			if (p.Refund == null)
			{
				pending.Remove(p.Id);
				SavePending();
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			Inventory val = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null);
			if (val == null)
			{
				return;
			}
			try
			{
				while (p.Refund.Stack > 0)
				{
					int num = Math.Min(ObjectDB.instance.GetItemPrefab(p.Refund.Prefab).GetComponent<ItemDrop>().m_itemData.m_shared.m_maxStackSize, p.Refund.Stack);
					ItemData val2 = p.Refund.ToItem();
					val2.m_stack = num;
					if (!val.CanAddItem(val2, -1) || !val.AddItem(val2))
					{
						break;
					}
					p.Refund.Stack -= num;
					Game.instance.SavePlayerProfile(false, false);
					SavePending();
				}
				if (p.Refund.Stack == 0)
				{
					pending.Remove(p.Id);
					SavePending();
				}
			}
			catch (Exception ex)
			{
				MarketPlugin.Instance.Log.LogError((object)("Refund waiting: " + ex.Message));
			}
		}
	}
	internal enum MarketAction : byte
	{
		Snapshot = 1,
		CreateListing,
		Buy,
		MakeOffer,
		AcceptOffer,
		RejectOffer,
		CancelListing,
		CancelOffer,
		Collect,
		SellToNpc
	}
	internal sealed class ItemBlob
	{
		internal string Prefab;

		internal int Stack;

		internal float Durability;

		internal int Quality;

		internal int Variant;

		internal int WorldLevel;

		internal long CrafterId;

		internal string CrafterName;

		internal bool PickedUp;

		internal bool Cheated;

		internal Dictionary<string, string> CustomData = new Dictionary<string, string>();

		internal static ItemBlob FromItem(ItemData item, int amount)
		{
			if (item == null || (Object)(object)item.m_dropPrefab == (Object)null || amount < 1 || amount > item.m_stack)
			{
				throw new InvalidDataException("Invalid inventory item");
			}
			return new ItemBlob
			{
				Prefab = Utils.GetPrefabName(item.m_dropPrefab),
				Stack = amount,
				Durability = item.m_durability,
				Quality = item.m_quality,
				Variant = item.m_variant,
				WorldLevel = item.m_worldLevel,
				CrafterId = item.m_crafterID,
				CrafterName = (item.m_crafterName ?? ""),
				PickedUp = item.m_pickedUp,
				Cheated = item.m_cheated,
				CustomData = new Dictionary<string, string>(item.m_customData)
			};
		}

		internal ItemData ToItem()
		{
			ObjectDB instance = ObjectDB.instance;
			GameObject val = ((instance != null) ? instance.GetItemPrefab(Prefab) : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val.GetComponent<ItemDrop>() == (Object)null)
			{
				throw new InvalidDataException("Unavailable item: " + Prefab);
			}
			ItemData obj = val.GetComponent<ItemDrop>().m_itemData.Clone();
			obj.m_dropPrefab = val;
			obj.m_stack = Stack;
			obj.m_durability = Durability;
			obj.m_quality = Quality;
			obj.m_variant = Variant;
			obj.m_worldLevel = WorldLevel;
			obj.m_crafterID = CrafterId;
			obj.m_crafterName = CrafterName;
			obj.m_pickedUp = PickedUp;
			obj.m_cheated = Cheated;
			obj.m_customData = new Dictionary<string, string>(CustomData);
			obj.m_equipped = false;
			return obj;
		}

		internal string DisplayName()
		{
			ObjectDB instance = ObjectDB.instance;
			GameObject val = ((instance != null) ? instance.GetItemPrefab(Prefab) : null);
			ItemDrop val2 = (((Object)(object)val == (Object)null) ? null : val.GetComponent<ItemDrop>());
			if (!((Object)(object)val2 == (Object)null))
			{
				return Localization.instance.Localize(val2.m_itemData.m_shared.m_name);
			}
			return Prefab;
		}

		internal Sprite Icon()
		{
			ObjectDB instance = ObjectDB.instance;
			GameObject val = ((instance != null) ? instance.GetItemPrefab(Prefab) : null);
			Sprite[] array = (((Object)(object)val == (Object)null) ? null : val.GetComponent<ItemDrop>())?.m_itemData.m_shared.m_icons;
			if (array != null && array.Length != 0)
			{
				return array[0];
			}
			return null;
		}

		internal void Write(BinaryWriter w)
		{
			w.Write(Prefab ?? "");
			w.Write(Stack);
			w.Write(Durability);
			w.Write(Quality);
			w.Write(Variant);
			w.Write(WorldLevel);
			w.Write(CrafterId);
			w.Write(CrafterName ?? "");
			w.Write(PickedUp);
			w.Write(Cheated);
			w.Write(CustomData.Count);
			foreach (KeyValuePair<string, string> customDatum in CustomData)
			{
				w.Write(customDatum.Key);
				w.Write(customDatum.Value);
			}
		}

		internal static ItemBlob Read(BinaryReader r)
		{
			ItemBlob itemBlob = new ItemBlob
			{
				Prefab = r.ReadString(),
				Stack = r.ReadInt32(),
				Durability = r.ReadSingle(),
				Quality = r.ReadInt32(),
				Variant = r.ReadInt32(),
				WorldLevel = r.ReadInt32(),
				CrafterId = r.ReadInt64(),
				CrafterName = r.ReadString(),
				PickedUp = r.ReadBoolean(),
				Cheated = r.ReadBoolean()
			};
			int num = r.ReadInt32();
			if (num < 0 || num > 256)
			{
				throw new InvalidDataException("Invalid item data");
			}
			for (int i = 0; i < num; i++)
			{
				itemBlob.CustomData.Add(r.ReadString(), r.ReadString());
			}
			return itemBlob;
		}

		internal void Validate()
		{
			ObjectDB instance = ObjectDB.instance;
			GameObject val = ((instance != null) ? instance.GetItemPrefab(Prefab) : null);
			ItemDrop val2 = (((Object)(object)val == (Object)null) ? null : val.GetComponent<ItemDrop>());
			if ((Object)(object)val2 == (Object)null || Stack < 1 || Stack > val2.m_itemData.m_shared.m_maxStackSize || Quality < 1 || Quality > val2.m_itemData.m_shared.m_maxQuality || CustomData.Sum((KeyValuePair<string, string> x) => x.Key.Length + x.Value.Length) > 4096)
			{
				throw new InvalidDataException("Unsupported item or stack");
			}
		}
	}
	internal sealed class Listing
	{
		internal Guid Id;

		internal long Seller;

		internal string SellerName;

		internal ItemBlob Item;

		internal int GoldPrice;

		internal bool Active = true;

		internal void Write(BinaryWriter w)
		{
			MarketCodec.WriteGuid(w, Id);
			w.Write(Seller);
			w.Write(SellerName ?? "");
			Item.Write(w);
			w.Write(GoldPrice);
			w.Write(Active);
		}

		internal static Listing Read(BinaryReader r)
		{
			return new Listing
			{
				Id = MarketCodec.ReadGuid(r),
				Seller = r.ReadInt64(),
				SellerName = r.ReadString(),
				Item = ItemBlob.Read(r),
				GoldPrice = r.ReadInt32(),
				Active = r.ReadBoolean()
			};
		}
	}
	internal sealed class Offer
	{
		internal Guid Id;

		internal Guid ListingId;

		internal long Seller;

		internal long Buyer;

		internal string BuyerName;

		internal ItemBlob Item;

		internal bool Active = true;

		internal void Write(BinaryWriter w)
		{
			MarketCodec.WriteGuid(w, Id);
			MarketCodec.WriteGuid(w, ListingId);
			w.Write(Seller);
			w.Write(Buyer);
			w.Write(BuyerName ?? "");
			Item.Write(w);
			w.Write(Active);
		}

		internal static Offer Read(BinaryReader r)
		{
			return new Offer
			{
				Id = MarketCodec.ReadGuid(r),
				ListingId = MarketCodec.ReadGuid(r),
				Seller = r.ReadInt64(),
				Buyer = r.ReadInt64(),
				BuyerName = r.ReadString(),
				Item = ItemBlob.Read(r),
				Active = r.ReadBoolean()
			};
		}
	}
	internal sealed class Claim
	{
		internal Guid Id;

		internal long ShopOwner;

		internal long Recipient;

		internal ItemBlob Item;

		internal bool Collected;

		internal void Write(BinaryWriter w)
		{
			MarketCodec.WriteGuid(w, Id);
			w.Write(ShopOwner);
			w.Write(Recipient);
			Item.Write(w);
			w.Write(Collected);
		}

		internal static Claim Read(BinaryReader r)
		{
			return new Claim
			{
				Id = MarketCodec.ReadGuid(r),
				ShopOwner = r.ReadInt64(),
				Recipient = r.ReadInt64(),
				Item = ItemBlob.Read(r),
				Collected = r.ReadBoolean()
			};
		}
	}
	internal sealed class ShopSummary
	{
		internal long Owner;

		internal string Name;

		internal int Listings;

		internal Vector3 NearestStall;

		internal void Write(BinaryWriter w)
		{
			w.Write(Owner);
			w.Write(Name ?? "");
			w.Write(Listings);
			w.Write(NearestStall.x);
			w.Write(NearestStall.y);
			w.Write(NearestStall.z);
		}

		internal static ShopSummary Read(BinaryReader r)
		{
			//IL_003c: 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)
			return new ShopSummary
			{
				Owner = r.ReadInt64(),
				Name = r.ReadString(),
				Listings = r.ReadInt32(),
				NearestStall = new Vector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle())
			};
		}
	}
	internal sealed class MarketSnapshot
	{
		internal long ShopOwner;

		internal bool NpcEnabled;

		internal bool NpcHereAlive;

		internal bool NpcHostileHere;

		internal float NpcRespawnDays;

		internal List<ShopSummary> Shops = new List<ShopSummary>();

		internal List<Listing> Listings = new List<Listing>();

		internal List<Offer> Offers = new List<Offer>();

		internal List<Claim> Claims = new List<Claim>();

		internal void Write(BinaryWriter w)
		{
			w.Write(ShopOwner);
			w.Write(NpcEnabled);
			w.Write(NpcHereAlive);
			w.Write(NpcHostileHere);
			w.Write(NpcRespawnDays);
			MarketCodec.WriteList(w, Shops, delegate(BinaryWriter x, ShopSummary s)
			{
				s.Write(x);
			});
			MarketCodec.WriteList(w, Listings, delegate(BinaryWriter x, Listing s)
			{
				s.Write(x);
			});
			MarketCodec.WriteList(w, Offers, delegate(BinaryWriter x, Offer s)
			{
				s.Write(x);
			});
			MarketCodec.WriteList(w, Claims, delegate(BinaryWriter x, Claim s)
			{
				s.Write(x);
			});
		}

		internal static MarketSnapshot Read(BinaryReader r)
		{
			return new MarketSnapshot
			{
				ShopOwner = r.ReadInt64(),
				NpcEnabled = r.ReadBoolean(),
				NpcHereAlive = r.ReadBoolean(),
				NpcHostileHere = r.ReadBoolean(),
				NpcRespawnDays = r.ReadSingle(),
				Shops = MarketCodec.ReadList(r, ShopSummary.Read),
				Listings = MarketCodec.ReadList(r, Listing.Read),
				Offers = MarketCodec.ReadList(r, Offer.Read),
				Claims = MarketCodec.ReadList(r, Claim.Read)
			};
		}
	}
	internal static class MarketCodec
	{
		internal const int Protocol = 3;

		internal static void WriteGuid(BinaryWriter w, Guid id)
		{
			w.Write(id.ToByteArray());
		}

		internal static Guid ReadGuid(BinaryReader r)
		{
			byte[] array = r.ReadBytes(16);
			if (array.Length != 16)
			{
				throw new EndOfStreamException();
			}
			return new Guid(array);
		}

		internal static void WriteList<T>(BinaryWriter w, List<T> list, Action<BinaryWriter, T> write)
		{
			w.Write(list.Count);
			foreach (T item in list)
			{
				write(w, item);
			}
		}

		internal static List<T> ReadList<T>(BinaryReader r, Func<BinaryReader, T> read)
		{
			int num = r.ReadInt32();
			if (num < 0 || num > 10000)
			{
				throw new InvalidDataException("Invalid market list");
			}
			List<T> list = new List<T>(num);
			for (int i = 0; i < num; i++)
			{
				list.Add(read(r));
			}
			return list;
		}

		internal static byte[] Encode(Action<BinaryWriter> write)
		{
			using MemoryStream memoryStream = new MemoryStream();
			using BinaryWriter binaryWriter = new BinaryWriter(memoryStream);
			write(binaryWriter);
			binaryWriter.Flush();
			return memoryStream.ToArray();
		}

		internal static T Decode<T>(byte[] bytes, Func<BinaryReader, T> read)
		{
			using MemoryStream input = new MemoryStream(bytes);
			using BinaryReader arg = new BinaryReader(input);
			return read(arg);
		}
	}
	internal enum WireKind : byte
	{
		Request = 1,
		Result,
		Snapshot,
		ClaimGrant,
		ClaimAck
	}
	internal static class MarketTransport
	{
		private sealed class Assembly
		{
			internal DateTime Created = DateTime.UtcNow;

			internal byte[][] Parts;

			internal int Count;
		}

		private const string Rpc = "VM_MarketChunk_1";

		private const int ChunkSize = 3500;

		private static readonly Dictionary<string, Assembly> incoming = new Dictionary<string, Assembly>();

		internal static void Register()
		{
			incoming.Clear();
			ZRoutedRpc.instance.Register<ZPackage>("VM_MarketChunk_1", (Action<long, ZPackage>)Receive);
		}

		internal static void Send(long peer, byte[] data)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			if (data.Length > 1048576)
			{
				throw new InvalidDataException("Market message is too large");
			}
			string text = Guid.NewGuid().ToString("N");
			int num = Math.Max(1, (data.Length + 3500 - 1) / 3500);
			for (int i = 0; i < num; i++)
			{
				int num2 = Math.Min(3500, data.Length - i * 3500);
				byte[] array = new byte[num2];
				Buffer.BlockCopy(data, i * 3500, array, 0, num2);
				ZPackage val = new ZPackage();
				val.Write(text);
				val.Write(i);
				val.Write(num);
				val.Write(array);
				ZRoutedRpc.instance.InvokeRoutedRPC(peer, "VM_MarketChunk_1", new object[1] { val });
			}
		}

		private static void Receive(long sender, ZPackage pkg)
		{
			try
			{
				string text = pkg.ReadString();
				int num = pkg.ReadInt();
				int num2 = pkg.ReadInt();
				byte[] array = pkg.ReadByteArray();
				if (text.Length != 32 || num2 < 1 || num2 > 300 || num < 0 || num >= num2 || array.Length > 3500)
				{
					return;
				}
				string key = sender + ":" + text;
				if (!incoming.TryGetValue(key, out var value))
				{
					Dictionary<string, Assembly> dictionary = incoming;
					Assembly obj = new Assembly
					{
						Parts = new byte[num2][]
					};
					value = obj;
					dictionary[key] = obj;
				}
				if (value.Parts.Length != num2)
				{
					incoming.Remove(key);
					return;
				}
				if (value.Parts[num] == null)
				{
					value.Parts[num] = array;
					value.Count++;
				}
				if (value.Count != num2)
				{
					return;
				}
				incoming.Remove(key);
				using (MemoryStream memoryStream = new MemoryStream())
				{
					byte[][] parts = value.Parts;
					foreach (byte[] array2 in parts)
					{
						memoryStream.Write(array2, 0, array2.Length);
					}
					byte[] array3 = memoryStream.ToArray();
					if (array3.Length > 1048576)
					{
						return;
					}
					WireKind wireKind = (WireKind)((array3.Length != 0) ? array3[0] : 0);
					if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && (wireKind == WireKind.Request || wireKind == WireKind.ClaimAck))
					{
						MarketPlugin.Server?.Handle(sender, array3);
					}
					else
					{
						MarketPlugin.Client?.Handle(sender, array3);
					}
				}
				foreach (string item in (from x in incoming
					where (DateTime.UtcNow - x.Value.Created).TotalSeconds > 30.0
					select x.Key).ToList())
				{
					incoming.Remove(item);
				}
			}
			catch (Exception ex)
			{
				MarketPlugin.Instance.Log.LogWarning((object)("Invalid market packet: " + ex.Message));
			}
		}
	}
	internal sealed class MarketServer
	{
		private readonly MarketStore store;

		private readonly long worldId;

		private bool failed;

		private readonly Dictionary<Guid, long> claimGrants = new Dictionary<Guid, long>();

		internal MarketServer(long worldId)
		{
			this.worldId = worldId;
			try
			{
				store = MarketStore.Open(worldId);
			}
			catch (Exception ex)
			{
				MarketPlugin.Instance.Log.LogError((object)("Market save could not be loaded: " + ex));
			}
		}

		internal void Handle(long sender, byte[] data)
		{
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			Guid requestId = Guid.Empty;
			bool flag = false;
			try
			{
				using MemoryStream input = new MemoryStream(data);
				using BinaryReader binaryReader = new BinaryReader(input);
				WireKind wireKind = (WireKind)binaryReader.ReadByte();
				int num = binaryReader.ReadInt32();
				if (wireKind == WireKind.Request)
				{
					requestId = MarketCodec.ReadGuid(binaryReader);
				}
				if (num != 3)
				{
					throw new InvalidDataException("Incompatible market mod version");
				}
				if (store == null || failed)
				{
					if (wireKind == WireKind.Request)
					{
						SendResult(sender, requestId, ok: false, "Market save is unavailable");
					}
					return;
				}
				switch (wireKind)
				{
				case WireKind.ClaimAck:
				{
					Guid guid = MarketCodec.ReadGuid(binaryReader);
					long num7 = MarketPlugin.PlayerIdForSender(sender);
					if (claimGrants.TryGetValue(guid, out var value) && value == sender && num7 != 0L)
					{
						long num8 = store.FindClaim(guid)?.ShopOwner ?? 0;
						store.MarkCollected(guid, num7);
						flag = true;
						claimGrants.Remove(guid);
						SendResult(sender, guid, ok: true, "Collected");
						if (num8 != 0L)
						{
							SendSnapshot(sender, num8);
						}
					}
					break;
				}
				default:
					throw new InvalidDataException("Unknown market message");
				case WireKind.Request:
				{
					MarketAction marketAction = (MarketAction)binaryReader.ReadByte();
					ZDOID id = default(ZDOID);
					((ZDOID)(ref id))..ctor(binaryReader.ReadInt64(), binaryReader.ReadUInt32());
					long num2 = binaryReader.ReadInt64();
					Guid entityId = MarketCodec.ReadGuid(binaryReader);
					int num3 = binaryReader.ReadInt32();
					ItemBlob itemBlob = (binaryReader.ReadBoolean() ? ItemBlob.Read(binaryReader) : null);
					long num4 = MarketPlugin.PlayerIdForSender(sender);
					if (num4 == 0L)
					{
						throw new InvalidDataException("Player is not ready");
					}
					ZDO val = RequireNearbyStall(sender, id);
					bool flag2 = val.GetPrefab() == StringExtensionMethods.GetStableHashCode("VM_NpcStall");
					long num5 = (flag2 ? WorldNpcManager.ShopFor(val) : val.GetLong(ZDOVars.s_creator, 0L));
					if (flag2 && num5 == 0L)
					{
						throw new InvalidDataException("This trader is still preparing the stall");
					}
					if (flag2 && !MarketPlugin.EnableNpcMerchant.Value && marketAction != MarketAction.Snapshot && marketAction != MarketAction.Collect)
					{
						throw new InvalidDataException("NPC merchant is disabled on this server");
					}
					if (marketAction == MarketAction.Snapshot)
					{
						long num6 = ((num2 == 0L) ? num5 : num2);
						if (num6 == num5)
						{
							GrantClaims(sender, num5, num4);
						}
						SendSnapshot(sender, num6);
						break;
					}
					if (store.WasCompleted(requestId))
					{
						flag = true;
						SendResult(sender, requestId, ok: true, "Already processed");
						GrantClaims(sender, num5, num4);
						SendSnapshot(sender, num5);
						break;
					}
					if (flag2 && marketAction != MarketAction.Collect && !WorldNpcManager.IsAliveAt(val))
					{
						throw new InvalidDataException("The trader will return after two in-game days");
					}
					if (flag2 && marketAction != MarketAction.Collect && WorldNpcManager.IsHostileAt(val))
					{
						throw new InvalidDataException("Edda refuses to trade while hostile");
					}
					string message;
					switch (marketAction)
					{
					case MarketAction.SellToNpc:
						if (!flag2 || itemBlob == null)
						{
							throw new InvalidDataException("Visit Edda's stall with goods to sell");
						}
						message = store.SellToNpc(requestId, num5, num4, itemBlob);
						break;
					case MarketAction.CreateListing:
						if (flag2 || num5 != num4)
						{
							throw new InvalidDataException("Only the shop owner may post here");
						}
						if (itemBlob == null)
						{
							throw new InvalidDataException("Select an item");
						}
						message = store.CreateListing(requestId, num4, MarketPlugin.PlayerNameForSender(sender), itemBlob, num3);
						break;
					case MarketAction.Buy:
						if (flag2)
						{
							message = store.BuyFromNpc(requestId, num4, NpcMerchant.Stock(worldId, WorldNpcManager.GameDay, num5).FirstOrDefault((Listing x) => x.Id == entityId), num3);
						}
						else
						{
							RequireListingShop(entityId, num5);
							message = store.Buy(requestId, entityId, num4, num3);
						}
						break;
					case MarketAction.MakeOffer:
						if (flag2)
						{
							throw new InvalidDataException("Edda does not barter");
						}
						RequireListingShop(entityId, num5);
						if (itemBlob == null)
						{
							throw new InvalidDataException("Select an item");
						}
						message = store.MakeOffer(requestId, entityId, num4, MarketPlugin.PlayerNameForSender(sender), itemBlob);
						break;
					case MarketAction.AcceptOffer:
						RequireOfferShop(entityId, num5);
						message = store.AcceptOffer(requestId, entityId, num4);
						break;
					case MarketAction.RejectOffer:
						RequireOfferShop(entityId, num5);
						message = store.RejectOffer(requestId, entityId, num4);
						break;
					case MarketAction.CancelListing:
						RequireListingShop(entityId, num5);
						message = store.CancelListing(requestId, entityId, num4);
						break;
					case MarketAction.CancelOffer:
						RequireOfferShop(entityId, num5);
						message = store.CancelOffer(requestId, entityId, num4);
						break;
					case MarketAction.Collect:
					{
						Claim claim = store.FindClaim(entityId);
						if (claim == null || claim.ShopOwner != num5 || claim.Recipient != num4)
						{
							throw new InvalidDataException("This collection is unavailable");
						}
						SendClaimGrant(sender, claim);
						return;
					}
					default:
						throw new InvalidDataException("Unknown market action");
					}
					flag = true;
					SendResult(sender, requestId, ok: true, message);
					GrantClaims(sender, num5, num4);
					SendSnapshot(sender, num5);
					break;
				}
				}
			}
			catch (Exception ex)
			{
				if (!flag && (ex is IOException || ex is UnauthorizedAccessException))
				{
					failed = true;
				}
				MarketPlugin.Instance.Log.LogWarning((object)("Market request failed: " + ex.Message));
				if (!flag)
				{
					SendResult(sender, requestId, ok: false, ex.Message);
				}
			}
		}

		private void RequireListingShop(Guid id, long owner)
		{
			Listing listing = store.FindListing(id);
			if (listing == null || listing.Seller != owner)
			{
				throw new InvalidDataException("Visit the selling stall");
			}
		}

		private void RequireOfferShop(Guid id, long owner)
		{
			Offer offer = store.FindOffer(id);
			if (offer == null || offer.Seller != owner)
			{
				throw new InvalidDataException("Visit the selling stall");
			}
		}

		private ZDO RequireNearbyStall(long sender, ZDOID id)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: 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_00ac: 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)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			ZDOMan instance = ZDOMan.instance;
			ZDO val = ((instance != null) ? instance.GetZDO(id) : null);
			if (val == null || !val.IsValid() || (val.GetPrefab() != StringExtensionMethods.GetStableHashCode("VM_MarketStall") && val.GetPrefab() != StringExtensionMethods.GetStableHashCode("VM_NpcStall")))
			{
				throw new InvalidDataException("Market stall is unavailable");
			}
			Vector3 val2;
			if (sender == ZNet.GetUID())
			{
				if ((Object)(object)Player.m_localPlayer == (Object)null)
				{
					throw new InvalidDataException("Player is not ready");
				}
				val2 = ((Component)Player.m_localPlayer).transform.position;
			}
			else
			{
				ZNetPeer peer = ZNet.instance.GetPeer(sender);
				if (peer == null || ((ZDOID)(ref peer.m_characterID)).IsNone())
				{
					throw new InvalidDataException("Player is not ready");
				}
				val2 = peer.m_refPos;
			}
			if (Vector3.Distance(val2, val.GetPosition()) > 8f)
			{
				throw new InvalidDataException("Move closer to the market stall");
			}
			return val;
		}

		private List<ZDO> AllStalls()
		{
			List<ZDO> list = WorldNpcManager.All("VM_MarketStall");
			list.AddRange(WorldNpcManager.All("VM_NpcStall"));
			return list;
		}

		private void GrantClaims(long peer, long shopOwner, long recipient)
		{
			foreach (Claim item in store.Claims.Where((Claim x) => !x.Collected && x.ShopOwner == shopOwner && x.Recipient == recipient).ToList())
			{
				SendClaimGrant(peer, item);
			}
		}

		private void SendClaimGrant(long peer, Claim claim)
		{
			claimGrants[claim.Id] = peer;
			MarketTransport.Send(peer, MarketCodec.Encode(delegate(BinaryWriter w)
			{
				w.Write((byte)4);
				w.Write(3);
				MarketCodec.WriteGuid(w, claim.Id);
				claim.Item.Write(w);
			}));
		}

		private void SendSnapshot(long peer, long selectedOwner)
		{
			//IL_004c: 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_005d: 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_049e: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0266: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			List<ZDO> source = AllStalls();
			long actor = MarketPlugin.PlayerIdForSender(peer);
			Vector3 position = ((peer == ZNet.GetUID() && (Object)(object)Player.m_localPlayer != (Object)null) ? ((Component)Player.m_localPlayer).transform.position : (ZNet.instance.GetPeer(peer)?.m_refPos ?? Vector3.zero));
			MarketSnapshot snapshot = new MarketSnapshot
			{
				ShopOwner = selectedOwner,
				NpcEnabled = MarketPlugin.EnableNpcMerchant.Value
			};
			List<ZDO> list = source.Where((ZDO x) => x.GetPrefab() == StringExtensionMethods.GetStableHashCode("VM_NpcStall")).ToList();
			HashSet<string> livingSites = new HashSet<string>(from x in WorldNpcManager.All("VM_NpcTrader")
				where x.GetFloat(ZDOVars.s_health, 1f) > 0f
				select x.GetString("vm_npc_site", ""));
			if (list.Count > 0)
			{
				foreach (IGrouping<long, ZDO> item in from x in list.GroupBy(WorldNpcManager.ShopFor)
					where NpcMerchant.IsOwner(x.Key)
					select x)
				{
					ZDO val = (from x in item
						orderby (!livingSites.Contains(x.GetString("vm_npc_site", ""))) ? 1 : 0, Vector3.Distance(position, x.GetPosition())
						select x).First();
					bool flag = snapshot.NpcEnabled && item.Any((ZDO x) => livingSites.Contains(x.GetString("vm_npc_site", "")));
					snapshot.Shops.Add(new ShopSummary
					{
						Owner = item.Key,
						Name = NpcMerchant.ShopName(item.Key),
						Listings = (flag ? NpcMerchant.Stock(worldId, WorldNpcManager.GameDay, item.Key).Count : 0),
						NearestStall = val.GetPosition()
					});
				}
				ZDO val2 = (from x in list
					where WorldNpcManager.ShopFor(x) == selectedOwner && Vector3.Distance(position, x.GetPosition()) < 9f
					orderby Vector3.Distance(position, x.GetPosition())
					select x).FirstOrDefault();
				if (val2 != null)
				{
					snapshot.NpcHereAlive = snapshot.NpcEnabled && livingSites.Contains(val2.GetString("vm_npc_site", ""));
					snapshot.NpcHostileHere = snapshot.NpcHereAlive && WorldNpcManager.IsHostileAt(val2);
					snapshot.NpcRespawnDays = WorldNpcManager.DaysUntilReturn(val2);
				}
			}
			foreach (IGrouping<long, ZDO> group in from x in source
				where x.GetPrefab() == StringExtensionMethods.GetStableHashCode("VM_MarketStall")
				group x by x.GetLong(ZDOVars.s_creator, 0L) into x
				where x.Key != 0
				select x)
			{
				ZDO val3 = group.OrderBy((ZDO x) => Vector3.Distance(position, x.GetPosition())).First();
				Listing listing = store.Listings.FirstOrDefault((Listing x) => x.Active && x.Seller == group.Key);
				snapshot.Shops.Add(new ShopSummary
				{
					Owner = group.Key,
					Name = (listing?.SellerName ?? ((group.Key == actor) ? "Your shop" : ("Viking " + group.Key))),
					Listings = store.Listings.Count((Listing x) => x.Active && x.Seller == group.Key),
					NearestStall = val3.GetPosition()
				});
			}
			if (snapshot.Shops.Any((ShopSummary x) => x.Owner == selectedOwner))
			{
				if (NpcMerchant.IsOwner(selectedOwner))
				{
					if (snapshot.NpcEnabled && list.Any((ZDO x) => WorldNpcManager.ShopFor(x) == selectedOwner && livingSites.Contains(x.GetString("vm_npc_site", ""))))
					{
						snapshot.Listings.AddRange(NpcMerchant.Stock(worldId, WorldNpcManager.GameDay, selectedOwner));
					}
				}
				else
				{
					snapshot.Listings.AddRange(store.Listings.Where((Listing x) => x.Active && x.Seller == selectedOwner));
				}
				snapshot.Offers.AddRange(store.Offers.Where((Offer x) => x.Active && x.Seller == selectedOwner && (x.Buyer == actor || x.Seller == actor)));
			}
			snapshot.Claims.AddRange(store.Claims.Where((Claim x) => !x.Collected && x.ShopOwner == selectedOwner && x.Recipient == actor));
			MarketTransport.Send(peer, MarketCodec.Encode(delegate(BinaryWriter w)
			{
				w.Write((byte)3);
				w.Write(3);
				snapshot.Write(w);
			}));
		}

		private static void SendResult(long peer, Guid requestId, bool ok, string message)
		{
			MarketTransport.Send(peer, MarketCodec.Encode(delegate(BinaryWriter w)
			{
				w.Write((byte)2);
				w.Write(3);
				MarketCodec.WriteGuid(w, requestId);
				w.Write(ok);
				w.Write(message ?? "");
			}));
		}
	}
	[BepInPlugin("dev.monokaijs.valheimmarket", "Valheim Marketplace", "1.1.3")]
	public sealed class MarketPlugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		private static class ScenePatch
		{
			private static void Prefix(ZNetScene __instance)
			{
				RegisterStall(__instance);
			}
		}

		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		private static class ObjectDbPatch
		{
			private static void Postfix()
			{
				TryAddToHammer();
			}
		}

		[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
		private static class CopyDbPatch
		{
			private static void Postfix()
			{
				TryAddToHammer();
			}
		}

		[HarmonyPatch(typeof(ZNet), "Awake")]
		private static class NetworkPatch
		{
			private static void Postfix(ZNet __instance)
			{
				MarketTransport.Register();
				Server = (__instance.IsServer() ? new MarketServer(__instance.GetWorldUID()) : null);
				NpcManager = (__instance.IsServer() ? new WorldNpcManager(__instance.GetWorldUID()) : null);
				Client.Reset();
			}
		}

		[HarmonyPatch(typeof(ZNet), "Shutdown")]
		private static class ShutdownPatch
		{
			private static void Prefix()
			{
				UI?.Close();
				Server = null;
				NpcManager = null;
				Client?.Reset();
			}
		}

		[HarmonyPatch(typeof(Player), "TakeInput")]
		private static class InputPatch
		{
			private static void Postfix(ref bool __result)
			{
				if (UI != null && UI.BlocksGameInput)
				{
					__result = false;
				}
			}
		}

		[HarmonyPatch(typeof(PlayerController), "TakeInput")]
		private static class ControllerInputPatch
		{
			private static void Postfix(ref bool __result)
			{
				if (UI != null && UI.BlocksGameInput)
				{
					__result = false;
				}
			}
		}

		[HarmonyPatch(typeof(Player), "PlayerAttackInput")]
		private static class AttackInputPatch
		{
			private static bool Prefix()
			{
				if (UI != null)
				{
					return !UI.BlocksGameInput;
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(GameCamera), "UpdateCamera")]
		private static class CameraInputPatch
		{
			private static bool Prefix()
			{
				if (UI != null)
				{
					return !UI.IsOpen;
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(Minimap), "AddPin")]
		private static class NpcPinPatch
		{
			private static void Postfix(Minimap __instance, PinData __result)
			{
				NpcMapPins.Style(__instance, __result);
			}
		}

		[HarmonyPatch(typeof(GameCamera), "UpdateMouseCapture")]
		private static class CursorPatch
		{
			private static bool Prefix()
			{
				if (UI == null || !UI.IsOpen)
				{
					return true;
				}
				ZCursor.LockState = (CursorLockMode)0;
				ZCursor.Show();
				return false;
			}
		}

		[HarmonyPatch(typeof(Piece), "CanBeRemoved")]
		private static class RemovePiecePatch
		{
			private static void Postfix(Piece __instance, ref bool __result)
			{
				if (!((Object)(object)((Component)__instance).GetComponent<MarketStall>() == (Object)null))
				{
					__result = __result && (Object)(object)Player.m_localPlayer != (Object)null && __instance.GetCreator() == Player.m_localPlayer.GetPlayerID();
				}
			}
		}

		[HarmonyPatch(typeof(WearNTear), "RPC_Remove")]
		private static class RemoteRemovePatch
		{
			private static bool Prefix(WearNTear __instance, long sender)
			{
				MarketStall component = ((Component)__instance).GetComponent<MarketStall>();
				if (!((Object)(object)component == (Object)null))
				{
					if (component.OwnerId != 0L)
					{
						return component.OwnerId == PlayerIdForSender(sender);
					}
					return false;
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(WearNTear), "RPC_Damage")]
		private static class DamagePatch
		{
			private static bool Prefix(WearNTear __instance)
			{
				return (Object)(object)((Component)__instance).GetComponent<MarketStall>() == (Object)null;
			}
		}

		[HarmonyPatch(typeof(WearNTear), "UpdateWear")]
		private static class WearPatch
		{
			private static bool Prefix(WearNTear __instance)
			{
				return (Object)(object)((Component)__instance).GetComponent<MarketStall>() == (Object)null;
			}
		}

		[HarmonyPatch(typeof(Character), "GetHoverText")]
		private static class MerchantHoverPatch
		{
			private static void Postfix(Character __instance, ref string __result)
			{
				MarketNpcCharacter component = ((Component)__instance).GetComponent<MarketNpcCharacter>();
				if ((Object)(object)component != (Object)null)
				{
					__result = component.HoverText();
				}
			}
		}

		internal const string StallPrefab = "VM_MarketStall";

		internal const string NpcStallPrefab = "VM_NpcStall";

		internal const string NpcCharacterPrefabName = "VM_NpcTrader";

		internal static MarketPlugin Instance;

		internal static GameObject Prefab;

		internal static GameObject NpcPrefab;

		internal static GameObject NpcCharacterPrefab;

		internal static WorldNpcManager NpcManager;

		internal static ConfigEntry<bool> EnableNpcMerchant;

		internal static MarketServer Server;

		internal static MarketClient Client;

		internal static MarketUI UI;

		private Harmony harmony;

		internal ManualLogSource Log => ((BaseUnityPlugin)this).Logger;

		private void Awake()
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			Instance = this;
			EnableNpcMerchant = ((BaseUnityPlugin)this).Config.Bind<bool>("NPC Merchant", "Enabled", true, "Allow the server to place regional traders and enable their trades. Enabled by default.");
			Client = new MarketClient();
			harmony = new Harmony("dev.monokaijs.valheimmarket");
			harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Marketplace loaded without Jotunn");
		}

		private void Update()
		{
			Client?.Update();
			UI?.Tick();
			NpcManager?.Tick();
			TryAddToHammer();
		}

		private void OnDestroy()
		{
			UI?.Close();
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			Server = null;
			NpcManager = null;
			Instance = null;
		}

		internal static void RegisterStall(ZNetScene scene)
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			if (scene.m_prefabs.Any((GameObject x) => (Object)(object)x != (Object)null && ((Object)x).name == "VM_MarketStall"))
			{
				return;
			}
			GameObject val = ((IEnumerable<GameObject>)scene.m_prefabs).FirstOrDefault((Func<GameObject, bool>)((GameObject x) => (Object)(object)x != (Object)null && ((Object)x).name == "piece_chest_wood"));
			if ((Object)(object)val == (Object)null)
			{
				((BaseUnityPlugin)Instance).Logger.LogError((object)"Could not find piece_chest_wood to create a market stall");
				return;
			}
			GameObject val2 = new GameObject("VM_Prefabs");
			val2.transform.SetParent(((Component)scene).transform, false);
			val2.SetActive(false);
			GameObject val3 = Object.Instantiate<GameObject>(val, val2.transform);
			((Object)val3).name = "VM_MarketStall";
			val3.SetActive(true);
			Container[] componentsInChildren = val3.GetComponentsInChildren<Container>(true);
			for (int num = 0; num < componentsInChildren.Length; num++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren[num]);
			}
			Piece component = val3.GetComponent<Piece>();
			component.m_name = "Market Stall";
			component.m_description = "Buy, sell, and barter with other Vikings";
			component.m_category = (PieceCategory)4;
			component.m_usage = (UsageTagFlags)1024;
			component.m_canBeRemoved = true;
			component.m_icon = LoadStallIcon();
			WearNTear component2 = val3.GetComponent<WearNTear>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.m_noRoofWear = false;
			}
			ZNetView component3 = val3.GetComponent<ZNetView>();
			if ((Object)(object)component3 != (Object)null)
			{
				component3.m_persistent = true;
			}
			GameObject? obj = ((IEnumerable<GameObject>)scene.m_prefabs).FirstOrDefault((Func<GameObject, bool>)((GameObject x) => (Object)(object)x != (Object)null && ((Object)x).name == "wood_wall"));
			Material gameWood = ((obj != null) ? obj.GetComponentsInChildren<MeshRenderer>(true).SelectMany((MeshRenderer x) => ((Renderer)x).sharedMaterials).FirstOrDefault((Func<Material, bool>)((Material x) => (Object)(object)x != (Object)null && (Object)(object)x.mainTexture != (Object)null)) : null);
			MarketStallModel.Build(val3, gameWood);
			val3.AddComponent<MarketStall>();
			scene.m_prefabs.Add(val3);
			Prefab = val3;
			GameObject val4 = Object.Instantiate<GameObject>(val3, val2.transform);
			((Object)val4).name = "VM_NpcStall";
			val4.GetComponent<Piece>().m_name = "Edda's Trading Stall";
			val4.GetComponent<Piece>().m_description = "Sell gathered goods for coins and buy rare necessities";
			MarketStallModel.BuildNpc(val4);
			scene.m_prefabs.Add(val4);
			NpcPrefab = val4;
			RegisterMerchantCharacter(scene, val2.transform);
			TryAddToHammer();
		}

		private static Sprite LoadStallIcon()
		{
			//IL_0063: 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_0074: Expected O, but got Unknown
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ValheimMarket.StallIcon.rgba");
			if (stream == null)
			{
				throw new InvalidOperationException("Market stall icon resource is missing");
			}
			byte[] array = new byte[65536];
			using (BinaryReader binaryReader = new BinaryReader(stream))
			{
				if (binaryReader.Read(array, 0, array.Length) != array.Length)
				{
					throw new InvalidOperationException("Market stall icon resource is incomplete");
				}
			}
			Texture2D val = new Texture2D(128, 128, (TextureFormat)4, false)
			{
				name = "Market stall icon"
			};
			val.LoadRawTextureData(array);
			val.Apply(false, true);
			return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
		}

		private static void RegisterMerchantCharacter(ZNetScene scene, Transform holder)
		{
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: 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_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Expected O, but got Unknown
			GameObject val = new string[6] { "DvergerRogue", "DvergerMage", "DvergerMageFire", "DvergerMageIce", "DvergerMageSupport", "Dverger" }.Select((string name) => ((IEnumerable<GameObject>)scene.m_prefabs).FirstOrDefault((Func<GameObject, bool>)((GameObject x) => (Object)(object)x != (Object)null && ((Object)x).name == name && (Object)(object)x.GetComponent<Humanoid>() != (Object)null && (Object)(object)x.GetComponent<MonsterAI>() != (Object)null))).FirstOrDefault<GameObject>((Func<GameObject, bool>)((GameObject x) => (Object)(object)x != (Object)null));
			if ((Object)(object)val == (Object)null)
			{
				((BaseUnityPlugin)Instance).Logger.LogError((object)"Could not find a Dverger character for the regional trader");
				return;
			}
			GameObject val2 = Object.Instantiate<GameObject>(val, holder);
			((Object)val2).name = "VM_NpcTrader";
			val2.SetActive(true);
			Character component = val2.GetComponent<Character>();
			component.m_name = "Edda the Trader";
			component.m_faction = (Faction)10;
			component.m_health = 550f;
			component.m_group = "VM_NpcTraders";
			component.m_boss = false;
			component.m_defeatSetGlobalKey = "";
			component.m_killedForAchievements = (AchievementInclusion)2;
			component.m_damageModifiers.m_frost = (DamageModifier)3;
			component.m_damageModifiers.m_poison = (DamageModifier)1;
			MonsterAI component2 = val2.GetComponent<MonsterAI>();
			((BaseAI)component2).m_aggravatable = true;
			component2.m_attackPlayerObjects = false;
			component2.m_enableHuntPlayer = false;
			((BaseAI)component2).m_randomMoveRange = 2f;
			component2.m_maxChaseDistance = 26f;
			if ((Object)(object)val2.GetComponent<Talker>() == (Object)null)
			{
				val2.AddComponent<Talker>();
			}
			val2.AddComponent<MarketNpcCharacter>();
			ZNetView component3 = val2.GetComponent<ZNetView>();
			if ((Object)(object)component3 != (Object)null)
			{
				component3.m_persistent = true;
			}
			CharacterDrop obj = val2.GetComponent<CharacterDrop>() ?? val2.AddComponent<CharacterDrop>();
			object obj2 = ((IEnumerable<GameObject>)scene.m_prefabs).FirstOrDefault((Func<GameObject, bool>)((GameObject x) => (Object)(object)x != (Object)null && ((Object)x).name == "Coins"));
			if (obj2 == null)
			{
				ObjectDB instance = ObjectDB.instance;
				obj2 = ((instance != null) ? instance.GetItemPrefab("Coins") : null);
			}
			GameObject val3 = (GameObject)obj2;
			obj.m_drops = (((Object)(object)val3 == (Object)null) ? new List<Drop>() : new List<Drop>
			{
				new Drop
				{
					m_prefab = val3,
					m_amountMin = 15,
					m_amountMax = 30,
					m_chance = 1f,
					m_levelMultiplier = false,
					m_dontScale = true
				}
			});
			if ((Object)(object)val3 == (Object)null)
			{
				((BaseUnityPlugin)Instance).Logger.LogWarning((object)"Trader coin drops are unavailable: Coins prefab missing");
			}
			scene.m_prefabs.Add(val2);
			NpcCharacterPrefab = val2;
		}

		internal static void ConfigureMerchantDrops()
		{
			//IL_0051: 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_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			GameObject npcCharacterPrefab = NpcCharacterPrefab;
			CharacterDrop val = ((npcCharacterPrefab != null) ? npcCharacterPrefab.GetComponent<CharacterDrop>() : null);
			if (!((Object)(object)val == (Object)null) && val.m_drops.Count == 0)
			{
				ObjectDB instance = ObjectDB.instance;
				GameObject val2 = ((instance != null) ? instance.GetItemPrefab("Coins") : null);
				if (!((Object)(object)val2 == (Object)null))
				{
					val.m_drops = new List<Drop>
					{
						new Drop
						{
							m_prefab = val2,
							m_amountMin = 15,
							m_amountMax = 30,
							m_chance = 1f,
							m_levelMultiplier = false,
							m_dontScale = true
						}
					};
				}
			}
		}

		internal static void TryAddToHammer()
		{
			if (!((Object)(object)Prefab == (Object)null) && !((Object)(object)ObjectDB.instance == (Object)null))
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Hammer");
				PieceTable val = (((Object)(object)itemPrefab == (Object)null) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_buildPieces);
				if ((Object)(object)val != (Object)null && !val.m_pieces.Contains(Prefab))
				{
					val.m_pieces.Add(Prefab);
				}
				if ((Object)(object)val != (Object)null && (Object)(object)NpcPrefab != (Object)null)
				{
					val.m_pieces.Remove(NpcPrefab);
				}
			}
		}

		internal static long PlayerIdForSender(long sender)
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return 0L;
			}
			if (sender == ZNet.GetUID())
			{
				if (!((Object)(object)Player.m_localPlayer == (Object)null))
				{
					return Player.m_localPlayer.GetPlayerID();
				}
				return 0L;
			}
			return ZNet.instance.GetPeer(sender)?.m_playerID ?? 0;
		}

		internal static string PlayerNameForSender(long sender)
		{
			if (sender == ZNet.GetUID())
			{
				if (!((Object)(object)Player.m_localPlayer == (Object)null))
				{
					return Player.m_localPlayer.GetPlayerName();
				}
				return "Viking";
			}
			return ZNet.instance.GetPeer(sender)?.m_playerName ?? "Viking";
		}
	}
	internal sealed class MarketStall : MonoBehaviour, Interactable, Hoverable
	{
		private Piece piece;

		private ZNetView view;

		private float nextNpcPinCheck;

		internal long OwnerId
		{
			get
			{
				if (!((Object)(object)piece == (Object)null))
				{
					return piece.GetCreator();
				}
				return 0L;
			}
		}

		internal bool IsNpc => ((Object)((Component)this).gameObject).name.StartsWith("VM_NpcStall", StringComparison.Ordinal);

		internal long ShopId
		{
			get
			{
				//IL_0066: Unknown result type (might be due to invalid IL or missing references)
				//IL_006b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0071: Expected I4, but got Unknown
				if (!IsNpc)
				{
					return OwnerId;
				}
				ZNetView obj = view;
				int? obj2;
				if (obj == null)
				{
					obj2 = null;
				}
				else
				{
					ZDO zDO = obj.GetZDO();
					obj2 = ((zDO != null) ? new int?(zDO.GetInt("vm_npc_biome", 0)) : ((int?)null));
				}
				int? num = obj2;
				int num2 = num.GetValueOrDefault();
				if (num2 == 0 && WorldGenerator.instance != null)
				{
					num2 = (int)WorldGenerator.instance.GetBiome(((Component)this).transform.position);
				}
				return NpcMerchant.OwnerForBiome(num2);
			}
		}

		internal ZDOID Id
		{
			get
			{
				//IL_002c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)view == (Object)null) && view.GetZDO() != null)
				{
					return view.GetZDO().m_uid;
				}
				return ZDOID.None;
			}
		}

		private void Awake()
		{
			piece = ((Component)this).GetComponent<Piece>();
			view = ((Component)this).GetComponent<ZNetView>();
		}

		private void Update()
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: 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)
			if (IsNpc && !(Time.unscaledTime < nextNpcPinCheck) && !((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)Minimap.instance == (Object)null))
			{
				nextNpcPinCheck = Time.unscaledTime + 2f;
				ZNetView obj = view;
				if (((obj != null) ? obj.GetZDO() : null) != null && !(Utils.DistanceXZ(((Component)Player.m_localPlayer).transform.position, ((Component)this).transform.position) > Mathf.Min(90f, Minimap.instance.m_exploreRadius)))
				{
					NpcMapPins.Discover(((Component)this).transform.position, ShopId);
				}
			}
		}

		public string GetHoverName()
		{
			if (!IsNpc)
			{
				return "Market Stall";
			}
			return "Edda the Trader";
		}

		public float GetHoverOffset()
		{
			return 0f;
		}

		public string GetHoverText()
		{
			return Localization.instance.Localize((IsNpc ? "Edda the Trader" : "Market Stall") + "\n[<color=yellow><b>$KEY_Use</b></color>] " + (IsNpc ? "Trade with Edda" : "Open market"));
		}

		public bool Interact(Humanoid user, bool hold, bool alt)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			if (!hold && !((Object)(object)user != (Object)(object)Player.m_localPlayer))
			{
				ZDOID id = Id;
				if (!((ZDOID)(ref id)).IsNone())
				{
					if (IsNpc && ShopId == 0L)
					{
						((Character)user).Message((MessageType)2, "The trader is preparing this stall.", 0, (Sprite)null, false);
						return true;
					}
					if (MarketPlugin.UI == null)
					{
						MarketPlugin.UI = new MarketUI();
					}
					MarketPlugin.UI.Open(this);
					return true;
				}
			}
			return false;
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			return false;
		}
	}
	internal static class MarketStallModel
	{
		private static Shader fallbackShader;

		internal static void Build(GameObject prefab, Material gameWood)
		{
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_030a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0336: Unknown result type (might be due to invalid IL or missing references)
			//IL_034a: Unknown result type (might be due to invalid IL or missing references)
			//IL_039a: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0527: Unknown result type (might be due to invalid IL or missing references)
			//IL_053b: Unknown result type (might be due to invalid IL or missing references)
			//IL_055d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0571: Unknown result type (might be due to invalid IL or missing references)
			//IL_0593: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0623: Unknown result type (might be due to invalid IL or missing references)
			//IL_0637: Unknown result type (might be due to invalid IL or missing references)
			//IL_0684: Unknown result type (might be due to invalid IL or missing references)
			//IL_06a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_06f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0704: Unknown result type (might be due to invalid IL or missing references)
			//IL_0731: Unknown result type (might be due to invalid IL or missing references)
			//IL_0745: Unknown result type (might be due to invalid IL or missing references)
			//IL_0762: Unknown result type (might be due to invalid IL or missing references)
			//IL_07b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_07c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_07f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0807: Unknown result type (might be due to invalid IL or missing references)
			//IL_0829: Unknown result type (might be due to invalid IL or missing references)
			//IL_083d: Unknown result type (might be due to invalid IL or missing references)
			//IL_085a: Unknown result type (might be due to invalid IL or missing references)
			//IL_088b: Unknown result type (might be due to invalid IL or missing references)
			//IL_089f: Unknown result type (might be due to invalid IL or missing references)
			//IL_08d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_08f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_090d: Unknown result type (might be due to invalid IL or missing references)
			//IL_092a: Unknown result type (might be due to invalid IL or missing references)
			//IL_094c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0960: Unknown result type (might be due to invalid IL or missing references)
			//IL_0971: Unknown result type (might be due to invalid IL or missing references)
			//IL_0976: Unknown result type (might be due to invalid IL or missing references)
			//IL_097d: Unknown result type (might be due to invalid IL or missing references)
			//IL_098f: Unknown result type (might be due to invalid IL or missing references)
			//IL_09a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_09be: Unknown result type (might be due to invalid IL or missing references)
			//IL_09dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_09f6: Unknown result type (might be due to invalid IL or missing references)
			MeshRenderer val = prefab.GetComponentsInChildren<MeshRenderer>(true).FirstOrDefault();
			Material val2 = (((Object)(object)gameWood != (Object)null) ? gameWood : (((Object)(object)val == (Object)null) ? null : ((Renderer)val).sharedMaterial));
			fallbackShader = (((Object)(object)val2 == (Object)null) ? null : val2.shader);
			Collider val3 = prefab.GetComponentsInChildren<Collider>(true).FirstOrDefault();
			int layer = (((Object)(object)val3 == (Object)null) ? prefab.layer : ((Component)val3).gameObject.layer);
			MeshRenderer[] componentsInChildren = prefab.GetComponentsInChildren<MeshRenderer>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				((Renderer)componentsInChildren[i]).enabled = false;
			}
			Collider[] componentsInChildren2 = prefab.GetComponentsInChildren<Collider>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren2[i]);
			}
			Material val4 = Wood(val2, new Color(0.49f, 0.34f, 0.22f));
			Material val5 = Wood(val2, new Color(0.32f, 0.22f, 0.15f));
			Material material = Plain("Stall canvas", new Color(0.66f, 0.48f, 0.29f));
			Material val6 = Plain("Stall red canvas", new Color(0.42f, 0.18f, 0.15f));
			Material val7 = Plain("Stall rope", new Color(0.65f, 0.55f, 0.37f));
			Material material2 = Plain("Stall produce", new Color(0.29f, 0.37f, 0.17f));
			Material material3 = Plain("Stall apples", new Color(0.55f, 0.19f, 0.12f));
			Material pottery = Plain("Stall pottery", new Color(0.4f, 0.23f, 0.16f));
			Material val8 = Plain("Stall linen", new Color(0.61f, 0.54f, 0.4f));
			Transform transform = new GameObject("Timber and canvas market stall").transform;
			transform.SetParent(prefab.transform, false);
			((Component)transform).gameObject.layer = prefab.layer;
			float[] array = new float[2] { -1.55f, 1.55f };
			foreach (float num in array)
			{
				Block(transform, "Front timber post", new Vector3(num, 1.23f, -0.78f), new Vector3(0.2f, 2.46f, 0.2f), val4);
				Block(transform, "Back timber post", new Vector3(num, 1.46f, 0.78f), new Vector3(0.2f, 2.92f, 0.2f), val4);
				Lash(transform, num, -0.78f, 0.68f, val7);
				Lash(transform, num, -0.78f, 2.28f, val7);
				Lash(transform, num, 0.78f, 2.67f, val7);
			}
			Beam(transform, "Front awning beam", new Vector3(-1.72f, 2.39f, -0.8f), new Vector3(1.72f, 2.39f, -0.8f), 0.14f, 0.14f, val5);
			Beam(transform, "Back awning beam", new Vector3(-1.7f, 2.96f, 0.78f), new Vector3(1.7f, 2.96f, 0.78f), 0.14f, 0.14f, val5);
			array = new float[2] { -1.55f, 1.55f };
			foreach (float num2 in array)
			{
				Beam(transform, "Awning side rail", new Vector3(num2, 2.4f, -0.82f), new Vector3(num2, 2.96f, 0.8f), 0.11f, 0.11f, val4);
				Beam(transform, "Front diagonal brace", new Vector3(num2, 1.45f, -0.78f), new Vector3(num2 - Mathf.Sign(num2) * 0.52f, 2.34f, -0.78f), 0.11f, 0.11f, val5);
			}
			Canopy(transform, "Ochre canvas roof", -0.36f, 0.92f, 2.55f, 3.02f, material);
			Canopy(transform, "Red canvas roof band", -0.97f, -0.36f, 2.39f, 2.55f, val6);
			for (int j = 0; j < 7; j++)
			{
				Flap(transform, -1.78f + (float)j * 3.56f / 7f, 89f / 175f, val6);
			}
			Beam(transform, "Canvas front hem", new Vector3(-1.77f, 2.38f, -0.97f), new Vector3(1.77f, 2.38f, -0.97f), 0.025f, 0.025f, val7);
			Beam(transform, "Roof stitched seam", new Vector3(0.15f, 2.55f, -0.35f), new Vector3(0.15f, 3.02f, 0.92f), 0.014f, 0.014f, val7);
			Block(transform, "Rear canvas wall", new Vector3(0f, 1.85f, 0.84f), new Vector3(3.12f, 1.86f, 0.055f), material);
			Block(transform, "Right canvas side", new Vector3(1.61f, 1.81f, 0.17f), new Vector3(0.055f, 1.75f, 1.2f), material);
			Block(transform, "Counter top", new Vector3(0f, 0.99f, -0.52f), new Vector3(3.05f, 0.13f, 0.88f), val4);
			for (int k = 0; k < 8; k++)
			{
				Block(transform, "Counter front plank", new Vector3(-1.32f + (float)k * 0.377f, 0.52f, -0.92f), new Vector3(0.35f, 0.72f, 0.09f), (k % 3 == 0) ? val5 : val4);
			}
			Beam(transform, "Counter bottom rail", new Vector3(-1.49f, 0.17f, -0.97f), new Vector3(1.49f, 0.17f, -0.97f), 0.1f, 0.12f, val5);
			array = new float[2] { -1.34f, 1.34f };
			foreach (float num3 in array)
			{
				Beam(transform, "Counter diagonal", new Vector3(num3, 0.17f, -0.98f), new Vector3(num3 - Mathf.Sign(num3) * 0.56f, 0.89f, -0.98f), 0.09f, 0.09f, val5);
			}
			for (int l = 0; l < 2; l++)
			{
				float num4 = 1.42f + (float)l * 0.44f;
				Block(transform, "Merchant shelf", new Vector3(0.83f, num4, 0.44f), new Vector3(1.35f, 0.095f, 0.61f), val4);
			}
			Block(transform, "Shelf divider", new Vector3(0.12f, 1.58f, 0.43f), new Vector3(0.11f, 1.28f, 0.68f), val5);
			Crate(transform, new Vector3(-1.08f, 1.13f, -0.49f), 0.52f, val4, val5);
			for (int m = 0; m < 5; m++)
			{
				Round(transform, "Apple", new Vector3(-1.26f + (float)m * 0.105f, 1.25f + (float)(m % 2) * 0.035f, -0.49f + (float)(m % 2) * 0.1f), new Vector3(0.12f, 0.12f, 0.12f), material3);
			}
			Round(transform, "Cabbage", new Vector3(-0.35f, 1.16f, -0.44f), new Vector3(0.29f, 0.24f, 0.29f), material2);
			Round(transform, "Cabbage", new Vector3(-0.02f, 1.14f, -0.5f), new Vector3(0.25f, 0.22f, 0.25f), material2);
			Pot(transform, new Vector3(0.63f, 1.09f, -0.5f), pottery);
			for (int n = 0; n < 3; n++)
			{
				Block(transform, "Folded trade cloth", new Vector3(1.15f, 1.12f + (float)n * 0.08f, -0.51f), new Vector3(0.57f, 0.07f, 0.37f), (n == 1) ? val6 : val8);
			}
			Crate(transform, new Vector3(1.09f, 1.55f, 0.4f), 0.4f, val4, val5);
			Round(transform, "Sack of goods", new Vector3(0.52f, 1.58f, 0.42f), new Vector3(0.25f, 0.3f, 0.23f), val8);
			Pot(transform, new Vector3(0.67f, 1.94f, 0.4f), pottery);
			Round(transform, "Sack of goods", new Vector3(1.1f, 2.01f, 0.42f), new Vector3(0.25f, 0.28f, 0.23f), val8);
			GameObject val9 = new GameObject("Market stall interaction hitboxes")
			{
				layer = layer
			};
			val9.transform.SetParent(prefab.transform, false);
			BoxCollider obj = val9.AddComponent<BoxCollider>();
			obj.center = new Vector3(0f, 1.13f, -0.76f);
			obj.size = new Vector3(3.3f, 2.26f, 0.45f);
			BoxCollider obj2 = val9.AddComponent<BoxCollider>();
			obj2.center = new Vector3(0f, 1.02f, 0.8f);
			obj2.size = new Vector3(3.24f, 2.04f, 0.22f);
		}

		internal static void BuildNpc(GameObject prefab)
		{
			//IL_0014: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: 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_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			Material material = Plain("Merchant sign cloth", new Color(0.16f, 0.27f, 0.31f));
			Material material2 = Plain("Edda's gold trim", new Color(0.76f, 0.53f, 0.22f));
			Transform transform = new GameObject("Merchant sign").transform;
			transform.SetParent(prefab.transform, false);
			Block(transform, "Indigo stall sign", new Vector3(0f, 2.31f, -1.02f), new Vector3(1.32f, 0.21f, 0.055f), material);
			float[] array = new float[2] { -0.49f, 0.49f };
			foreach (float num in array)
			{
				Round(transform, "Gold stud", new Vector3(num, 2.31f, -1.06f), new Vector3(0.07f, 0.07f, 0.035f), material2);
			}
			Round(transform, "Merchant emblem", new Vector3(0f, 2.31f, -1.065f), new Vector3(0.17f, 0.17f, 0.035f), material2);
		}

		private static Material Wood(Material source, Color tint)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)source == (Object)null)
			{
				return Plain("Stall timber", tint);
			}
			Material val = new Material(source)
			{
				name = "Market stall timber"
			};
			if (val.HasProperty("_Color"))
			{
				val.color = tint;
			}
			return val;
		}

		private static Material Plain(string name, Color color)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			Material val = new Material(Shader.Find("Standard") ?? Shader.Find("Diffuse") ?? fallbackShader)
			{
				name = name,
				color = color
			};
			if (val.HasProperty("_Glossiness"))
			{
				val.SetFloat("_Glossiness", 0.04f);
			}
			return val;
		}

		private static Transform Shape(Transform parent, PrimitiveType shape, string name, Vector3 position, Vector3 scale, Material material)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: 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)
			GameObject obj = GameObject.CreatePrimitive(shape);
			((Object)obj).name = name;
			obj.layer = ((Component)parent).gameObject.layer;
			obj.transform.SetParent(parent, false);
			obj.transform.localPosition = position;
			obj.transform.localScale = scale;
			((Renderer)obj.GetComponent<MeshRenderer>()).sharedMaterial = material;
			Object.DestroyImmediate((Object)(object)obj.GetComponent<Collider>());
			return obj.transform;
		}

		private static void Block(Transform parent, string name, Vector3 position, Vector3 scale, Material material)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			Shape(parent, (PrimitiveType)3, name, position, scale, material);
		}

		private static void Round(Transform parent, string name, Vector3 position, Vector3 scale, Material material)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			Shape(parent, (PrimitiveType)0, name, position, scale, material);
		}

		private static void Beam(Transform parent, string name, Vector3 from, Vector3 to, float width, float depth, Material material)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: 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_0016: 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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			Shape(parent, (PrimitiveType)3, name, (from + to) * 0.5f, new Vector3(width, Vector3.Distance(from, to), depth), material).localRotation = Quaternion.FromToRotation(Vector3.up, to - from);
		}

		private static void Lash(Transform parent, float x, float z, float y, Material rope)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < 2; i++)
			{
				float num = y + (float)i * 0.045f;
				Block(parent, "Rope lashing", new Vector3(x, num, z - 0.11f), new Vector3(0.27f, 0.025f, 0.025f), rope);
				Block(parent, "Rope lashing", new Vector3(x, num, z + 0.11f), new Vector3(0.27f, 0.025f, 0.025f), rope);
				Block(parent, "Rope lashing", new Vector3(x - 0.11f, num, z), new Vector3(0.025f, 0.025f, 0.27f), rope);
				Block(parent, "Rope lashing", new Vector3(x + 0.11f, num, z), new Vector3(0.025f, 0.025f, 0.27f), rope);
			}
		}

		private static void Canopy(Transform parent, string name, float frontZ, float backZ, float frontY, float backY, Material material)
		{
			//IL_000a: 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)
			Beam(parent, name, new Vector3(0f, frontY, frontZ), new Vector3(0f, backY, backZ), 3.58f, 0.045f, material);
		}

		private static void Flap(Transform parent, float x, float width, Material material)
		{
			//IL_0019: 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_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			Block(parent, "Canvas valance", new Vector3(x + width * 0.5f, 2.27f, -0.99f), new Vector3(width - 0.018f, 0.24f, 0.04f), material);
			Round(parent, "Valance scallop", new Vector3(x + width * 0.5f, 2.15f, -0.99f), new Vector3(width - 0.018f, 0.1f, 0.04f), material);
		}

		private static void Crate(Transform parent, Vector3 at, float size, Material timber, Material dark)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			Block(parent, "Produce crate base", at, new Vector3(size, 0.05f, size * 0.7f), dark);
			float[] array = new float[2] { -1f, 1f };
			foreach (float num in array)
			{
				Block(parent, "Produce crate side", at + new Vector3(num * size * 0.47f, 0.09f, 0f), new Vector3(0.045f, 0.2f, size * 0.7f), timber);
			}
			array = new float[2] { -1f, 1f };
			foreach (float num2 in array)
			{
				Block(parent, "Produce crate slat", at + new Vector3(0f, 0.09f, num2 * size * 0.32f), new Vector3(size, 0.18f, 0.045f), timber);
			}
		}

		private static void Pot(Transform parent, Vector3 at, Material pottery)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: 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_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: 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_004e: 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)
			Shape(parent, (PrimitiveType)2, "Clay pot", at + Vector3.up * 0.11f, new Vector3(0.22f, 0.2f, 0.22f), pottery);
			Shape(parent, (PrimitiveType)2, "Clay pot rim", at + Vector3.up * 0.23f, new Vector3(0.16f, 0.035f, 0.16f), pottery);
		}
	}
	internal sealed class MarketStore
	{
		private const int Format = 1;

		private readonly string path;

		private readonly HashSet<Guid> completed = new HashSet<Guid>();

		internal readonly List<Listing> Listings = new List<Listing>();

		internal readonly List<Offer> Offers = new List<Offer>();

		internal readonly List<Claim> Claims = new List<Claim>();

		private MarketStore(string file)
		{
			path = file;
		}

		internal static MarketStore Open(long worldId)
		{
			string path = Path.Combine(Paths.ConfigPath, "ValheimMarket");
			Directory.CreateDirectory(path);
			MarketStore marketStore = new MarketStore(Path.Combine(path, worldId + ".market"));
			if (!File.Exists(marketStore.path))
			{
				return marketStore;
			}
			using FileStream input = File.OpenRead(marketStore.path);
			using BinaryReader binaryReader = new BinaryReader(input);
			if (binaryReader.ReadInt32() != 1)
			{
				throw new InvalidDataException("Unsupported market save format");
			}
			marketStore.Listings.AddRange(MarketCodec.ReadList(binaryReader, Listing.Read));
			marketStore.Offers.AddRange(MarketCodec.ReadList(binaryReader, Offer.Read));
			marketStore.Claims.AddRange(MarketCodec.ReadList(binaryReader, Claim.Read));
			int num = binaryReader.ReadInt32();
			if (num < 0 || num > 1000000)
			{
				throw new InvalidDataException("Invalid transaction journal");
			}
			for (int i = 0; i < num; i++)
			{
				marketStore.completed.Add(MarketCodec.ReadGuid(binaryReader));
			}
			return marketStore;
		}

		private void Save()
		{
			string sourceFileName = path + ".tmp";
			using (FileStream fileStream = new FileStream(sourceFileName, FileMode.Create, FileAccess.Write, FileShare.None))
			{
				using BinaryWriter binaryWriter = new BinaryWriter(fileStream);
				binaryWriter.Write(1);
				MarketCodec.WriteList(binaryWriter, Listings, delegate(BinaryWriter x, Listing y)
				{
					y.Write(x);
				});
				MarketCodec.WriteList(binaryWriter, Offers, delegate(BinaryWriter x, Offer y)
				{
					y.Write(x);
				});
				MarketCodec.WriteList(binaryWriter, Claims, delegate(BinaryWriter x, Claim y)
				{
					y.Write(x);
				});
				binaryWriter.Write(completed.Count);
				foreach (Guid item in completed)
				{
					MarketCodec.WriteGuid(binaryWriter, item);
				}
				binaryWriter.Flush();
				fileStream.Flush(flushToDisk: true);
			}
			if (File.Exists(path))
			{
				try
				{
					File.Replace(sourceFileName, path, path + ".bak");
					return;
				}
				catch (PlatformNotSupportedException)
				{
					File.Copy(path, path + ".bak", overwrite: true);
					File.Delete(path);
					File.Move(sourceFileName, path);
					return;
				}
			}
			File.Move(sourceFileName, path);
		}

		private string Commit(Guid requestId, string result)
		{
			completed.Add(requestId);
			Listings.RemoveAll((Listing x) => !x.Active);
			Offers.RemoveAll((Offer x) => !x.Active);
			Save();
			return result;
		}

		internal bool WasCompleted(Guid requestId)
		{
			return completed.Contains(requestId);
		}

		internal Listing FindListing(Guid id)
		{
			return Listings.FirstOrDefault((Listing x) => x.Id == id && x.Active);
		}

		internal Offer FindOffer(Guid id)
		{
			return Offers.FirstOrDefault((Offer x) => x.Id == id && x.Active);
		}

		internal Claim FindClaim(Guid id)
		{
			return Claims.FirstOrDefault((Claim x) => x.Id == id && !x.Collected);
		}

		internal string CreateListing(Guid requestId, long seller, string sellerName, ItemBlob item, int price)
		{
			if (WasCompleted(requestId))
			{
				return "Already posted";
			}
			item.Validate();
			if (price < 1 || price > 1000000)
			{
				throw new InvalidDataException("Gold price must be 1–1,000,000");
			}
			if (Listings.Count((Listing x) => x.Active && x.Seller == seller) >= 20)
			{
				throw new InvalidDataException("Shop is full (20 listings)");
			}
			Listings.Add(new Listing
			{
				Id = Guid.NewGuid(),
				Seller = seller,
				SellerName = sellerName,
				Item = item,
				GoldPrice = price
			});
			return Commit(requestId, "Listing posted");
		}

		internal string MakeOffer(Guid requestId, Guid listingId, long buyer, string buyerName, ItemBlob item)
		{
			if (WasCompleted(requestId))
			{
				return "Offer already posted";
			}
			Listing listing = FindListing(listingId) ?? throw new InvalidDataException("Listing is unavailable");
			if (listing.Seller == buyer)
			{
				throw new InvalidDataException("You cannot offer on your own listing");
			}
			item.Validate();
			if (Offers.Count((Offer x) => x.Active && x.ListingId == listingId) >= 10)
			{
				throw new InvalidDataException("This listing has too many offers");
			}
			Offers.Add(new Offer
			{
				Id = Guid.NewGuid(),
				ListingId = listingId,
				Seller = listing.Seller,
				Buyer = buyer,
				BuyerName = buyerName,
				Item = item
			});
			return Commit(requestId, "Offer sent");
		}

		internal string Buy(Guid requestId, Guid listingId, long buyer, int paidCoins)
		{
			if (WasCompleted(requestId))
			{
				return "Purchase already completed";
			}
			Listing listing = FindListing(listingId) ?? throw new InvalidDataException("Listing is unavailable");
			if (listing.Seller == buyer)
			{
				throw new InvalidDataException("You cannot buy your own listing");
			}
			if (paidCoins != listing.GoldPrice)
			{
				throw new InvalidDataException("Price changed");
			}
			listing.Active = false;
			AddClaim(listing.Seller, buyer, listing.Item);
			AddCoins(listing.Seller, listing.Seller, paidCoins);
			ReturnOtherOffers(listing.Id, Guid.Empty);
			return Commit(requestId, "Purchased. Delivering your item to inventory");
		}

		internal string SellToNpc(Guid requestId, long shopOwner, long seller, ItemBlob item)
		{
			if (WasCompleted(requestId))
			{
				return "Already sold";
			}
			int amount = NpcMerchant.Quote(item, shopOwner);
			AddCoins(shopOwner, seller, amount);
			return Commit(requestId, "Sold to Edda. Delivering coins to your inventory");
		}

		internal string BuyFromNpc(Guid requestId, long buyer, Listing stock, int paidCoins)
		{
			if (WasCompleted(requestId))
			{
				return "Purchase already completed";
			}
			if (stock == null || !NpcMerchant.IsOwner(stock.Seller) || paidCoins != stock.GoldPrice)
			{
				throw new InvalidDataException("Edda's stock or price changed");
			}
			stock.Item.Validate();
			AddClaim(stock.Seller, buyer, stock.Item);
			return Commit(requestId, "Purchased. Delivering goods to your inventory");
		}

		internal string AcceptOffer(Guid requestId, Guid offerId, long seller)
		{
			if (WasCompleted(requestId))
			{
				return "Offer already accepted";
			}
			Offer offer = FindOffer(offerId) ?? throw new InvalidDataException("Offer is unavailable");
			Listing listing = FindListing(offer.ListingId) ?? throw new InvalidDataException("Listing is unavailable");
			if (listing.Seller != seller)
			{
				throw new InvalidDataException("This is not your listing");
			}
			offer.Active = false;
			listing.Active = false;
			AddClaim(seller, offer.Buyer, listing.Item);
			AddClaim(seller, seller, offer.Item);
			ReturnOtherOffers(listing.Id, offer.Id);
			return Commit(requestId, "Offer accepted. Goods are ready to collect");
		}

		internal string RejectOffer(Guid requestId, Guid offerId, long seller)
		{
			if (WasCompleted(requestId))
			{
				return "Offer already rejected";
			}
			Offer offer = FindOffer(offerId) ?? throw new InvalidDataException("Offer is unavailable");
			if (offer.Seller != seller)
			{
				throw new InvalidDataException("This is not your offer to reject");
			}
			offer.Active = false;
			AddClaim(seller, offer.Buyer, offer.Item);
			return Commit(requestId, "Offer rejected");
		}

		internal string CancelListing(Guid requestId, Guid listingId, long seller)
		{
			if (WasCompleted(requestId))
			{
				return "Listing already canceled";
			}
			Listing listing = FindListing(listingId) ?? throw new InvalidDataException("Listing is unavailable");
			if (listing.Seller != seller)
			{
				throw new InvalidDataException("This is not your listing");
			}
			listing.Active = false;
			AddClaim(seller, seller, listing.Item);
			ReturnOtherOffers(listingId, Guid.Empty);
			return Commit(requestId, "Listing canceled. Collect your item");
		}

		internal string CancelOffer(Guid requestId, Guid offerId, long buyer)
		{
			if (WasCompleted(requestId))
			{
				return "Offer already canceled";
			}
			Offer offer = FindOffer(offerId) ?? throw new InvalidDataException("Offer is unavailable");
			if (offer.Buyer != buyer)
			{
				throw new InvalidDataException("This is not your offer");
			}
			offer.Active = false;
			AddClaim(offer.Seller, buyer, offer.Item);
			return Commit(requestId, "Offer canceled. Collect your item");
		}

		internal void MarkCollected(Guid claimId, long recipient)
		{
			Claim claim = FindClaim(claimId);
			if (claim != null && claim.Recipient == recipient)
			{
				claim.Collected = true;
				Claims.RemoveAll((Claim x) => x.Collected);
				Save();
			}
		}

		private void ReturnOtherOffers(Guid listingId, Guid except)
		{
			foreach (Offer item in Offers.Where((Offer x) => x.Active && x.ListingId == listingId && x.Id != except))
			{
				item.Active = false;
				AddClaim(item.Seller, item.Buyer, item.Item);
			}
		}

		private void AddClaim(long shopOwner, long recipient, ItemBlob item)
		{
			Claims.Add(new Claim
			{
				Id = Guid.NewGuid(),
				ShopOwner = shopOwner,
				Recipient = recipient,
				Item = item
			});
		}

		private void AddCoins(long shopOwner, long recipient, int amount)
		{
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Coins");
			if ((Object)(object)itemPrefab == (Object)null)
			{
				throw new InvalidDataException("Coin item is unavailable");
			}
			int maxStackSize = itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_maxStackSize;
			ItemData val = itemPrefab.GetComponent<ItemDrop>().m_itemData.Clone();
			val.m_dropPrefab = itemPrefab;
			while (amount > 0)
			{
				int num = Math.Min(amount, maxStackSize);
				ItemBlob itemBlob = ItemBlob.FromItem(val, 1);
				itemBlob.Stack = num;
				AddClaim(shopOwner, recipient, itemBlob);
				amount -= num;
			}
		}
	}
	internal sealed class MarketUI
	{
		private enum Page
		{
			Browse,
			Merchant,
			MyShop,
			Offers,
			Collect
		}

		private static readonly Color Wood = new Color(0.4f, 0.29f, 0.2f, 0.98f);

		private static readonly Color Dark = new Color(0.1f, 0.11f, 0.12f, 0.96f);

		private static readonly Color Line = new Color(0.54f, 0.36f, 0.17f, 1f);

		private static readonly Color Gold = new Color(0.94f, 0.72f, 0.39f, 1f);

		private static readonly Color Cream = new Color(0.94f, 0.88f, 0.76f, 1f);

		private Canvas canvas;

		private GameObject root;

		private GameObject ownedEventSystem;

		private RectTransform panel;

		private RectTransform body;

		private readonly Dictionary<Page, Button> tabs = new Dictionary<Page, Button>();

		private TextMeshProUGUI status;

		private TMP_FontAsset font;

		private TMP_FontAsset titleFont;

		private Sprite woodSprite;

		private Sprite insetSprite;

		private Sprite boxSprite;

		private Sprite buttonSprite;

		private Sprite buttonHoverSprite;

		private Sprite buttonPressedSprite;

		private Sprite tabSprite;

		private Sprite tabHoverSprite;

		private Sprite tabSelectedSprite;

		private Sprite fieldSprite;

		private Sprite separatorSprite;

		private Page page;

		private MarketStall stall;

		private MarketSnapshot snapshot;

		private string loadError;

		private long selectedOwner;

		private string search = "";

		private ItemData selectedItem;

		private TMP_InputField priceField;

		private TMP_InputField quantityField;

		private CursorLockMode oldLock;

		private bool oldCursor;

		private bool opened;

		private bool waitForMouseRelease;

		private int closedFrame = -1;

		private bool rebuildNextFrame;

		private Guid selectedOfferListing;

		internal bool IsOpen
		{
			get
			{
				if (opened)
				{
					return (Object)(object)root != (Object)null;
				}
				return false;
			}
		}

		internal bool BlocksGameInput
		{
			get
			{
				if (!IsOpen && !waitForMouseRelease)
				{
					return Time.frameCount == closedFrame;
				}
				return true;
			}
		}

		internal void Open(MarketStall marketStall)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Expected O, but got Unknown
			//IL_0267: 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_02ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0304: Unknown result type (might be due to invalid IL or missing references)
			//IL_032c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0383: Unknown result type (might be due to invalid IL or missing references)
			//IL_0389: Unknown result type (might be due to invalid IL or missing references)
			//IL_039f: Unknown result type (might be due to invalid IL or missing references)
			//IL_03af: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0404: Unknown result type (might be due to invalid IL or missing references)
			//IL_0430: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b8: Expected O, but got Unknown
			//IL_0477: Unknown result type (might be due to invalid IL or missing references)
			//IL_0635: Unknown result type (might be due to invalid IL or missing references)
			//IL_0655: Unknown result type (might be due to invalid IL or missing references)
			//IL_058c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0585: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)marketStall == (Object)null)
			{
				return;
			}
			Close();
			closedFrame = -1;
			stall = marketStall;
			loadError = null;
			selectedOwner = stall.ShopId;
			this.page = (stall.IsNpc ? Page.Merchant : Page.Browse);
			oldLock = Cursor.lockState;
			oldCursor = Cursor.visible;
			font = ((IEnumerable<TMP_FontAsset>)Resources.FindObjectsOfTypeAll<TMP_FontAsset>()).FirstOrDefault((Func<TMP_FontAsset, bool>)((TMP_FontAsset x) => ((Object)x).name.IndexOf("Averia", StringComparison.OrdinalIgnoreCase) >= 0)) ?? TMP_Settings.defaultFontAsset;
			titleFont = ((IEnumerable<TMP_FontAsset>)Resources.FindObjectsOfTypeAll<TMP_FontAsset>()).FirstOrDefault((Func<TMP_FontAsset, bool>)((TMP_FontAsset x) => ((Object)x).name.IndexOf("Norse", StringComparison.OrdinalIgnoreCase) >= 0)) ?? font;
			Sprite[] sprites = Resources.FindObjectsOfTypeAll<Sprite>();
			woodSprite = FindSprite(sprites, "woodpanel_large") ?? FindSprite(sprites, "woodpanel_512x512") ?? FindSprite(sprites, "woodpanel_playerinventory");
			insetSprite = FindSprite(sprites, "panel_bkg_128") ?? FindSprite(sprites, "panel_bkg");
			boxSprite = FindSprite(sprites, "panel_bkg") ?? insetSprite;
			buttonSprite = FindSprite(sprites, "button");
			buttonHoverSprite = FindSprite(sprites, "button_highlight");
			buttonPressedSprite = FindSprite(sprites, "button_pressed");
			tabSprite = FindSprite(sprites, "button_tab");
			tabHoverSprite = FindSprite(sprites, "button_tab_hover");
			tabSelectedSprite = FindSprite(sprites, "button_tab_selected");
			fieldSprite = FindSprite(sprites, "text_field");
			separatorSprite = FindSprite(sprites, "panel_separator");
			root = new GameObject("ValheimMarketUI", new Type[4]
			{
				typeof(RectTransform),
				typeof(Canvas),
				typeof(CanvasScaler),
				typeof(GraphicRaycaster)
			});
			opened = true;
			canvas = root.GetComponent<Canvas>();
			canvas.renderMode = (RenderMode)0;
			canvas.sortingOrder = 250;
			CanvasScaler component = root.GetComponent<CanvasScaler>();
			component.uiScaleMode = (ScaleMode)1;
			component.referenceResolution = new Vector2(1920f, 1080f);
			component.matchWidthOrHeight = 0.5f;
			if ((Object)(object)EventSystem.current == (Object)null)
			{
				ownedEventSystem = new GameObject("ValheimMarketEventSystem", new Type[2]
				{
					typeof(EventSystem),
					typeof(StandaloneInputModule)
				});
			}
			RectTransform obj = Rect(root.transform, "Backdrop", 0f, 0f, 0f, 0f, stretch: true);
			obj.anchorMin = Vector2.zero;
			obj.anchorMax = Vector2.one;
			obj.offsetMin = Vector2.zero;
			obj.offsetMax = Vector2.zero;
			((Graphic)((Component)obj).gameObject.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0.62f);
			panel = Rect(root.transform, "Market Panel", 0f, 0f, 1250f, 780f);
			RectTransform obj2 = panel;
			RectTransform obj3 = panel;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0.5f, 0.5f);
			obj3.anchorMax = val;
			obj2.anchorMin = val;
			panel.pivot = new Vector2(0.5f, 0.5f);
			panel.anchoredPosi