Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Marketplace v1.5.0
Marketplace.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Managers; using Jotunn.Utils; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace Marketplace; public class ItemRecord { private const int Version = 1; public string Prefab = ""; public int Stack = 1; public int Quality = 1; public int Variant; public float Durability; public long CrafterID; public string CrafterName = ""; public int WorldLevel; public Dictionary<string, string> CustomData = new Dictionary<string, string>(); public void Write(ZPackage pkg) { pkg.Write(1); pkg.Write(Prefab); pkg.Write(Stack); pkg.Write(Quality); pkg.Write(Variant); pkg.Write(Durability); pkg.Write(CrafterID); pkg.Write(CrafterName ?? ""); pkg.Write(WorldLevel); pkg.Write(CustomData.Count); foreach (KeyValuePair<string, string> customDatum in CustomData) { pkg.Write(customDatum.Key); pkg.Write(customDatum.Value ?? ""); } } public static ItemRecord Read(ZPackage pkg) { ItemRecord itemRecord = new ItemRecord(); pkg.ReadInt(); itemRecord.Prefab = pkg.ReadString(); itemRecord.Stack = pkg.ReadInt(); itemRecord.Quality = pkg.ReadInt(); itemRecord.Variant = pkg.ReadInt(); itemRecord.Durability = pkg.ReadSingle(); itemRecord.CrafterID = pkg.ReadLong(); itemRecord.CrafterName = pkg.ReadString(); itemRecord.WorldLevel = pkg.ReadInt(); int num = pkg.ReadInt(); for (int i = 0; i < num; i++) { string key = pkg.ReadString(); itemRecord.CustomData[key] = pkg.ReadString(); } return itemRecord; } public ItemRecord Clone(int stack) { ItemRecord itemRecord = (ItemRecord)MemberwiseClone(); itemRecord.CustomData = new Dictionary<string, string>(CustomData); itemRecord.Stack = stack; return itemRecord; } public static ItemRecord FromItem(ItemData item, int amount) { ItemRecord itemRecord = new ItemRecord(); itemRecord.Prefab = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : ""); itemRecord.Stack = amount; itemRecord.Quality = item.m_quality; itemRecord.Variant = item.m_variant; itemRecord.Durability = item.m_durability; itemRecord.CrafterID = item.m_crafterID; itemRecord.CrafterName = item.m_crafterName ?? ""; itemRecord.WorldLevel = item.m_worldLevel; if (item.m_customData != null) { itemRecord.CustomData = new Dictionary<string, string>(item.m_customData); } return itemRecord; } public static bool PrefabExists(string prefab) { if ((Object)(object)ObjectDB.instance == (Object)null || string.IsNullOrEmpty(prefab)) { return false; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefab); if ((Object)(object)itemPrefab != (Object)null) { return (Object)(object)itemPrefab.GetComponent<ItemDrop>() != (Object)null; } return false; } public ItemData ToItemData() { if ((Object)(object)ObjectDB.instance == (Object)null) { return null; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(Prefab); if ((Object)(object)itemPrefab == (Object)null) { return null; } ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null) { return null; } ItemData val = component.m_itemData.Clone(); val.m_dropPrefab = itemPrefab; val.m_stack = Stack; val.m_quality = Quality; val.m_variant = Variant; val.m_durability = Durability; val.m_crafterID = CrafterID; val.m_crafterName = CrafterName; val.m_worldLevel = WorldLevel; val.m_customData = new Dictionary<string, string>(CustomData); val.m_equipped = false; return val; } } public class Listing { public long Id; public long SellerId; public string SellerName = ""; public int Price; public long CreatedTicks; public ItemRecord Item; public void Write(ZPackage pkg) { pkg.Write(Id); pkg.Write(SellerId); pkg.Write(SellerName ?? ""); pkg.Write(Price); pkg.Write(CreatedTicks); Item.Write(pkg); } public static Listing Read(ZPackage pkg) { Listing listing = new Listing(); listing.Id = pkg.ReadLong(); listing.SellerId = pkg.ReadLong(); listing.SellerName = pkg.ReadString(); listing.Price = pkg.ReadInt(); listing.CreatedTicks = pkg.ReadLong(); listing.Item = ItemRecord.Read(pkg); return listing; } } public enum HistoryKind { Deposit, Withdraw, Sale, Purchase, OrderEscrow, OrderRefund, OrderSale, OrderFilled, BountyEscrow, BountyRefund, BountyReward, BountyPaid, GuildPayment, GuildPayout } public class HistoryEntry { public HistoryKind Kind; public long Ticks; public long Amount; public long Fee; public string Prefab = ""; public int Stack; public string Other = ""; public string Text = ""; public void Write(ZPackage pkg) { pkg.Write((int)Kind); pkg.Write(Ticks); pkg.Write(Amount); pkg.Write(Fee); pkg.Write(Prefab ?? ""); pkg.Write(Stack); pkg.Write(Other ?? ""); pkg.Write(Text ?? ""); } public static HistoryEntry Read(ZPackage pkg, bool hasText) { HistoryEntry historyEntry = ReadCore(pkg); if (hasText) { historyEntry.Text = pkg.ReadString(); } return historyEntry; } private static HistoryEntry ReadCore(ZPackage pkg) { HistoryEntry historyEntry = new HistoryEntry(); historyEntry.Kind = (HistoryKind)pkg.ReadInt(); historyEntry.Ticks = pkg.ReadLong(); historyEntry.Amount = pkg.ReadLong(); historyEntry.Fee = pkg.ReadLong(); historyEntry.Prefab = pkg.ReadString(); historyEntry.Stack = pkg.ReadInt(); historyEntry.Other = pkg.ReadString(); return historyEntry; } } public class Account { public const int MaxHistory = 50; public string Name = ""; public long Balance; public List<ItemRecord> Items = new List<ItemRecord>(); public List<HistoryEntry> History = new List<HistoryEntry>(); public bool IsEmpty { get { if (Balance <= 0 && Items.Count == 0) { return History.Count == 0; } return false; } } public void AddHistory(HistoryEntry h) { h.Ticks = DateTime.UtcNow.Ticks; History.Insert(0, h); if (History.Count > 50) { History.RemoveRange(50, History.Count - 50); } } } public class BuyOrder { public long Id; public long BuyerId; public string BuyerName = ""; public string Prefab = ""; public int Remaining; public int Delivered; public int Price; public long CreatedTicks; public long Escrow => (long)Remaining * (long)Price; public void Write(ZPackage pkg) { pkg.Write(Id); pkg.Write(BuyerId); pkg.Write(BuyerName ?? ""); pkg.Write(Prefab ?? ""); pkg.Write(Remaining); pkg.Write(Delivered); pkg.Write(Price); pkg.Write(CreatedTicks); } public static BuyOrder Read(ZPackage pkg) { BuyOrder buyOrder = new BuyOrder(); buyOrder.Id = pkg.ReadLong(); buyOrder.BuyerId = pkg.ReadLong(); buyOrder.BuyerName = pkg.ReadString(); buyOrder.Prefab = pkg.ReadString(); buyOrder.Remaining = pkg.ReadInt(); buyOrder.Delivered = pkg.ReadInt(); buyOrder.Price = pkg.ReadInt(); buyOrder.CreatedTicks = pkg.ReadLong(); return buyOrder; } } public enum BountyState { Open, Claimed, Submitted } public enum BountyAction { Claim, Abandon, Submit, Confirm, Reject, Cancel } public class Bounty { public const int MaxTitle = 60; public const int MaxDescription = 400; public long Id; public long OwnerId; public string OwnerName = ""; public string Title = ""; public string Description = ""; public int Reward; public long CreatedTicks; public BountyState State; public long ContractorId; public string ContractorName = ""; public long StateTicks; public void Write(ZPackage pkg) { pkg.Write(Id); pkg.Write(OwnerId); pkg.Write(OwnerName ?? ""); pkg.Write(Title ?? ""); pkg.Write(Description ?? ""); pkg.Write(Reward); pkg.Write(CreatedTicks); pkg.Write((int)State); pkg.Write(ContractorId); pkg.Write(ContractorName ?? ""); pkg.Write(StateTicks); } public static Bounty Read(ZPackage pkg) { Bounty bounty = new Bounty(); bounty.Id = pkg.ReadLong(); bounty.OwnerId = pkg.ReadLong(); bounty.OwnerName = pkg.ReadString(); bounty.Title = pkg.ReadString(); bounty.Description = pkg.ReadString(); bounty.Reward = pkg.ReadInt(); bounty.CreatedTicks = pkg.ReadLong(); bounty.State = (BountyState)pkg.ReadInt(); bounty.ContractorId = pkg.ReadLong(); bounty.ContractorName = pkg.ReadString(); bounty.StateTicks = pkg.ReadLong(); return bounty; } } public class PriceStat { public string Prefab = ""; public int Avg; public int Min; public int Max; public int Trades; public int Volume; public long LastTicks; public void Write(ZPackage pkg) { pkg.Write(Prefab ?? ""); pkg.Write(Avg); pkg.Write(Min); pkg.Write(Max); pkg.Write(Trades); pkg.Write(Volume); pkg.Write(LastTicks); } public static PriceStat Read(ZPackage pkg) { PriceStat priceStat = new PriceStat(); priceStat.Prefab = pkg.ReadString(); priceStat.Avg = pkg.ReadInt(); priceStat.Min = pkg.ReadInt(); priceStat.Max = pkg.ReadInt(); priceStat.Trades = pkg.ReadInt(); priceStat.Volume = pkg.ReadInt(); priceStat.LastTicks = pkg.ReadLong(); return priceStat; } } public enum ItemCategory { All, Weapons, Armor, Tools, Materials, Food, Potions, Other } internal static class Categories { public const int Count = 8; public static readonly string[] Keys = new string[8] { "cat_all", "cat_weapons", "cat_armor", "cat_tools", "cat_materials", "cat_food", "cat_potions", "cat_other" }; public static ItemCategory Of(ItemData item) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected I4, but got Unknown if (item == null || item.m_shared == null) { return ItemCategory.Other; } SharedData shared = item.m_shared; ItemType itemType = shared.m_itemType; switch (itemType - 1) { case 2: case 3: case 8: case 13: case 14: case 19: case 21: case 22: return ItemCategory.Weapons; case 4: case 5: case 6: case 10: case 11: case 16: case 17: case 23: return ItemCategory.Armor; case 18: return ItemCategory.Tools; case 0: case 20: return ItemCategory.Materials; case 1: if (!(shared.m_food > 0f) && !(shared.m_foodStamina > 0f) && !(shared.m_foodEitr > 0f)) { return ItemCategory.Potions; } return ItemCategory.Food; default: return ItemCategory.Other; } } public static bool Matches(ItemData item, ItemCategory filter) { if (filter != ItemCategory.All) { return Of(item) == filter; } return true; } } internal static class Rpc { public const string RequestData = "Marketplace_RequestData"; public const string Create = "Marketplace_Create"; public const string Buy = "Marketplace_Buy"; public const string Cancel = "Marketplace_Cancel"; public const string Claim = "Marketplace_Claim"; public const string Deposit = "Marketplace_Deposit"; public const string Withdraw = "Marketplace_Withdraw"; public const string OrderCreate = "Marketplace_OrderCreate"; public const string OrderFill = "Marketplace_OrderFill"; public const string OrderCancel = "Marketplace_OrderCancel"; public const string BountyCreate = "Marketplace_BountyCreate"; public const string BountyAct = "Marketplace_BountyAction"; public const string Data = "Marketplace_Data"; public const string Result = "Marketplace_Result"; public const string Deliver = "Marketplace_Deliver"; public const string Changed = "Marketplace_Changed"; public const string CoinsPrefab = "Coins"; public const string CoinsName = "$item_coins"; public static void Register() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.Register("Marketplace_RequestData", (Action<long>)MarketServer.RPC_RequestData); instance.Register<ZPackage>("Marketplace_Create", (Action<long, ZPackage>)MarketServer.RPC_Create); instance.Register<ZPackage>("Marketplace_Buy", (Action<long, ZPackage>)MarketServer.RPC_Buy); instance.Register<long>("Marketplace_Cancel", (Action<long, long>)MarketServer.RPC_Cancel); instance.Register("Marketplace_Claim", (Action<long>)MarketServer.RPC_Claim); instance.Register<int>("Marketplace_Deposit", (Action<long, int>)MarketServer.RPC_Deposit); instance.Register<int>("Marketplace_Withdraw", (Action<long, int>)MarketServer.RPC_Withdraw); instance.Register<ZPackage>("Marketplace_OrderCreate", (Action<long, ZPackage>)MarketServer.RPC_OrderCreate); instance.Register<ZPackage>("Marketplace_OrderFill", (Action<long, ZPackage>)MarketServer.RPC_OrderFill); instance.Register<long>("Marketplace_OrderCancel", (Action<long, long>)MarketServer.RPC_OrderCancel); instance.Register<ZPackage>("Marketplace_BountyCreate", (Action<long, ZPackage>)MarketServer.RPC_BountyCreate); instance.Register<ZPackage>("Marketplace_BountyAction", (Action<long, ZPackage>)MarketServer.RPC_BountyAction); instance.Register<ZPackage>("Marketplace_Data", (Action<long, ZPackage>)MarketClient.RPC_Data); instance.Register<ZPackage>("Marketplace_Result", (Action<long, ZPackage>)MarketClient.RPC_Result); instance.Register<ZPackage>("Marketplace_Deliver", (Action<long, ZPackage>)MarketClient.RPC_Deliver); instance.Register("Marketplace_Changed", (Action<long>)MarketClient.RPC_Changed); } } public static long ServerId() { if ((Object)(object)ZNet.instance == (Object)null) { return 0L; } if (ZNet.instance.IsServer()) { return ZDOMan.GetSessionID(); } return ZNet.instance.GetServerPeer()?.m_uid ?? 0; } } public enum MarketLanguage { English, Polish } internal static class Loc { private static readonly Dictionary<string, string> En = new Dictionary<string, string> { { "title", "MARKETPLACE" }, { "tab_buy", "MARKETPLACE" }, { "tab_sell", "SELL" }, { "tab_mine", "MY LISTINGS" }, { "tab_mail", "MAILBOX" }, { "tab_bank", "BANK" }, { "hdr_inventory", "Inventory" }, { "hdr_balance", "Balance" }, { "connecting", "Connecting to the marketplace…" }, { "search", "Search item or seller…" }, { "sort_new", "Sort: newest" }, { "sort_cheap", "Sort: cheapest" }, { "sort_expensive", "Sort: most expensive" }, { "sort_name", "Sort: name" }, { "pick", "Select an entry from the list on the left." }, { "loading", "Loading listings…" }, { "no_match", "Nothing matches your search." }, { "no_listings", "There are no listings yet.\nList something in the SELL tab!" }, { "nothing_to_sell", "You have nothing that can be listed.\nEquipped items and coins are skipped." }, { "no_own", "You have no active listings." }, { "mail_empty_list", "Your mailbox is empty.\nItems from cancelled and expired listings end up here." }, { "no_history", "No transactions yet." }, { "your_listing", "Your listing" }, { "expires_in", "expires in {0}" }, { "trader_value", "Trader value: {0} / pc" }, { "seller", "Seller: {0}" }, { "price_each", "Price per piece: <color=#FFD27A>{0}</color> coins" }, { "available", "Available: {0} pcs" }, { "remaining", "Remaining: {0} pcs" }, { "listed_ago", "Listed {0} ago" }, { "expires_in_cap", "Expires in {0}" }, { "no_expiry", "Does not expire" }, { "in_inventory", "In inventory: {0} pcs" }, { "my_listings_count", "Your listings: {0} / {1}" }, { "expires_after", "Listing expires after {0} h" }, { "never_expires", "Listings never expire" }, { "qty", "Amount:" }, { "price", "Price / pc:" }, { "max", "Max" }, { "missing_mod", "<color=#FF7766>This item comes from a mod you do not have installed.</color>" }, { "quality", "lvl {0}" }, { "mail_title", "Your mailbox" }, { "mail_desc", "Items from cancelled and expired listings, and purchases that could not be delivered, wait here.\n\n\"Collect all\" moves them to your inventory. Whatever does not fit is dropped next to you." }, { "mail_items", "Items: {0}" }, { "total_buy", "Total: {0} coins" }, { "pay_split", "From balance {0}, from inventory {1}" }, { "total_sell", "You will receive: {0} coins" }, { "total_sell_fee", "Total {0}, fee {1}% – you receive {2}" }, { "btn_own", "This is your listing" }, { "btn_poor", "Not enough coins" }, { "btn_buy", "Buy for {0}" }, { "btn_need_price", "Enter a price" }, { "btn_max_price", "Max {0} / pc" }, { "btn_sell", "List for sale" }, { "btn_cancel", "Cancel listing" }, { "btn_collect", "Collect all" }, { "btn_wait", "Waiting for server…" }, { "bank_title", "Marketplace bank" }, { "bank_desc", "Coins in your balance are stored on the server, not in your inventory — you cannot lose them on death. Sale income lands here and purchases are paid from here first." }, { "bank_balance", "Balance: <color=#FFD27A>{0}</color> coins" }, { "bank_inventory", "In inventory: {0} coins" }, { "bank_amount", "Coins:" }, { "btn_deposit", "Deposit" }, { "btn_withdraw", "Withdraw" }, { "btn_deposit_all", "Deposit all" }, { "btn_withdraw_all", "Withdraw all" }, { "h_deposit", "Deposit" }, { "h_withdraw", "Withdrawal" }, { "h_sale", "Sale: {0}" }, { "h_purchase", "Purchase: {0}" }, { "h_to", "to {0}" }, { "h_from", "from {0}" }, { "h_fee", "fee {0}" }, { "ago", "{0} ago" }, { "span_d", "{0} d {1} h" }, { "span_h", "{0} h {1} min" }, { "span_m", "{0} min" }, { "inv_full_drop", "Inventory full — some items were dropped next to you" }, { "unknown_prefab", "Unknown item {0} — missing mod?" }, { "hud_prefix", "Marketplace: {0}" }, { "c_no_player", "No character." }, { "c_busy", "Wait for the server to respond." }, { "c_item_gone", "This item is no longer in your inventory." }, { "c_unequip", "Unequip this item first." }, { "c_cannot_list", "This item cannot be listed." }, { "c_bad_amount", "Invalid amount." }, { "c_need_price", "Enter a price (at least 1 coin)." }, { "c_max_price", "The maximum price is {0} coins per piece." }, { "c_total_high", "The total price is too high." }, { "c_remove_failed", "Could not take the item from your inventory." }, { "c_own", "This is your listing." }, { "c_poor", "Not enough coins ({0} needed)." }, { "c_no_coins_inv", "You do not have that many coins in your inventory." }, { "c_no_balance", "You do not have that many coins in your balance." }, { "c_no_space", "Not enough inventory space for {0} coins (room for {1})." }, { "s_raw", "{0}" }, { "s_unresolved_item", "The server could not identify your character — the item was returned." }, { "s_unresolved_coins", "The server could not identify your character — your coins were returned." }, { "s_bad_amount", "Invalid amount." }, { "s_unknown_item", "The server does not know this item (mod missing on the server?)." }, { "s_blocked", "This item cannot be listed on the marketplace." }, { "s_bad_price", "The price must be between 1 and {0} coins." }, { "s_total_high", "The total price is too high." }, { "s_limit", "Listing limit reached ({0})." }, { "s_create_failed", "{0} The item was returned to you." }, { "s_listed", "Listed: {0} for {1} coins each." }, { "s_gone", "This listing is no longer available." }, { "s_own", "You cannot buy your own listing." }, { "s_amount_changed", "The available amount has changed." }, { "s_price_changed", "The listing price has changed." }, { "s_no_funds", "Not enough coins: {0} needed, {1} in your balance." }, { "s_buy_failed_deposit", "{0} The coins you paid stay in your marketplace balance." }, { "s_bought", "Bought: {0} for {1} coins." }, { "s_sold", "{0} bought {1} from you. +{2} coins added to your marketplace balance." }, { "s_not_found", "Listing not found." }, { "s_cancelled", "Listing cancelled: {0}." }, { "s_mail_empty", "Your mailbox is empty." }, { "s_claimed", "Mailbox contents collected." }, { "s_expired", "Your listing expired: {0}. The item is waiting in your marketplace mailbox." }, { "s_deposited", "Deposited {0} coins. Balance: {1}." }, { "s_withdrawn", "Withdrew {0} coins. Balance: {1}." }, { "s_withdraw_too_much", "You only have {0} coins in your balance." }, { "tab_orders", "BUY ORDERS" }, { "tab_jobs", "JOBS" }, { "mode_orders", "All buy orders" }, { "mode_bounties", "All jobs" }, { "mode_new_order", "+ Post a buy order" }, { "mode_new_bounty", "+ Post a job" }, { "pick_search", "Search for the item you want to buy…" }, { "pick_hint", "Type at least 2 letters of the item name." }, { "pick_none", "No items match." }, { "pick_order_item", "Pick the item you want to buy from the list on the left." }, { "no_orders", "There are no buy orders yet.\nPost one with \"+ Post a buy order\"!" }, { "no_bounties", "No jobs posted yet.\nPost one with \"+ Post a job\"!" }, { "order_row", "Wanted: {0}" }, { "order_title", "Wanted: {0}" }, { "order_by", "Buyer: {0}" }, { "your_order", "Your buy order" }, { "order_remaining", "Still wanted: {0} pcs (delivered {1})" }, { "order_escrow", "Locked in the order: {0} coins" }, { "you_have", "You have: {0}" }, { "new_order_title", "Buy: {0}" }, { "my_orders_count", "Your buy orders: {0} / {1}" }, { "my_bounties_count", "Your jobs: {0} / {1}" }, { "order_lock", "Locks {0} coins until delivered" }, { "bounty_lock", "Locks {0} coins until you confirm the job" }, { "your_bounty", "Your job" }, { "bounty_by", "Posted by {0}" }, { "bounty_no_desc", "(no description)" }, { "bounty_reward", "Reward: <color=#FFD27A>{0}</color> coins" }, { "b_open", "Open — waiting for a contractor" }, { "b_claimed", "Taken by {0}" }, { "b_submitted", "{0} reports the job as done" }, { "bounty_timeout_in", "You can cancel if not reported within {0}" }, { "new_bounty_title", "New job" }, { "bounty_title_label", "Job title" }, { "bounty_desc_label", "Description (what exactly, where, by when)" }, { "bounty_title_ph", "e.g. Build a bridge over the river near spawn" }, { "bounty_reward_label", "Reward:" }, { "btn_cancel_order", "Cancel order (refund {0})" }, { "btn_no_items", "You don't have this item" }, { "btn_fill", "Sell {0} for {1}" }, { "btn_admin_cancel", "Admin: cancel" }, { "btn_admin_pay", "Admin: pay out" }, { "btn_cancel_bounty", "Cancel job (refund {0})" }, { "btn_claim", "Take this job" }, { "btn_submit", "Report as done" }, { "btn_abandon", "Abandon" }, { "btn_confirm", "Confirm & pay" }, { "btn_reject", "Reject" }, { "btn_cancel_inactive", "Cancel (no report)" }, { "btn_taken", "Already taken" }, { "btn_wait_confirm", "Waiting for confirmation" }, { "btn_post_order", "Post buy order" }, { "btn_post_bounty", "Post job" }, { "btn_need_title", "Enter a title" }, { "btn_need_reward", "Enter a reward" }, { "h_order_escrow", "Buy order posted: {0}" }, { "h_order_refund", "Buy order refund: {0}" }, { "h_order_sale", "Sold to buy order: {0}" }, { "h_order_filled", "Delivered to your order: {0}" }, { "h_bounty_escrow", "Job posted: {0}" }, { "h_bounty_refund", "Job refund: {0}" }, { "h_bounty_reward", "Job reward: {0}" }, { "h_bounty_paid", "Job completed: {0}" }, { "h_guild_out", "Paid to guild {0}" }, { "h_guild_in", "Payout from guild {0}" }, { "h_by", "by {0}" }, { "c_pick_item", "Pick an item first." }, { "c_order_own", "This is your own buy order." }, { "c_not_enough_items", "You don't have that many of this item." }, { "c_bounty_title", "Enter a job title." }, { "c_bounty_reward", "Enter a reward (at least 1 coin)." }, { "s_order_limit", "Buy order limit reached ({0})." }, { "s_order_created", "Buy order posted: {0} at {1} coins each. {2} coins locked." }, { "s_order_gone", "This buy order no longer exists." }, { "s_order_own", "You cannot sell to your own buy order." }, { "s_order_wrong_item", "These items do not match the order." }, { "s_order_sold", "Sold {0} to a buy order. +{1} coins added to your balance." }, { "s_order_filled", "{0} delivered {1} to your buy order. The items are waiting in your MAILBOX." }, { "s_order_cancelled", "Buy order cancelled: {0}. {1} coins returned to the balance." }, { "s_order_cancelled_admin", "An admin cancelled your buy order: {0}. {1} coins returned to your balance." }, { "s_order_expired", "Your buy order expired: {0}. {1} coins returned to your balance." }, { "s_bounty_no_title", "The job needs a title." }, { "s_bounty_too_long", "The title or description is too long." }, { "s_bounty_bad_reward", "The reward must be between 1 and {0} coins." }, { "s_bounty_limit", "Job limit reached ({0})." }, { "s_bounty_created", "Job posted: \"{0}\" — {1} coins locked until you confirm it is done." }, { "s_bounty_gone", "This job no longer exists." }, { "s_bounty_not_allowed", "This action is not available right now." }, { "s_bounty_claimed", "You took the job: \"{0}\". Report it as done in the JOBS tab when finished." }, { "s_bounty_claimed_owner", "{0} took your job: \"{1}\"." }, { "s_bounty_abandoned", "You abandoned the job: \"{0}\"." }, { "s_bounty_abandoned_owner", "{0} abandoned your job \"{1}\". It is open again." }, { "s_bounty_submitted", "You reported \"{0}\" as done. Waiting for confirmation." }, { "s_bounty_submitted_owner", "{0} reports your job \"{1}\" as done — check it and confirm or reject it in the JOBS tab." }, { "s_bounty_confirmed", "\"{0}\" confirmed — reward paid to {1}." }, { "s_bounty_paid", "{0} confirmed \"{1}\". +{2} coins added to your balance." }, { "s_bounty_confirmed_admin", "An admin confirmed your job \"{0}\" and paid the reward." }, { "s_bounty_rejected", "You rejected the report for \"{0}\". The contractor can continue the job." }, { "s_bounty_rejected_contractor", "{0} rejected your report for \"{1}\". Finish the job and report it again." }, { "s_bounty_cancelled", "Job cancelled: \"{0}\". {1} coins returned to the balance." }, { "s_bounty_cancelled_contractor", "The job \"{0}\" you were working on was cancelled." }, { "s_bounty_cancelled_admin", "An admin cancelled your job \"{0}\". {1} coins returned to your balance." }, { "s_bounty_expired", "Nobody took your job \"{0}\". {1} coins returned to your balance." }, { "price_stats", "Market price: avg <color=#FFD27A>{0}</color>, range {1}–{2} (trades: {3})" }, { "price_stats_one", "Market price: <color=#FFD27A>{0}</color> (trades: {1})" }, { "price_none", "Market price: no trades yet" }, { "avg_short", "market avg {0}" }, { "cat_all", "All categories" }, { "cat_weapons", "Weapons" }, { "cat_armor", "Armor" }, { "cat_tools", "Tools" }, { "cat_materials", "Materials" }, { "cat_food", "Food" }, { "cat_potions", "Potions" }, { "cat_other", "Other" }, { "hint_order_topic", "Buy order for this item" }, { "hint_order_better", "Someone pays at least your price" }, { "hint_order_text", "{0} is buying {1}× at {2} coins each." }, { "hint_click_order", "Click to open the order — you get paid instantly." }, { "hint_listing_topic", "This item is on the market" }, { "hint_listing_better", "Same or cheaper on the market" }, { "hint_listing_text", "{0} sells {1}× at {2} coins each." }, { "hint_click_listing", "Click to open the listing and buy it now." } }; private static readonly Dictionary<string, string> Pl = new Dictionary<string, string> { { "title", "TARGOWISKO" }, { "tab_buy", "TARGOWISKO" }, { "tab_sell", "SPRZEDAJ" }, { "tab_mine", "MOJE OFERTY" }, { "tab_mail", "SKRZYNKA" }, { "tab_bank", "BANK" }, { "hdr_inventory", "Ekwipunek" }, { "hdr_balance", "Saldo" }, { "connecting", "Łączenie z targowiskiem…" }, { "search", "Szukaj przedmiotu lub sprzedawcy…" }, { "sort_new", "Sortuj: najnowsze" }, { "sort_cheap", "Sortuj: najtańsze" }, { "sort_expensive", "Sortuj: najdroższe" }, { "sort_name", "Sortuj: nazwa" }, { "pick", "Wybierz pozycję z listy po lewej." }, { "loading", "Wczytywanie ofert…" }, { "no_match", "Nic nie pasuje do wyszukiwania." }, { "no_listings", "Na targowisku nie ma jeszcze żadnych ofert.\nWystaw coś w zakładce SPRZEDAJ!" }, { "nothing_to_sell", "Nie masz w ekwipunku nic, co można wystawić.\nZałożone przedmioty i monety są pomijane." }, { "no_own", "Nie masz aktywnych ofert." }, { "mail_empty_list", "Skrzynka jest pusta.\nTrafiają tu przedmioty z wycofanych i wygasłych ofert." }, { "no_history", "Brak transakcji." }, { "your_listing", "Twoja oferta" }, { "expires_in", "wygasa za {0}" }, { "trader_value", "Wartość u kupca: {0} / szt." }, { "seller", "Sprzedawca: {0}" }, { "price_each", "Cena za sztukę: <color=#FFD27A>{0}</color> monet" }, { "available", "Dostępne: {0} szt." }, { "remaining", "Pozostało: {0} szt." }, { "listed_ago", "Wystawiono {0} temu" }, { "expires_in_cap", "Wygasa za {0}" }, { "no_expiry", "Bez terminu ważności" }, { "in_inventory", "W ekwipunku: {0} szt." }, { "my_listings_count", "Twoje oferty: {0} / {1}" }, { "expires_after", "Oferta wygasa po {0} h" }, { "never_expires", "Oferty nie wygasają" }, { "qty", "Ilość:" }, { "price", "Cena / szt.:" }, { "max", "Max" }, { "missing_mod", "<color=#FF7766>Ten przedmiot pochodzi z moda, którego nie masz zainstalowanego.</color>" }, { "quality", "poz. {0}" }, { "mail_title", "Twoja skrzynka" }, { "mail_desc", "Czekają tu przedmioty z wycofanych i wygasłych ofert oraz zakupy, których nie dało się dostarczyć.\n\n„Odbierz wszystko” przenosi je do ekwipunku. Czego nie zmieścisz, wyląduje na ziemi obok Ciebie." }, { "mail_items", "Przedmioty: {0}" }, { "total_buy", "Razem: {0} monet" }, { "pay_split", "Z salda {0}, z ekwipunku {1}" }, { "total_sell", "Za całość dostaniesz: {0} monet" }, { "total_sell_fee", "Razem {0}, prowizja {1}% – dostaniesz {2}" }, { "btn_own", "To Twoja oferta" }, { "btn_poor", "Za mało monet" }, { "btn_buy", "Kup za {0}" }, { "btn_need_price", "Podaj cenę" }, { "btn_max_price", "Maks. {0} / szt." }, { "btn_sell", "Wystaw na sprzedaż" }, { "btn_cancel", "Wycofaj ofertę" }, { "btn_collect", "Odbierz wszystko" }, { "btn_wait", "Czekam na serwer…" }, { "bank_title", "Bank targowiska" }, { "bank_desc", "Monety na saldzie są przechowywane na serwerze, nie w ekwipunku — nie stracisz ich po śmierci. Tu trafia zarobek ze sprzedaży i stąd w pierwszej kolejności płacisz za zakupy." }, { "bank_balance", "Saldo: <color=#FFD27A>{0}</color> monet" }, { "bank_inventory", "W ekwipunku: {0} monet" }, { "bank_amount", "Monety:" }, { "btn_deposit", "Wpłać" }, { "btn_withdraw", "Wypłać" }, { "btn_deposit_all", "Wpłać wszystko" }, { "btn_withdraw_all", "Wypłać wszystko" }, { "h_deposit", "Wpłata" }, { "h_withdraw", "Wypłata" }, { "h_sale", "Sprzedaż: {0}" }, { "h_purchase", "Zakup: {0}" }, { "h_to", "dla {0}" }, { "h_from", "od {0}" }, { "h_fee", "prowizja {0}" }, { "ago", "{0} temu" }, { "span_d", "{0} d {1} h" }, { "span_h", "{0} h {1} min" }, { "span_m", "{0} min" }, { "inv_full_drop", "Ekwipunek pełny — część rzeczy leży na ziemi obok Ciebie" }, { "unknown_prefab", "Nie znam przedmiotu {0} — brak moda?" }, { "hud_prefix", "Targowisko: {0}" }, { "c_no_player", "Brak postaci." }, { "c_busy", "Poczekaj na odpowiedź serwera." }, { "c_item_gone", "Tego przedmiotu nie ma już w ekwipunku." }, { "c_unequip", "Najpierw zdejmij ten przedmiot." }, { "c_cannot_list", "Tego przedmiotu nie da się wystawić." }, { "c_bad_amount", "Nieprawidłowa ilość." }, { "c_need_price", "Podaj cenę (co najmniej 1 moneta)." }, { "c_max_price", "Maksymalna cena to {0} monet za sztukę." }, { "c_total_high", "Łączna cena jest zbyt wysoka." }, { "c_remove_failed", "Nie udało się zabrać przedmiotu z ekwipunku." }, { "c_own", "To Twoja oferta." }, { "c_poor", "Masz za mało monet (potrzeba {0})." }, { "c_no_coins_inv", "Nie masz tylu monet w ekwipunku." }, { "c_no_balance", "Nie masz tylu monet na saldzie." }, { "c_no_space", "Brak miejsca w ekwipunku na {0} monet (zmieści się {1})." }, { "s_unresolved_item", "Serwer nie rozpoznał Twojej postaci — przedmiot wrócił do ekwipunku." }, { "s_unresolved_coins", "Serwer nie rozpoznał Twojej postaci — monety wróciły." }, { "s_bad_amount", "Nieprawidłowa ilość." }, { "s_unknown_item", "Serwer nie zna tego przedmiotu (brak moda na serwerze?)." }, { "s_blocked", "Tego przedmiotu nie można wystawić na targowisku." }, { "s_bad_price", "Cena musi wynosić od 1 do {0} monet." }, { "s_total_high", "Łączna cena jest zbyt wysoka." }, { "s_limit", "Osiągnięto limit ofert ({0})." }, { "s_create_failed", "{0} Przedmiot wrócił do Ciebie." }, { "s_listed", "Wystawiono: {0} po {1} monet/szt." }, { "s_gone", "Ta oferta jest już nieaktualna." }, { "s_own", "Nie możesz kupić własnej oferty." }, { "s_amount_changed", "Dostępna ilość się zmieniła." }, { "s_price_changed", "Cena oferty się zmieniła." }, { "s_no_funds", "Za mało monet: potrzeba {0}, na saldzie masz {1}." }, { "s_buy_failed_deposit", "{0} Wpłacone monety zostały na Twoim saldzie targowiska." }, { "s_bought", "Kupiono: {0} za {1} monet." }, { "s_sold", "{0} kupił(a) od Ciebie {1}. +{2} monet na Twoim saldzie targowiska." }, { "s_not_found", "Nie znaleziono tej oferty." }, { "s_cancelled", "Wycofano ofertę: {0}." }, { "s_mail_empty", "Skrzynka jest pusta." }, { "s_claimed", "Odebrano zawartość skrzynki." }, { "s_expired", "Twoja oferta wygasła: {0}. Przedmiot czeka w skrzynce targowiska." }, { "s_deposited", "Wpłacono {0} monet. Saldo: {1}." }, { "s_withdrawn", "Wypłacono {0} monet. Saldo: {1}." }, { "s_withdraw_too_much", "Na saldzie masz tylko {0} monet." }, { "tab_orders", "SKUP" }, { "tab_jobs", "ZADANIA" }, { "mode_orders", "Wszystkie zlecenia kupna" }, { "mode_bounties", "Wszystkie zadania" }, { "mode_new_order", "+ Dodaj zlecenie kupna" }, { "mode_new_bounty", "+ Dodaj zadanie" }, { "pick_search", "Wyszukaj przedmiot, który chcesz kupić…" }, { "pick_hint", "Wpisz co najmniej 2 litery nazwy przedmiotu." }, { "pick_none", "Brak pasujących przedmiotów." }, { "pick_order_item", "Wybierz z listy po lewej przedmiot, który chcesz kupić." }, { "no_orders", "Nie ma jeszcze zleceń kupna.\nDodaj własne przyciskiem „+ Dodaj zlecenie kupna”!" }, { "no_bounties", "Nie ma jeszcze żadnych zadań.\nDodaj własne przyciskiem „+ Dodaj zadanie”!" }, { "order_row", "Kupię: {0}" }, { "order_title", "Kupię: {0}" }, { "order_by", "Kupujący: {0}" }, { "your_order", "Twoje zlecenie kupna" }, { "order_remaining", "Jeszcze potrzeba: {0} szt. (dostarczono {1})" }, { "order_escrow", "Zablokowane w zleceniu: {0} monet" }, { "you_have", "Masz: {0}" }, { "new_order_title", "Kupię: {0}" }, { "my_orders_count", "Twoje zlecenia kupna: {0} / {1}" }, { "my_bounties_count", "Twoje zadania: {0} / {1}" }, { "order_lock", "Zablokuje {0} monet do czasu dostawy" }, { "bounty_lock", "Zablokuje {0} monet do potwierdzenia wykonania" }, { "your_bounty", "Twoje zadanie" }, { "bounty_by", "Zleceniodawca: {0}" }, { "bounty_no_desc", "(brak opisu)" }, { "bounty_reward", "Nagroda: <color=#FFD27A>{0}</color> monet" }, { "b_open", "Otwarte — czeka na wykonawcę" }, { "b_claimed", "W realizacji: {0}" }, { "b_submitted", "{0} zgłasza wykonanie" }, { "bounty_timeout_in", "Anulujesz, jeśli wykonawca nie zgłosi wykonania w ciągu {0}" }, { "new_bounty_title", "Nowe zadanie" }, { "bounty_title_label", "Tytuł zadania" }, { "bounty_desc_label", "Opis (co dokładnie, gdzie, do kiedy)" }, { "bounty_title_ph", "np. Zbuduj most nad rzeką przy spawnie" }, { "bounty_reward_label", "Nagroda:" }, { "btn_cancel_order", "Anuluj zlecenie (zwrot {0})" }, { "btn_no_items", "Nie masz tego przedmiotu" }, { "btn_fill", "Sprzedaj {0} szt. za {1}" }, { "btn_admin_cancel", "Admin: anuluj" }, { "btn_admin_pay", "Admin: wypłać" }, { "btn_cancel_bounty", "Anuluj zadanie (zwrot {0})" }, { "btn_claim", "Przyjmij zlecenie" }, { "btn_submit", "Zgłoś wykonanie" }, { "btn_abandon", "Zrezygnuj" }, { "btn_confirm", "Potwierdź i zapłać" }, { "btn_reject", "Odrzuć" }, { "btn_cancel_inactive", "Anuluj (brak zgłoszenia)" }, { "btn_taken", "Zlecenie zajęte" }, { "btn_wait_confirm", "Czeka na potwierdzenie" }, { "btn_post_order", "Dodaj zlecenie kupna" }, { "btn_post_bounty", "Dodaj zadanie" }, { "btn_need_title", "Podaj tytuł" }, { "btn_need_reward", "Podaj nagrodę" }, { "h_order_escrow", "Zlecenie kupna: {0}" }, { "h_order_refund", "Zwrot ze zlecenia: {0}" }, { "h_order_sale", "Sprzedaż do zlecenia: {0}" }, { "h_order_filled", "Dostawa do Twojego zlecenia: {0}" }, { "h_bounty_escrow", "Dodane zadanie: {0}" }, { "h_bounty_refund", "Zwrot za zadanie: {0}" }, { "h_bounty_reward", "Nagroda za zadanie: {0}" }, { "h_bounty_paid", "Zadanie wykonane: {0}" }, { "h_guild_out", "Wpłata do gildii {0}" }, { "h_guild_in", "Wypłata z gildii {0}" }, { "h_by", "wykonawca: {0}" }, { "c_pick_item", "Najpierw wybierz przedmiot." }, { "c_order_own", "To Twoje zlecenie kupna." }, { "c_not_enough_items", "Nie masz tylu sztuk tego przedmiotu." }, { "c_bounty_title", "Podaj tytuł zadania." }, { "c_bounty_reward", "Podaj nagrodę (co najmniej 1 moneta)." }, { "s_order_limit", "Osiągnięto limit zleceń kupna ({0})." }, { "s_order_created", "Dodano zlecenie kupna: {0} po {1} monet/szt. Zablokowano {2} monet." }, { "s_order_gone", "To zlecenie kupna już nie istnieje." }, { "s_order_own", "Nie możesz sprzedać do własnego zlecenia." }, { "s_order_wrong_item", "Te przedmioty nie pasują do zlecenia." }, { "s_order_sold", "Sprzedano {0} do zlecenia. +{1} monet na saldzie." }, { "s_order_filled", "{0} dostarczył(a) {1} do Twojego zlecenia. Przedmioty czekają w SKRZYNCE." }, { "s_order_cancelled", "Anulowano zlecenie kupna: {0}. {1} monet wróciło na saldo." }, { "s_order_cancelled_admin", "Admin anulował Twoje zlecenie kupna: {0}. {1} monet wróciło na saldo." }, { "s_order_expired", "Twoje zlecenie kupna wygasło: {0}. {1} monet wróciło na saldo." }, { "s_bounty_no_title", "Zadanie musi mieć tytuł." }, { "s_bounty_too_long", "Tytuł lub opis jest za długi." }, { "s_bounty_bad_reward", "Nagroda musi wynosić od 1 do {0} monet." }, { "s_bounty_limit", "Osiągnięto limit zadań ({0})." }, { "s_bounty_created", "Dodano zadanie: „{0}” — {1} monet zablokowano do potwierdzenia wykonania." }, { "s_bounty_gone", "To zadanie już nie istnieje." }, { "s_bounty_not_allowed", "Ta akcja nie jest teraz dostępna." }, { "s_bounty_claimed", "Przyjąłeś zadanie: „{0}”. Po wykonaniu zgłoś to w zakładce ZADANIA." }, { "s_bounty_claimed_owner", "{0} przyjął(a) Twoje zadanie: „{1}”." }, { "s_bounty_abandoned", "Zrezygnowałeś z zadania: „{0}”." }, { "s_bounty_abandoned_owner", "{0} zrezygnował(a) z Twojego zadania „{1}”. Jest znowu otwarte." }, { "s_bounty_submitted", "Zgłosiłeś wykonanie „{0}”. Czekasz na potwierdzenie." }, { "s_bounty_submitted_owner", "{0} zgłasza wykonanie zadania „{1}” — sprawdź i potwierdź lub odrzuć w zakładce ZADANIA." }, { "s_bounty_confirmed", "Potwierdzono „{0}” — nagroda wypłacona dla {1}." }, { "s_bounty_paid", "{0} potwierdził(a) „{1}”. +{2} monet na Twoim saldzie." }, { "s_bounty_confirmed_admin", "Admin potwierdził Twoje zadanie „{0}” i wypłacił nagrodę." }, { "s_bounty_rejected", "Odrzuciłeś zgłoszenie „{0}”. Wykonawca może kontynuować pracę." }, { "s_bounty_rejected_contractor", "{0} odrzucił(a) Twoje zgłoszenie „{1}”. Dokończ zadanie i zgłoś je ponownie." }, { "s_bounty_cancelled", "Anulowano zadanie: „{0}”. {1} monet wróciło na saldo." }, { "s_bounty_cancelled_contractor", "Zadanie „{0}”, nad którym pracowałeś, zostało anulowane." }, { "s_bounty_cancelled_admin", "Admin anulował Twoje zadanie „{0}”. {1} monet wróciło na saldo." }, { "s_bounty_expired", "Nikt nie przyjął Twojego zadania „{0}”. {1} monet wróciło na saldo." }, { "price_stats", "Cena rynkowa: średnio <color=#FFD27A>{0}</color>, od {1} do {2} (transakcji: {3})" }, { "price_stats_one", "Cena rynkowa: <color=#FFD27A>{0}</color> (transakcji: {1})" }, { "price_none", "Cena rynkowa: brak transakcji" }, { "avg_short", "rynek śr. {0}" }, { "cat_all", "Wszystkie kategorie" }, { "cat_weapons", "Broń" }, { "cat_armor", "Pancerz" }, { "cat_tools", "Narzędzia" }, { "cat_materials", "Surowce" }, { "cat_food", "Jedzenie" }, { "cat_potions", "Mikstury" }, { "cat_other", "Inne" }, { "hint_order_topic", "Zlecenie kupna na ten przedmiot" }, { "hint_order_better", "Ktoś płaci co najmniej Twoją cenę" }, { "hint_order_text", "{0} kupuje {1}× po {2} monet/szt." }, { "hint_click_order", "Kliknij, aby otworzyć zlecenie — zapłatę dostajesz od razu." }, { "hint_listing_topic", "Ten przedmiot jest na targu" }, { "hint_listing_better", "Na targu jest tak samo lub taniej" }, { "hint_listing_text", "{0} sprzedaje {1}× po {2} monet/szt." }, { "hint_click_listing", "Kliknij, aby otworzyć ofertę i kupić od razu." } }; public static string T(string key) { Dictionary<string, string> dictionary = ((Plugin.Language != null && Plugin.Language.Value == MarketLanguage.Polish) ? Pl : En); if (dictionary.TryGetValue(key, out var value)) { return value; } if (En.TryGetValue(key, out value)) { return value; } return key; } public static string T(string key, params object[] args) { string text = T(key); if (args == null || args.Length == 0) { return text; } object[] array = new object[args.Length]; for (int i = 0; i < args.Length; i++) { string text2 = args[i] as string; array[i] = ((text2 != null && text2.Length > 1 && text2[0] == '@') ? Nested(text2.Substring(1)) : args[i]); } try { return string.Format(text, array); } catch { return text; } } private static string Nested(string spec) { string[] array = spec.Split('|'); if (array.Length == 1) { return T(array[0]); } object[] array2 = new object[array.Length - 1]; for (int i = 1; i < array.Length; i++) { array2[i - 1] = array[i]; } return T(array[0], array2); } } internal class ListingView { public Listing Data; public ItemData Item; public string DisplayName; } internal class HistoryView { public HistoryEntry Data; public ItemData Item; public string ItemName; } internal static class MarketClient { public static bool HasData; public static long MyId; public static int FeePercent; public static int MaxListings; public static int ExpireHours; public static int MaxPrice = 100000; public static long ServerNowTicks; public static float ReceivedAt; public static readonly List<ListingView> Listings = new List<ListingView>(); public static long Balance; public static readonly List<ListingView> MailItems = new List<ListingView>(); public static readonly List<HistoryView> History = new List<HistoryView>(); public static float BusyUntil; public static readonly List<OrderView> Orders = new List<OrderView>(); public static readonly List<Bounty> Bounties = new List<Bounty>(); public static bool IsAdmin; public static int MaxOrders = 10; public static int MaxBounties = 10; public static int BountyTimeoutHours = 72; public static readonly Dictionary<string, PriceStat> Prices = new Dictionary<string, PriceStat>(); public static bool Busy => Time.time < BusyUntil; public static void Reset() { HasData = false; Listings.Clear(); MailItems.Clear(); History.Clear(); Orders.Clear(); Bounties.Clear(); Prices.Clear(); Balance = 0L; BusyUntil = 0f; } private static void Send(string method, params object[] args) { long num = Rpc.ServerId(); if (ZRoutedRpc.instance != null && num != 0) { ZRoutedRpc.instance.InvokeRoutedRPC(num, method, args); } } public static void RequestData() { Send("Marketplace_RequestData"); } public static string DisplayName(ItemData item, string fallback) { if (item == null) { return fallback; } string text = Localization.instance.Localize(item.m_shared.m_name); if (item.m_shared.m_maxQuality > 1) { text = text + " · " + Loc.T("quality", item.m_quality); } return text; } private static ListingView MakeView(Listing l) { ListingView listingView = new ListingView(); listingView.Data = l; listingView.Item = l.Item.ToItemData(); listingView.DisplayName = DisplayName(listingView.Item, l.Item.Prefab); return listingView; } public static int CountCoins() { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { return ((Humanoid)localPlayer).GetInventory().CountItems("$item_coins", -1, false); } return 0; } public static long CoinCapacity() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { return 0L; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Coins"); if ((Object)(object)itemPrefab == (Object)null) { return 0L; } int num = Math.Max(1, itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_maxStackSize); Inventory inventory = ((Humanoid)localPlayer).GetInventory(); long num2 = (long)inventory.GetEmptySlots() * (long)num; foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem.m_shared.m_name == "$item_coins" && allItem.m_stack < num) { num2 += num - allItem.m_stack; } } return num2; } public static string CreateListing(ItemData item, int amount, int price) { //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Expected O, but got Unknown Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return Loc.T("c_no_player"); } if (Busy) { return Loc.T("c_busy"); } Inventory inventory = ((Humanoid)localPlayer).GetInventory(); if (item == null || !inventory.ContainsItem(item)) { return Loc.T("c_item_gone"); } if (((Humanoid)localPlayer).IsItemEquiped(item)) { return Loc.T("c_unequip"); } if ((Object)(object)item.m_dropPrefab == (Object)null) { return Loc.T("c_cannot_list"); } if (amount < 1 || amount > item.m_stack) { return Loc.T("c_bad_amount"); } if (price < 1) { return Loc.T("c_need_price"); } if (price > MaxPrice) { return Loc.T("c_max_price", MaxPrice); } if ((long)price * (long)amount > int.MaxValue) { return Loc.T("c_total_high"); } ItemRecord itemRecord = ItemRecord.FromItem(item, amount); if (!((amount >= item.m_stack) ? inventory.RemoveItem(item) : inventory.RemoveItem(item, amount))) { return Loc.T("c_remove_failed"); } ZPackage val = new ZPackage(); itemRecord.Write(val); val.Write(price); BusyUntil = Time.time + 5f; Send("Marketplace_Create", val); return null; } public static string Buy(ListingView v, int amount) { //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Expected O, but got Unknown Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return Loc.T("c_no_player"); } if (Busy) { return Loc.T("c_busy"); } if (v.Data.SellerId == MyId) { return Loc.T("c_own"); } if (amount < 1 || amount > v.Data.Item.Stack) { return Loc.T("c_bad_amount"); } long num = (long)v.Data.Price * (long)amount; if (num > int.MaxValue) { return Loc.T("c_total_high"); } int num2 = CountCoins(); if (Balance + num2 < num) { return Loc.T("c_poor", num); } int num3 = (int)Math.Max(0L, num - Balance); if (num3 > 0) { ((Humanoid)localPlayer).GetInventory().RemoveItem("$item_coins", num3, -1, false); } ZPackage val = new ZPackage(); val.Write(v.Data.Id); val.Write(amount); val.Write(v.Data.Price); val.Write(num3); BusyUntil = Time.time + 5f; Send("Marketplace_Buy", val); return null; } public static string Cancel(ListingView v) { if (Busy) { return Loc.T("c_busy"); } BusyUntil = Time.time + 5f; Send("Marketplace_Cancel", v.Data.Id); return null; } public static string Claim() { if (Busy) { return Loc.T("c_busy"); } BusyUntil = Time.time + 5f; Send("Marketplace_Claim"); return null; } public static string Deposit(int amount) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return Loc.T("c_no_player"); } if (Busy) { return Loc.T("c_busy"); } if (amount < 1) { return Loc.T("c_bad_amount"); } if (CountCoins() < amount) { return Loc.T("c_no_coins_inv"); } ((Humanoid)localPlayer).GetInventory().RemoveItem("$item_coins", amount, -1, false); BusyUntil = Time.time + 5f; Send("Marketplace_Deposit", amount); return null; } public static string Withdraw(int amount) { if ((Object)(object)Player.m_localPlayer == (Object)null) { return Loc.T("c_no_player"); } if (Busy) { return Loc.T("c_busy"); } if (amount < 1) { return Loc.T("c_bad_amount"); } if (Balance < amount) { return Loc.T("c_no_balance"); } long num = CoinCapacity(); if (num < amount) { return Loc.T("c_no_space", amount, num); } BusyUntil = Time.time + 5f; Send("Marketplace_Withdraw", amount); return null; } public static void RPC_Data(long sender, ZPackage pkg) { try { MyId = pkg.ReadLong(); FeePercent = pkg.ReadInt(); MaxListings = pkg.ReadInt(); ExpireHours = pkg.ReadInt(); MaxPrice = pkg.ReadInt(); ServerNowTicks = pkg.ReadLong(); ReceivedAt = Time.realtimeSinceStartup; Listings.Clear(); int num = pkg.ReadInt(); for (int i = 0; i < num; i++) { Listings.Add(MakeView(Listing.Read(pkg))); } Balance = pkg.ReadLong(); MailItems.Clear(); int num2 = pkg.ReadInt(); for (int j = 0; j < num2; j++) { Listing listing = new Listing(); listing.Id = -1 - j; listing.Item = ItemRecord.Read(pkg); MailItems.Add(MakeView(listing)); } History.Clear(); int num3 = pkg.ReadInt(); for (int k = 0; k < num3; k++) { HistoryView historyView = new HistoryView(); historyView.Data = HistoryEntry.Read(pkg, hasText: true); if (!string.IsNullOrEmpty(historyView.Data.Prefab)) { ItemRecord itemRecord = new ItemRecord(); itemRecord.Prefab = historyView.Data.Prefab; itemRecord.Stack = historyView.Data.Stack; historyView.Item = itemRecord.ToItemData(); historyView.ItemName = ((historyView.Item != null) ? Localization.instance.Localize(historyView.Item.m_shared.m_name) : historyView.Data.Prefab) + " x" + historyView.Data.Stack; } History.Add(historyView); } ReadOrders(pkg); ReadPrices(pkg); HasData = true; } catch (Exception ex) { Plugin.Log.LogError((object)("Error reading marketplace data: " + ex)); } BusyUntil = 0f; MarketUI.OnDataChanged(); } public static void RPC_Result(long sender, ZPackage pkg) { BusyUntil = 0f; bool ok = pkg.ReadBool(); string key = pkg.ReadString(); int num = pkg.ReadInt(); object[] array = new object[num]; for (int i = 0; i < num; i++) { array[i] = pkg.ReadString(); } string text = Localization.instance.Localize(Loc.T(key, array)); MarketUI.SetStatus(text, ok); if ((Object)(object)MessageHud.instance != (Object)null && !MarketUI.IsOpen) { MessageHud.instance.ShowMessage((MessageType)1, Loc.T("hud_prefix", text), 0, (Sprite)null, false, true); } } public static void RPC_Changed(long sender) { if (MarketUI.IsOpen) { RequestData(); } } public static void RPC_Deliver(long sender, ZPackage pkg) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { Plugin.Log.LogError((object)"Marketplace delivery without a character — items lost!"); return; } bool flag = false; long num = pkg.ReadLong(); if (num > 0) { flag |= GiveCoins(localPlayer, num); } int num2 = pkg.ReadInt(); for (int i = 0; i < num2; i++) { ItemRecord itemRecord = ItemRecord.Read(pkg); ItemData val = itemRecord.ToItemData(); if (val == null) { Plugin.Log.LogError((object)("Unknown marketplace prefab: " + itemRecord.Prefab + " x" + itemRecord.Stack)); MarketUI.SetStatus(Loc.T("unknown_prefab", itemRecord.Prefab), ok: false); } else { flag |= Give(localPlayer, val); } } if (flag) { MessageHud.instance.ShowMessage((MessageType)2, Loc.T("inv_full_drop"), 0, (Sprite)null, false, true); } MarketUI.OnInventoryChanged(); } private static bool GiveCoins(Player p, long amount) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Coins"); if ((Object)(object)itemPrefab == (Object)null) { return false; } ItemData itemData = itemPrefab.GetComponent<ItemDrop>().m_itemData; int num = Math.Max(1, itemData.m_shared.m_maxStackSize); bool flag = false; while (amount > 0) { int num2 = (int)Math.Min(amount, num); ItemData val = itemData.Clone(); val.m_dropPrefab = itemPrefab; val.m_stack = num2; flag |= Give(p, val); amount -= num2; } return flag; } private static bool Give(Player p, ItemData item) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0072: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) int num = Math.Max(1, item.m_shared.m_maxStackSize); bool result = false; int num2 = item.m_stack; while (num2 > 0) { ItemData val = ((num2 > num) ? item.Clone() : item); val.m_stack = Math.Min(num2, num); num2 -= val.m_stack; Inventory inventory = ((Humanoid)p).GetInventory(); if (!inventory.CanAddItem(val, -1) || !inventory.AddItem(val)) { Vector3 val2 = ((Component)p).transform.position + ((Component)p).transform.forward * 0.7f + Vector3.up; ItemDrop.DropItem(val, val.m_stack, val2, Quaternion.identity); result = true; } } return result; } private static void ReadOrders(ZPackage pkg) { Orders.Clear(); Bounties.Clear(); pkg.ReadLong(); int num = pkg.ReadInt(); for (int i = 0; i < num; i++) { OrderView orderView = new OrderView(); orderView.Data = BuyOrder.Read(pkg); ItemRecord itemRecord = new ItemRecord(); itemRecord.Prefab = orderView.Data.Prefab; itemRecord.Stack = 1; orderView.Item = itemRecord.ToItemData(); orderView.DisplayName = ((orderView.Item != null) ? Localization.instance.Localize(orderView.Item.m_shared.m_name) : orderView.Data.Prefab); Orders.Add(orderView); } pkg.ReadLong(); int num2 = pkg.ReadInt(); for (int j = 0; j < num2; j++) { Bounties.Add(Bounty.Read(pkg)); } IsAdmin = pkg.ReadBool(); MaxOrders = pkg.ReadInt(); MaxBounties = pkg.ReadInt(); BountyTimeoutHours = pkg.ReadInt(); } public static List<ItemData> MatchingItems(string prefab) { List<ItemData> list = new List<ItemData>(); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return list; } foreach (ItemData allItem in ((Humanoid)localPlayer).GetInventory().GetAllItems()) { if (!((Object)(object)allItem.m_dropPrefab == (Object)null) && !(((Object)allItem.m_dropPrefab).name != prefab) && !allItem.m_equipped && !((Humanoid)localPlayer).IsItemEquiped(allItem)) { list.Add(allItem); } } return list; } public static int CountMatching(string prefab) { int num = 0; foreach (ItemData item in MatchingItems(prefab)) { num += item.m_stack; } return num; } private static int TakeShortfall(long needed) { int num = (int)Math.Max(0L, needed - Balance); if (num > 0) { ((Humanoid)Player.m_localPlayer).GetInventory().RemoveItem("$item_coins", num, -1, false); } return num; } public static string CreateOrder(string prefab, int amount, int price) { //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Expected O, but got Unknown if ((Object)(object)Player.m_localPlayer == (Object)null) { return Loc.T("c_no_player"); } if (Busy) { return Loc.T("c_busy"); } if (string.IsNullOrEmpty(prefab)) { return Loc.T("c_pick_item"); } if (amount < 1) { return Loc.T("c_bad_amount"); } if (price < 1) { return Loc.T("c_need_price"); } if (price > MaxPrice) { return Loc.T("c_max_price", MaxPrice); } long num = (long)amount * (long)price; if (num > int.MaxValue) { return Loc.T("c_total_high"); } if (Balance + CountCoins() < num) { return Loc.T("c_poor", num); } int num2 = TakeShortfall(num); ZPackage val = new ZPackage(); val.Write(prefab); val.Write(amount); val.Write(price); val.Write(num2); BusyUntil = Time.time + 5f; Send("Marketplace_OrderCreate", val); return null; } public static string FillOrder(OrderView v, int amount) { //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Expected O, but got Unknown Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return Loc.T("c_no_player"); } if (Busy) { return Loc.T("c_busy"); } if (v.Data.BuyerId == MyId) { return Loc.T("c_order_own"); } if (amount < 1 || amount > v.Data.Remaining) { return Loc.T("c_bad_amount"); } if (CountMatching(v.Data.Prefab) < amount) { return Loc.T("c_not_enough_items"); } Inventory inventory = ((Humanoid)localPlayer).GetInventory(); List<ItemRecord> list = new List<ItemRecord>(); int num = amount; foreach (ItemData item in MatchingItems(v.Data.Prefab)) { if (num > 0) { int num2 = Math.Min(num, item.m_stack); list.Add(ItemRecord.FromItem(item, num2)); if (num2 >= item.m_stack) { inventory.RemoveItem(item); } else { inventory.RemoveItem(item, num2); } num -= num2; continue; } break; } ZPackage val = new ZPackage(); val.Write(v.Data.Id); val.Write(v.Data.Price); val.Write(list.Count); foreach (ItemRecord item2 in list) { item2.Write(val); } BusyUntil = Time.time + 5f; Send("Marketplace_OrderFill", val); return null; } public static string CancelOrder(OrderView v) { if (Busy) { return Loc.T("c_busy"); } BusyUntil = Time.time + 5f; Send("Marketplace_OrderCancel", v.Data.Id); return null; } public static string CreateBounty(string title, string desc, int reward) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown if ((Object)(object)Player.m_localPlayer == (Object)null) { return Loc.T("c_no_player"); } if (Busy) { return Loc.T("c_busy"); } title = (title ?? "").Trim(); desc = (desc ?? "").Trim(); if (title.Length == 0) { return Loc.T("c_bounty_title"); } if (reward < 1) { return Loc.T("c_bounty_reward"); } if (Balance + CountCoins() < reward) { return Loc.T("c_poor", reward); } int num = TakeShortfall(reward); ZPackage val = new ZPackage(); val.Write(title); val.Write(desc); val.Write(reward); val.Write(num); BusyUntil = Time.time + 5f; Send("Marketplace_BountyCreate", val); return null; } public static string BountyAct(Bounty b, BountyAction action) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown if (Busy) { return Loc.T("c_busy"); } ZPackage val = new ZPackage(); val.Write(b.Id); val.Write((int)action); BusyUntil = Time.time + 5f; Send("Marketplace_BountyAction", val); return null; } private static void ReadPrices(ZPackage pkg) { Prices.Clear(); int num = pkg.ReadInt(); for (int i = 0; i < num; i++) { PriceStat priceStat = PriceStat.Read(pkg); Prices[priceStat.Prefab] = priceStat; } } public static PriceStat GetStat(string prefab) { if (prefab == null || !Prices.TryGetValue(prefab, out var value)) { return null; } return value; } public static string PriceLine(string prefab) { PriceStat stat = GetStat(prefab); if (stat == null || stat.Trades == 0) { return Loc.T("price_none"); } if (stat.Min == stat.Max) { return Loc.T("price_stats_one", stat.Avg, stat.Trades); } return Loc.T("price_stats", stat.Avg, stat.Min, stat.Max, stat.Trades); } public static OrderView BestOrderFor(string prefab) { OrderView orderView = null; foreach (OrderView order in Orders) { if (!(order.Data.Prefab != prefab) && order.Data.BuyerId != MyId && order.Data.Remaining > 0 && (orderView == null || order.Data.Price > orderView.Data.Price)) { orderView = order; } } return orderView; } public static ListingView CheapestListingFor(string prefab) { ListingView listingView = null; foreach (ListingView listing in Listings) { if (!(listing.Data.Item.Prefab != prefab) && listing.Data.SellerId != MyId && (listingView == null || listing.Data.Price < listingView.Data.Price)) { listingView = listing; } } return listingView; } } internal class OrderView { public BuyOrder Data; public ItemData Item; public string DisplayName; } public static class MarketplaceApi { public static bool IsReady() { return MarketServer.ApiReady; } public static long GetBalance(long playerId) { return MarketServer.ApiBalance(playerId); } public static bool TryDebit(long playerId, string playerName, long amount, string note) { return MarketServer.ApiChange(playerId, playerName, -amount, note); } public static void Credit(long playerId, string playerName, long amount, string note) { MarketServer.ApiChange(playerId, playerName, amount, note); } } internal static class MarketServer { private class Trade { public long Ticks; public int Price; public int Qty; } private const int SaveVersion = 4; private const int MaxDeposit = 10000000; private const int MaxOrderAmount = 100000; private const int MaxBountyReward = 10000000; private const int TradesKept = 30; private const int TradesInStat = 20; private static bool _loaded; private static string _path; private static string _logPath; private static long _nextId = 1L; private static readonly List<Listing> Listings = new List<Listing>(); private static readonly Dictionary<long, Account> Accounts = new Dictionary<long, Account>(); private static long _nextOrderId = 1L; private static long _nextBountyId = 1L; private static readonly List<BuyOrder> Orders = new List<BuyOrder>(); private static readonly List<Bounty> Bounties = new List<Bounty>(); private static readonly Dictionary<string, List<Trade>> Trades = new Dictionary<string, List<Trade>>(); internal static bool ApiReady { get { if (_loaded) { return IsServer; } return false; } } private static bool IsServer { get { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } internal static long ApiBalance(long pid) { if (!ApiReady) { return 0L; } if (!Accounts.TryGetValue(pid, out var value)) { return 0L; } return value.Balance; } internal static bool ApiChange(long pid, string name, long delta, string note) { if (!ApiReady || pid == 0 || delta == 0) { return false; } Account account = Acc(pid, name); if (delta < 0 && account.Balance < -delta) { return false; } account.Balance += delta; HistoryEntry historyEntry = new HistoryEntry(); historyEntry.Kind = ((delta < 0) ? HistoryKind.GuildPayment : HistoryKind.GuildPayout); historyEntry.Amount = delta; historyEntry.Text = note ?? ""; account.AddHistory(historyEntry); Save(); Audit(name, pid, ((delta < 0) ? "API DEBIT " : "API CREDIT ") + Math.Abs(delta) + " (" + note + ") | balance " + account.Balance); long num = FindPeerOf(pid); if (num != 0) { SendData(num, pid); } return true; } public static void EnsureLoaded() { //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Expected O, but got Unknown if (_loaded || !IsServer) { return; } _loaded = true; Listings.Clear(); Accounts.Clear(); ClearOrders(); ClearPrices(); _nextId = 1L; string text = ZNet.instance.GetWorldName(); if (string.IsNullOrEmpty(text)) { text = "world"; } char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { text = text.Replace(oldChar, '_'); } string text2 = Path.Combine(Paths.ConfigPath, "Marketplace"); Directory.CreateDirectory(text2); _path = Path.Combine(text2, text + ".dat"); _logPath = Path.Combine(text2, text + ".log"); if (!File.Exists(_path)) { Plugin.Log.LogInfo((object)("New marketplace for world " + text)); return; } try { ZPackage val = new ZPackage(File.ReadAllBytes(_path)); int num = val.ReadInt(); _nextId = val.ReadLong(); int num2 = val.ReadInt(); for (int j = 0; j < num2; j++) { Listings.Add(Listing.Read(val)); } int num3 = val.ReadInt(); for (int k = 0; k < num3; k++) { long key = val.ReadLong(); Account account = new Account(); account.Name = val.ReadString(); account.Balance = val.ReadLong(); int num4 = val.ReadInt(); for (int l = 0; l < num4; l++) { account.Items.Add(ItemRecord.Read(val)); } if (num >= 2) { int num5 = val.ReadInt(); for (int m = 0; m < num5; m++) { account.History.Add(HistoryEntry.Read(val, num >= 3)); } } Accounts[key] = account; } if (num >= 3) { ReadOrders(val); } if (num >= 4) { ReadPrices(val); } Plugin.Log.LogInfo((object)("Marketplace loaded: " + Listings.Count + " listings, " + Accounts.Count + " accounts.")); } catch (Exception ex) { string text3 = _path + ".broken-" + DateTime.Now.ToString("yyyyMMdd-HHmmss"); Plugin.Log.LogError((object)("Could not load " + _path + " — copy saved as " + text3 + ". " + ex)); try { File.Copy(_path, text3, overwrite: true); } catch { } } } private static void Save() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown if (!_loaded || string.IsNullOrEmpty(_path)) { return; } try { ZPackage val = new ZPackage(); val.Write(4); val.Write(_nextId); val.Write(Listings.Count); foreach (Listing listing in Listings) { listing.Write(val); } int num = 0; foreach (KeyValuePair<long, Account> account in Accounts) { if (!account.Value.IsEmpty) { num++; } } val.Write(num); foreach (KeyValuePair<long, Account> account2 in Accounts) { Account value = account2.Value; if (value.IsEmpty) { continue; } val.Write(account2.Key); val.Write(value.Name ?? ""); val.Write(value.Balance); val.Write(value.Items.Count); foreach (ItemRecord item in value.Items) { item.Write(val); } val.Write(value.History.Count); foreach (HistoryEntry item2 in value.History) { item2.Write(val); } } WriteOrders(val); WritePrices(val); string text = _path + ".tmp"; File.WriteAllBytes(text, val.GetArray()); if (File.Exists(_path)) { File.Copy(_path, _path + ".bak", overwrite: true); } File.Copy(text, _path, overwrite: true); File.Delete(text); } catch (Exception ex) { Plugin.Log.LogError((object)("Marketplace save error: " + ex)); } } private static void Audit(string who, long pid, string what) { if (string.IsNullOrEmpty(_logPath)) { return; } try { File.AppendAllText(_logPath, DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "Z | " + who + " (" + pid + ") | " + what + Environment.NewLine); } catch { } } public static void Unload() { if (_loaded) { Save(); } _loaded = false; Listings.Clear(); Accounts.Clear(); ClearOrders(); ClearPrices(); } private static Account Acc(long pid, string name) { if (!Accounts.TryGetValue(pid, out var value)) { value = new Account(); Accounts[pid] = value; } if (!string.IsNullOrEmpty(name)) { value.Name = name; } return value; } private static bool Resolve(long sender, out long pid, out string name) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) pid = 0L; name = ""; if (sender == ZDOMan.GetSessionID()) { if ((Object)(object)Player.m_localPlayer == (Object)null) { return false; } pid = Player.m_localPlayer.GetPlayerID(); name = Player.m_localPlayer.GetPlayerName(); return pid != 0; } ZNetPeer peer = ZNet.instance.GetPeer(sender); if (peer == null) { return false; } ZDO zDO = ZDOMan.instance.GetZDO(peer.m_characterID); if (zDO != null) { pid = zDO.GetLong(ZDOVars.s_playerID, 0L); } if (pid == 0) { pid = peer.m_playerID; } name = peer.m_playerName ?? ""; return pid != 0; } private static long FindPeerOf(long pid) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer != (Object)null && Player.m_localPlayer.GetPlayerID() == pid) { return ZDOMan.GetSessionID(); } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { long num = 0L; ZDO zDO = ZDOMan.instance.GetZDO(peer.m_characterID); if (zDO != null) { num = zDO.GetLong(ZDOVars.s_playerID, 0L); } if (num == 0) { num = peer.m_playerID; } if (num == pid) { return peer.m_uid; } } return 0L; } private static void Result(long target, bool ok, string key, params object[] args) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(ok); val.Write(key); val.Write(args.Length); foreach (object obj in args) { val.Write((obj == null) ? "" : obj.ToString()); } ZRoutedRpc.instance.InvokeRoutedRPC(target, "Marketplace_Result", new object[1] { val }); } private static void BroadcastChanged() { ZRoutedRpc.instance.InvokeRoutedRPC(0L, "Marketplace_Changed", new object[0]); } private static void SendBack(long target, long coins, ItemRecord item) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(coins); val.Write((item != null) ? 1 : 0); item?.Write(val); ZRoutedRpc.instance.InvokeRoutedRPC(target, "Marketplace_Deliver", new object[1] { val }); } private static void DeliverItems(long pid, long target) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if (!Accounts.TryGetValue(pid, out var value) || value.Items.Count == 0) { return; } ZPackage val = new ZPackage(); val.Write(0L); val.Write(value.Items.Count); foreach (ItemRecord item in value.Items) { item.Write(val); } value.Items.Clear(); Save(); ZRoutedRpc.instance.InvokeRoutedRPC(target, "Marketplace_Deliver", new object[1] { val }); } private static bool IsBlocked(string prefab) { if (prefab == "Coins") { return true; } string value = Plugin.BlockedItems.Value; if (string.IsNullOrEmpty(value)) { return false; } string[] array = value.Split(','); foreach (string text in array) { if (string.Equals(text.Trim(), prefab, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private static int CountListings(long pid) { int num = 0; foreach (Listing listing in Listings) { if (listing.SellerId == pid) { num++; } } return num; } private static Listing Find(long id) { foreach (Listing listing in Listings) { if (listing.Id == id) { return listing; } } return null; } private static string Label(ItemRecord r) { string text = r.Prefab; if ((Object)(object)ObjectDB.instance != (Object)null) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(r.Prefab); if ((Object)(object)itemPrefab != (Object)null && (Object)(object)itemPrefab.GetComponent<ItemDrop>() != (Object)null) { text = itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_name; } } return text + " x" + r.Stack; } public static void RPC_RequestData(long sender) { if (IsServer) { EnsureLoaded(); if (Resolve(sender, out var pid, out var _)) { SendData(sender, pid); } } } private static void SendData(long target, long pid) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(pid); val.Write(Plugin.FeePercent.Value); val.Write(Plugin.MaxListingsPerPlayer.Value); val.Write(Plugin.ExpireHours.Value); val.Write(Plugin.MaxPricePerUnit.Value); val.Write(DateTime.UtcNow.Ticks); val.Write(Listings.Count); foreach (Listing listing in Listings) { listing.Write(val); } Accounts.TryGetValue(pid, out var value); val.Write(value?.Balance ?? 0); val.Write(value?.Items.Count ?? 0); if (value != null) { foreach (ItemRecord item in value.Items) { item.Write(val); } } val.Write(value?.History.Count ?? 0); if (value != null) { foreach (HistoryEntry item2 in value.History) { item2.Write(val); } } WriteOrders(val); val.Write(IsAdminSender(target)); val.Write(Plugin.MaxOrdersPerPlayer.Value); val.Write(Plugin.MaxBountiesPerPlayer.Value); val.Write(Plugin.BountyClaimTimeoutHours.Value); WriteStats(val); ZRoutedRpc.instance.InvokeRoutedRPC(target, "Marketplace_Data", new object[1] { val }); } public static void RPC_Create(long sender, ZPackage pkg) { if (!IsServer) { return; } EnsureLoaded(); ItemRecord itemRecord; int num; try { itemRecord = ItemRecord.Read(pkg); num = pkg.ReadInt(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Corrupted listing packet from " + sender + ": " + ex.Message)); return; } if (!Resolve(sender, out var pid, out var name)) { SendBack(sender, 0L, itemRecord); Result(sender, false, "s_unresolved_item"); return; } string text = null; if (itemRecord.Stack < 1) { text = "@s_bad_amount"; } else if (!ItemRecord.PrefabExists(itemRecord.Prefab)) { text = "@s_unknown_item"; } else if (IsBlocked(itemRecord.Prefab)) { text = "@s_blocked"; } else if (num < 1 || num > Plugin.MaxPricePerUnit.Value) { text = "@s_bad_price|" + Plugin.MaxPricePerUnit.Value; } else if ((long)num * (long)itemRecord.Stack > int.MaxValue) { text = "@s_total_high"; } else if (CountListings(pid) >= Plugin.MaxListingsPerPlayer.Value) { text = "@s_limit|" + Plugin.MaxListingsPerPlayer.Value; } if (text != null) { Acc(pid, name).Items.Add(itemRecord); DeliverItems(pid, sender); Result(sender, false, "s_create_failed", text); SendData(sender, pid); return; } Listing listing = new Listing(); listing.Id = _nextId++; listing.SellerId = pid; listing.SellerName = name; listing.Price = num; listing.CreatedTicks = DateTime.UtcNow.Ticks; listing.Item = itemRecord; Listings.Add(listing); Save(); Audit(name, pid, "LIST #" + listing.Id + " " + itemRecord.Prefab + " x" + itemRecord.Stack + " @ " + num); Result(sender, true, "s_listed", Label(itemRecord), num); SendData(sender, pid); BroadcastChanged(); } public static void RPC_Buy(long sender, ZPackage pkg) { if (!IsServer) { return; } EnsureLoaded(); long id; int num; int num2; int num3; try { id = pkg.ReadLong(); num = pkg.ReadInt(); num2 = pkg.ReadInt(); num3 = pkg.ReadInt(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Corrupted buy packet from " + sender + ": " + ex.Message)); return; } if (num3 < 0) { num3 = 0; } if (!Resolve(sender, out var pid, out var name)) { if (num3 > 0) { SendBack(sender, num3, null); } Result(sender, false, "s_unresolved_coins"); return; } Account account = Acc(pid, name); if (num3 > 0) { account.Balance += num3; HistoryEntry historyEntry = new HistoryEntry(); historyEntry.Kind = HistoryKind.Deposit; historyEntry.Amount = num3; account.AddHistory(historyEntry); Audit(name, pid, "DEPOSIT " + num3 + " (for purchase) | balance " + account.Balance); } Listing listing = Find(id); string text = null; if (listing == null) { text = "@s_gone"; } else if (listing.SellerId == pid) { text = "@s_own"; } else if (num < 1 || num > listing.Item.Stack) { text = "@s_amount_changed"; } else if (listing.Price != num2) { text = "@s_price_changed"; } else if (account.Balance < (long)listing.Price * (long)num) { text = "@s_no_funds|" + (long)listing.Price * (long)num + "|" + account.Balance; } if (text != null) { Save(); if (num3 > 0) { Result(sender, false, "s_buy_failed_deposit", text); } else { Result(sender, false, "s_raw", text); } SendData(sender, pid); return; } long num4 = (long)listing.Price * (long)num; ItemRecord itemRecord = listing.Item.Clone(num); RecordTrade(itemRecord.Prefab, listing.Price, num); listing.Item.Stack -= num; if (listing.Item.Stack <= 0) { Listings.Remove(listing); } long num5 = (long)Math.Floor((double)num4 * ((double)Plugin.FeePercent.Value / 100.0)); account.Balance -= num4; account.Items.Add(itemRecord); HistoryEntry historyEntry2 = new HistoryEntry(); historyEntry2.Kind = HistoryKind.Purchase; historyEntry2.Amount = -num4; historyEntry2.Prefab = itemRecord.Prefab; historyEntry2.Stack = num; historyEntry2.Other = listing.SellerName; account.AddHistory(historyEntry2); Account account2 = Acc(listing.SellerId, listing.SellerName); account2.Balance += num4 - num5; HistoryEntry historyEntry3 = new HistoryEntry(); historyEntry3.Kind = HistoryKind.Sale; historyEntry3.Amount = num4 - num5; historyEntry3.Fee = num5; historyEntry3.Prefab = itemRecord.Prefab; historyEntry3.Stack = num; historyEntry3.Other = name; account2.AddHistory(historyEntry3); Save(); Audit(name, pid, "BUY #" + listing.Id + " " + itemRecord.Prefab + " x" + num + " from " + listing.SellerName + " (" + listing.SellerId + ") for " + num4 + " fee " + num5 + " | buyer balance " + account.Balance + " | seller balance " + account2.Balance); DeliverItems(pid, sender); Result(sender, true, "s_bought", Label(itemRecord), num4); SendData(sender, pid); long num6 = FindPeerOf(listing.SellerId); if (num6 != 0) { Result(num6, true, "s_sold", name, Label(itemRecord), num4 - num5); if (num6 != sender) { SendData(num6, listing.SellerId); } } BroadcastChanged(); } public static void RPC_Cancel(long sender, long id) { if (!IsServer) { return; } EnsureLoaded(); if (Resolve(sender, out var pid, out var name)) { Listing listing = Find(id); if (listing == null || listing.SellerId != pid) { Result(sender, false, "s_not_found"); SendData(sender, pid); return; } Listings.Remove(listing); Acc(pid, name).Items.Add(listing.Item); Save(); Audit(name, pid, "CANCEL #" + listing.Id + " " + listing.Item.Prefab + " x" + listing.Item.Stack); DeliverItems(pid, sender); Result(sender, true, "s_cancelled", Label(listing.Item)); SendData(sender, pid); BroadcastChanged(); } } public static void RPC_Claim(long sender) { if (!IsServer) { return; } EnsureLoaded(); if (Resolve(sender, out var pid, out var _)) { if (!Accounts.TryGetValue(pid, out var value) || value.Items.Count == 0) { Result(sender, false, "s_mail_empty"); } else { DeliverItems(pid, sender); Result(sender, true, "s_claimed"); } SendData(sender, pid); } } public static void RPC_Deposit(long sender, int amount) { if (!IsServer) { return; } EnsureLoaded(); if (amount > 0) { if (!Resolve(sender, out var pid, out var name)) { SendBack(sender, amount, null); Result(sender, false, "s_unresolved_coins"); return; } if (amount > 10000000) { SendBack(sender, amount, null); Result(sender, false, "s_bad_amount"); return; } Account account = Acc(pid, name); account.Balance += amount; HistoryEntry historyEntry = new HistoryEntry(); historyEntry.Kind = HistoryKind.Deposit; historyEntry.Amount = amount; account.AddHistory(historyEntry); Save(); Audit(name, pid, "DEPOSIT " + amount + " | balance " + account.Balance); Result(sender, true, "s_deposited", amount, account.Balance); SendData(sender, pid); } } public static void RPC_Withdraw(long sender, int amount) { if (!IsServer) { return; } EnsureLoaded(); if (amount > 0 && Resolve(sender, out var pid, out var name)) { Account account = Acc(pid, name); if (account.Balance < amount) { Result(sender, false, "s_withdraw_too_much", account.Balance); SendData(sender, pid); return; } account.Balance -= amount; HistoryEntry historyEntry = new HistoryEntry(); historyEntry.Kind = HistoryKind.Withdraw; historyEntry.Amount = -amount; account.AddHistory(historyEntry); Save(); Audit(name, pid, "WITHDRAW " + amount + " | balance " + account.Balance); SendBack(sender, amount, null); Result(sender, true, "s_withdrawn", amount, account.Balance); SendData(sender, pid); } } public static void Tick() { if (!_loaded || Plugin.ExpireHours.Value <= 0) { return; } long num = DateTime.UtcNow.Ticks - TimeSpan.FromHours(Plugin.ExpireHours.Value).Ticks; bool flag = ExpireOrders(num); for (int num2 = Listings.Count - 1; num2 >= 0; num2--) { Listing listing = Listings[num2]; if (listing.CreatedTicks <= num) { Acc(listing.SellerId, listing.SellerName).Items.Add(listing.Item); Listings.RemoveAt(num2); flag = true; Audit(listing.SellerName, listing.SellerId, "EXPIRE #" + listing.Id + " " + listing.Item.Prefab + " x" + listing.Item.Stack); long num3 = FindPeerOf(listing.SellerId); if (num3 != 0) { Result(num3, false, "s_expired", Label(listing.Item)); } } } if (flag) { Save(); BroadcastChanged(); } } private static void ClearOrders() { Orders.Clear(); Bounties.Clear(); _nextOrderId = 1L; _nextBountyId = 1L; } private static void ReadOrders(ZPackage pkg) { _nextOrderId = pkg.ReadLong(); int num = pkg.ReadInt(); for (int i = 0; i < num; i++) { Orders.Add(BuyOrder.Read(pkg)); } _nextBountyId = pkg.ReadLong(); int num2 = pkg.ReadInt(); for (int j = 0; j < num2; j++) { Bounties.Add(Bounty.Read(pkg)); } } private static void WriteOrders(ZPackage pkg) { pkg.Write(_nextOrderId); pkg.Write(Orders.Count); foreach (BuyOrder order in Orders) { order.Write(pkg); } pkg.Write(_nextBountyId); pkg.Write(Bounties.Count); foreach (Bounty bounty in Bounties) { bounty.Write(pkg); } } private static bool IsAdminSender(long sender) { try { if (sender == ZDOMan.GetSessionID()) { return ZNet.instance.LocalPlayerIsAdminOrHost(); } ZNetPeer peer = ZNet.instance.GetPeer(sender); return peer != null && peer.m_socket != null && ZNet.instance.IsAdmin(peer.m_socket.GetHostName()); } catch { return false; } } private static void History(Account a, HistoryKind kind, long amount, long fee, string prefab, int stack, string other, string text) { HistoryEntry historyEntry = new HistoryEntry(); historyEntry.Kind = kind; historyEntry.Amount = amount; historyEntry.Fee = fee; historyEntry.Prefab = prefab ?? ""; historyEntry.Stack = stack; historyEntry.Other = other ?? ""; historyEntry.Text = text ?? ""; a.AddHistory(historyEntry); } private static void ApplyDeposit(Account a, long pid, string name, int deposit, string reason) { if (deposit > 0) { a.Balance += deposit; History(a, HistoryKind.Deposit, deposit, 0L, "", 0, "", ""); Audit(name, pid, "DEPOSIT " + deposit + " (" + reason + ") | balance " + a.Balance); } } private static void Notify(long pid, bool ok, string key, params object[] args) { long num = FindPeerOf(pid); if (num != 0) { Result(num, ok, key, args); SendData(num, pid); } } private static void SendItemsBack(long target, List<ItemRecord> items) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(0L); val.Write(items.Count); foreach (ItemRecord item in items) { item.Write(val); } ZRoutedRpc.instance.InvokeRoutedRPC(target, "Marketplace_Deliver", new object[1] { val }); } private static string PrefabToken(string prefab) { ItemRecord itemRecord = new ItemRecord(); itemRecord.Prefab = prefab; itemRecord.Stack = 1; string text = Label(itemRecord); return text.Substring(0, text.Length - 3); } private static BuyOrder FindOrder(long id) { foreach (BuyOrder order in Orders) { if (order.Id == id) { return order; } } return null; } private static Bounty FindBounty(long id) { foreach (Bounty bounty in Bounties) { if (bounty.Id == id) { return bounty; } } return null; } private static void FailWithDeposit(long sender, long pid, int deposit, string error) { Save(); if (deposit > 0) { Result(sender, false, "s_buy_failed_deposit", error); } else { Result(sender, false, "s_raw", error); } SendData(sender, pid); } public static void RPC_OrderCreate(long sender, ZPackage pkg) { if (!IsServer) { return; } EnsureLoaded(); string text; int num; int num2; int num3; try { text = pkg.ReadString(); num = pkg.ReadInt(); num2 = pkg.ReadInt(); num3 = Math.Max(0, pkg.ReadInt()); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Corrupted order packet from " + sender + ": " + ex.Message)); return; } if (!Resolve(sender, out var pid, out var name)) { if (num3 > 0) { SendBack(sender, num3, null); } Result(sender, false, "s_unresolved_coins"); return; } Account account = Acc(pid, name); ApplyDeposit(account, pid, name, num3, "for buy order"); long num4 = (long)num * (long)num2; int num5 = 0; foreach (BuyOrder order in Orders) { if (order.BuyerId == pid) { num5++; } } string text2 = null; if (num < 1 || num > 100000) { text2 = "@s_bad_amount"; } else if (!ItemRecord.PrefabExists(text)) { text2 = "@s_unknown_item"; } else if (IsBlocked(text)) { text2 = "@s_blocked"; } else if (num2 < 1 || num2 > Plugin.MaxPricePerUnit.Value) { text2 = "@s_bad_price|" + Plugin.MaxPricePerUnit.Value; } else if (num4 > int.MaxValue) { text2 = "@s_total_high"; } else if (num5 >= Plugin.MaxOrdersPerPlayer.Value) { text2 = "@s_order_limit|" + Plugin.MaxOrdersPerPlayer.Value; } else if (account.Balance < num4) { text2 = "@s_no_funds|" + num4 + "|" + account.Balance; } if (text2 != null) { FailWithDeposit(sender, pid, num3, text2); return; } account.Balance -= num4; BuyOrder buyOrder = new BuyOrder(); buyOrder.Id = _nextOrderId++; buyOrder.BuyerId = pid; buyOrder.BuyerName = name; buyOrder.Prefab = text; buyOrder.Remaining = num; buyOrder.Price = num2; buyOrder.CreatedTicks = DateTime.UtcNow.Ticks; Orders.Add(buyOrder); History(account, HistoryKind.OrderEscrow, -num4, 0L, text, num, "", ""); Save(); Audit(name, pid, "ORDER #" + buyOrder.Id + " want " + text + " x" + num + " @ " + num2 + " escrow " + num4 + " | balance " + account.Balance); Result(sender, true, "s_order_created", PrefabToken(text) + " x" + num, num2, num4); SendData(sender, pid); BroadcastChanged(); } public static void RPC_OrderFill(long sender, ZPackage pkg) { if (!IsServer) { return; } EnsureLoaded(); List<ItemRecord> list = new List<ItemRecord>(); long id; int num; try { id = pkg.ReadLong(); num = pkg.ReadInt(); int num2 = pkg.ReadInt(); for (int i = 0; i < num2; i++) { list.Add(ItemRecord.Read(pkg)); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Corrupted order fill packet from " + sender + ": " + ex.Message)); return; } if (!Resolve(sender, out var pid, out var name)) { SendItemsBack(sender, list); Result(sender, false, "s_unresolved_item"); return; } BuyOrder buyOrder = FindOrder(id); int num3 = 0; bool flag = false; foreach (ItemRecord item in list) { num3 += Math.Max(0, item.Stack); if (buyOrder != null && item.Prefab != buyOrder.Prefab) { flag = true; } } string text = null; if (buyOrder == null) { text = "@s_order_gone"; } else if (buyOrder.BuyerId == pid) { text = "@s_order_own"; } else if (buyOrder.Price != num) { text = "@s_price_changed"; } else if (flag || num3 < 1) { text = "@s_order_wrong_item"; } else if (num3 > buyOrder.Remaining) { text = "@s_amount_changed"; } Account account = Acc(pid, name); if (text != null) { account.Items.AddRange(list); DeliverItems(pid, sender); Result(sender, false, "s_create_failed", text); SendData(sender, pid); return; } long num4 = (long)num3 * (long)buyOrder.Price; long num5 = (long)Math.Floor((double)num4 * ((double)Plugin.FeePercent.Value / 100.0)); account.Balance += num4 - num5; History(account, HistoryKind.OrderSale, num4 - num5, num5, buyOrder.Prefab, num3, buyOrder.BuyerName, ""); Account account2 = Acc(buyOrder.BuyerId, buyOrder.BuyerName); account2.Items.AddRange(list); History(account2, HistoryKind.OrderFilled, 0L, 0L, buyOrder.Prefab, num3, name, ""); RecordTrade(buyOrder.Prefab, buyOrder.Price, num3); buyOrder.Remaining -= num3; buyOrder.Delivered += num3; if (buyOrder.Remaining <= 0) { Orders.Remove(buyOrder); } Save(); Audit(name, pid, "FILL ORDER #" + buyOrder.Id + " " + buyOrder.Prefab + " x" + num3 + " to " + buyOrder.BuyerName + " (" + buyOrder.BuyerId + ") for " + num4 + " fee " + num5 + " | seller balance " + account.Balance + " | order remaining " + buyOrder.Remaining); Result(sender, true, "s_order_sold", PrefabToken(buyOrder.Prefab) + " x" + num3, num4 - num5); SendData(sender, pid); Notify(buyOrder.BuyerId, true, "s_order_filled", name, PrefabToken(buyOrder.Prefab) + " x" + num3); BroadcastChanged(); } public static void RPC_OrderCancel(long sender, long id) { if (!IsServer) { return; } EnsureLoaded(); if (!Resolve(sender, out var pid, out var name)) { return; } BuyOrder buyOrder = FindOrder(id); bool flag = IsAdminSender(sender); if (buyOrder == null || (buyOrder.BuyerId != pid && !flag)) { Result(sender, false, "s_order_gone"); SendData(sender, pid); return; } long escrow = buyOrder.Escrow; Orders.Remove(buyOrder); Account account = Acc(buyOrder.BuyerId, buyOrder.BuyerName); account.Balance += escrow; History(account, HistoryKind.OrderRefund, escrow, 0L, buyOrder.Prefab, buyOrder.Remaining, "", ""); Save(); Audit(name, pid, "CANCEL ORDER #" + buyOrder.Id + ((buyOrder.BuyerId != pid) ? " (admin)" : "") + " refund " + escrow + " to " + buyOrder.BuyerName + " | balance " + account.Balance); Result(sender, true, "s_order_cancelled", PrefabToken(buyOrder.Prefab), escrow); SendData(sender, pid); if (buyOrder.BuyerId != pid) { Notify(buyOrder.BuyerId, false, "s_order_cancelled_admin", PrefabToken(buyOrder.Prefab), escrow); } BroadcastChanged(); } public static void RPC_BountyCreate(long sender, ZPackage pkg) { if (!IsServer) { return; } EnsureLoaded(); string text; string text2; int num; int num2; try { text = (pkg.ReadString() ?? "").Trim(); text2 = (pkg.ReadString() ?? "").Trim(); num = pkg.ReadInt(); num2 = Math.Max(0, pkg.ReadInt()); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Corrupted bounty packet from " + sender + ": " + ex.Message)); return; } if (!Resolve(sender, out var pid, out var name)) { if (num2 > 0) { SendBack(sender, num2, null); } Result(sender, false, "s_unresolved_coins"); return; } Account account = Acc(pid, name); ApplyDeposit(account, pid, name, num2, "for bounty"); int num3 = 0; foreach (Bounty bounty2 in Bounties) { if (bounty2.OwnerId == pid) { num3++; } } string text3 = null; if (text.Length == 0) { text3 = "@s_bounty_no_title"; } else if (text.Length > 60 || text2.Length > 400) { text3 = "@s_bounty_too_long"; } else if (num < 1 || num > 10000000) { text3 = "@s_bounty_bad_reward|" + 10000000; } else if (num3 >= Plugin.MaxBountiesPerPlayer.Value) { text3 = "@s_bounty_limit|" + Plugin.MaxBountiesPerPlayer.Value; } else if (account.Balance < num) { text3 = "@s_no_funds|" + num + "|" + account.Balance; } if (text3 != null) { FailWithDeposit(sender, pid, num2, text3); return; } account.Balance -= num; Bounty bounty = new Bounty(); bounty.Id = _nextBountyId++; bounty.OwnerId = pid; bounty.OwnerName = name; bounty.Title = text; bounty.Description = text2; bounty.Reward = num; bounty.CreatedTicks = DateTime.UtcNow.Ticks; bounty.StateTicks = bounty.CreatedTicks; bounty.State = BountyState.Open; Bounties.Add(bounty); History(account, HistoryKind.BountyEscrow, -num, 0L, "", 0, "", text); Save(); Audit(name, pid, "BOUNTY #" + bounty.Id + " \"" + text + "\" reward " + num + " | balance " + account.Balance); Result(sender, true, "s_bounty_created", text, num); SendData(sender, pid); BroadcastChanged(); } public static void RPC_BountyAction(long sender, ZPackage pkg) { if (!IsServer) { return; } EnsureLoaded(); long id; BountyAction bountyAction; try { id = pkg.ReadLong(); bountyAction = (BountyAction)pkg.ReadInt(); } catch { return; } if (!Resolve(sender, out var pid, out var name)) { return; } bool flag = IsAdminSender(sender); Bounty bounty = FindBounty(id); if (bounty == null) { Result(sender, false, "s_bounty_gone"); SendData(sender, pid); return; } bool flag2 = bounty.OwnerId == pid; bool flag3 = bounty.State != BountyState.Open && bounty.ContractorId == pid; long ticks = DateTime.UtcNow.Ticks; bool flag4 = Plugin.BountyClaimTimeoutHours.Value > 0 && ticks - bounty.StateTicks >= TimeSpan.FromHours(Plugin.BountyClaimTimeoutHours.Value).Ticks; switch (bountyAction) { case BountyAction.Claim: if (bounty.State == BountyState.Open && !flag2) { bounty.State = BountyState.Claimed; bounty.ContractorId = pid; bounty.ContractorName = name; bounty.StateTicks = ticks; Save(); Audit(name, pid, "CLAIM BOUNTY #" + bounty.Id + " \"" + bounty.Title + "\""); Result(sender, true, "s_bounty_claimed", bounty.Title); Notify(bounty.OwnerId, true, "s_bounty_claimed_owner", name, bounty.Title); SendData(sender, pid); BroadcastChanged(); return; } break; case BountyAction.Abandon: if (flag3) { Audit(name, pid, "ABANDON BOUNTY #" + bounty.Id); bounty.State = BountyState.Open; bounty.ContractorId = 0L; bounty.ContractorName = ""; bounty.StateTicks = ticks; Save(); Result(sender, true, "s_bounty_abandoned", bounty.Title); Notify(bounty.OwnerId, false, "s_bounty_abandoned_owner", name, bounty.Title); SendData(sender, pid); BroadcastChanged(); return; } break; case BountyAction.Submit: if (flag3 && bounty.State == BountyState.Claimed) { bounty.State = BountyState.Submitted; bounty.StateTicks = ticks; Save(); Audit(name, pid, "SUBMIT BOUNTY #" + bounty.Id); Result(sender, true, "s_bounty_submitted", bounty.Title); Notify(bounty.OwnerId, true, "s_bounty_submitted_owner", name, bounty.Title); SendData(sender, pid); BroadcastChanged(); return; } break; case BountyAction.Confirm: if (bounty.State != BountyState.Open && (flag2 || flag)) { long num = (long)Math.Floor((double)bounty.Reward * ((double)Plugin.FeePercent.Value / 100.0)); Account account2 = Acc(bounty.ContractorId, bounty.ContractorName); account2.Balance += bounty.Reward - num; History(account2, HistoryKind.BountyReward, bounty.Reward - num, num, "", 0, bounty.OwnerName, bounty.Title); History(Acc(bounty.OwnerId, bounty.OwnerName), HistoryKind.BountyPaid, 0L, 0L, "", 0, bounty.ContractorName, bounty.Title); Bounties.Remove(bounty); Save(); Audit(name, pid, "CONFIRM BOUNTY #" + bounty.Id + (flag2 ? "" : " (admin)") + " paid " + (bounty.Reward - num) + " fee " + num + " to " + bounty.ContractorName + " (" + bounty.ContractorId + ") | contractor balance " + account2.Balance); Result(sender, true, "s_bounty_confirmed", bounty.Title, bounty.ContractorName); Notify(bounty.ContractorId, true, "s_bounty_paid", bounty.OwnerName, bounty.Title, bounty.Reward - num); if (!flag2) { Notify(bounty.OwnerId, true, "s_bounty_confirmed_admin", bounty.Title); } SendData(sender, pid); BroadcastChanged(); return; } break; case BountyAction.Reject: if (bounty.State == BountyState.Submitted && flag2) { bounty.State = BountyState.Claimed; bounty.StateTicks = ticks; Save(); Audit(name, pid, "REJECT BOUNTY #" + bounty.Id + " submitted by " + bounty.ContractorName); Result(sender, true, "s_bounty_rejected", bounty.Title); Notify(bounty.ContractorId, false, "s_bounty_rejected_contractor", bounty.OwnerName, bounty.Title); SendData(sender, pid); BroadcastChanged(); return; } break; case BountyAction.Cancel: if (flag || (flag2 && bounty.State == BountyState.Open) || (flag2 && bounty.State == BountyState.Claimed && flag4)) { Account account = Acc(bounty.OwnerId, bounty.OwnerName); account.Balance += bounty.Reward; History(account, HistoryKind.BountyRefund, bounty.Reward, 0L, "", 0, "", bounty.Title); Bounties.Remove(bounty); Save(); Audit(name, pid, "CANCEL BOUNTY #" + bounty.Id + (flag2 ? "" : " (admin)") + " refund " + bounty.Reward + " to " + bounty.OwnerName + " | balance " + account.Balance); Result(sender, true, "s_bounty_cancelled", bounty.Title, bounty.Reward); if (bounty.ContractorId != 0) { Notify(bounty.ContractorId, false, "s_bounty_cancelled_contractor", bounty.Title); } if (!flag2) { Notify(bounty.OwnerId, false, "s_bounty_cancelled_admin", bounty.Title, bounty.Reward); } SendData(sender, pid); BroadcastChanged(); return; } break; } Result(sender, false, "s_bounty_not_allowed"); SendData(sender, pid); } private static bool ExpireOrders(long limit) { bool result = false; for (int num = Orders.Count - 1; num >= 0; num--) { BuyOrder buyOrder = Orders[num]; if (buyOrder.CreatedTicks <= limit) { Account account = Acc(buyOrder.BuyerId, buyOrder.BuyerName); long escrow = buyOrder.Escrow; account.Balance += escrow; History(account, HistoryKind.OrderRefund, escrow, 0L, buyOrder.Prefab, buyOrder.Remaining, "", ""); Orders.RemoveAt(num); result = true; Audit(buyOrder.BuyerName, buyOrder.BuyerId, "EXPIRE ORDER #" + buyOrder.Id + " refund " + escrow + " | balance " + account.Balance); Notify(buyOrder.BuyerId, false, "s_order_expired", PrefabToken(buyOrder.Prefab), escrow); } } for (int num2 = Bounties.Count - 1; num2 >= 0; num2--) { Bounty bounty = Bounties[num2]; if (bounty.State == BountyState.Open && bounty.StateTicks <= limit) { Account account2 = Acc(bounty.OwnerId, bounty.OwnerName); account2.Balance += bounty.Reward; History(account2, HistoryKind.BountyRefund, bounty.Reward, 0L, "", 0, "", bounty.Title); Bounties.RemoveAt(num2); result = true; Audit(bounty.OwnerName, bounty.OwnerId, "EXPIRE BOUNTY #" + bounty.Id + " refund " + bounty.Reward + " | balance " + account2.Balance); Notify(bounty.OwnerId, false, "s_bounty_expired", bounty.Title, bounty.Reward); } } return result; } private static void ClearPrices() { Trades.Clear(); } private static void RecordTrade(string prefab, int price, int qty) { if (!string.IsNullOrEmpty(prefab) && price >= 1 && qty >= 1) { if (!Trades.TryGetValue(prefab, out var value)) { value = new List<Trade>(); Trades[prefab] = value; } Trade trade = new Trade(); trade.Ticks = DateTime.UtcNow.Ticks; trade.Price = price; trade.Qty = qty; value.In