private void PerformTrade(TSPlayer player, ProtectionEntry protection, Inventory chestInventory, Item sellItem, Item payItem)
        {
            Inventory playerInventory = new Inventory(new PlayerItemsAdapter(player.Index, player.TPlayer.inventory, 0, 53), specificPrefixes: false);

              ItemData sellItemData = ItemData.FromItem(sellItem);
              ItemData payItemData = ItemData.FromItem(payItem);
              ItemData?[] playerInvUpdates;
              try {
            playerInvUpdates = playerInventory.Remove(payItemData);
            playerInventory.Add(playerInvUpdates, sellItemData);
              } catch (InvalidOperationException) {
            player.SendErrorMessage($"You either don't have the needed {TShock.Utils.ItemTag(payItem)} to purchase {TShock.Utils.ItemTag(sellItem)} or your inventory is full.");
            return;
              }

              bool isRefillChest = (protection.RefillChestData != null);
              ItemData?[] chestInvUpdates;
              try {
            if (isRefillChest) {
              chestInvUpdates = chestInventory.Add(payItemData);
            } else {
              chestInvUpdates = chestInventory.Remove(sellItemData);
              chestInventory.Add(chestInvUpdates, payItemData);
            }
              } catch (InvalidOperationException) {
            player.SendErrorMessage("The items in the trade chest are either sold out or there's no space in it to add your payment.");
            return;
              }

              try {
            protection.TradeChestData.AddOrUpdateLooter(player.User.ID);
              } catch (InvalidOperationException) {
            player.SendErrorMessage($"The vendor doesn't allow more than {protection.TradeChestData.LootLimitPerPlayer} purchases per player.");
            return;
              }

              playerInventory.ApplyUpdates(playerInvUpdates);
              chestInventory.ApplyUpdates(chestInvUpdates);

              protection.TradeChestData.AddJournalEntry(player.Name, sellItem, payItem);
              player.SendSuccessMessage($"You've just purchased {TShock.Utils.ItemTag(sellItem)} for {TShock.Utils.ItemTag(payItem)} from {TShock.Utils.ColorTag(GetUserName(protection.Owner), Color.Red)}.");
        }
        private void InitTrade(TSPlayer player, IChest chest, ProtectionEntry protection)
        {
            TradeChestMetadata tradeChestData = protection.TradeChestData;
              Item sellItem = new Item();
              sellItem.netDefaults(tradeChestData.ItemToSellId);
              sellItem.stack = tradeChestData.ItemToSellAmount;
              Item payItem = new Item();
              payItem.netDefaults(tradeChestData.ItemToPayId);
              payItem.stack = tradeChestData.ItemToPayAmount;

              player.SendMessage($"This is a trade chest owned by {TShock.Utils.ColorTag(GetUserName(protection.Owner), Color.Red)}.", Color.LightGray);

              Inventory chestInventory = new Inventory(chest.Items, specificPrefixes: false);
              int stock = chestInventory.Amount(sellItem.netID);
              if (stock < sellItem.stack) {
            player.SendMessage($"It was trading {TShock.Utils.ItemTag(sellItem)} for {TShock.Utils.ItemTag(payItem)} but it is out of stock.", Color.LightGray);
            return;
              }

              player.SendMessage($"Click again to purchase {TShock.Utils.ItemTag(sellItem)} for {TShock.Utils.ItemTag(payItem)}", Color.LightGray);

              CommandInteraction interaction = this.StartOrResetCommandInteraction(player);

              interaction.ChestOpenCallback += (playerLocal, chestLocation) => {
            bool complete;

            bool wasThisChestHit = (chestLocation == chest.Location);
            if (wasThisChestHit) {
              // this is important to check, otherwise players could use trade chests to easily duplicate items
              if (!this.IsChestInUse(playerLocal, chest))
            this.PerformTrade(player, protection, chestInventory, sellItem, payItem);
              else
            player.SendErrorMessage("Another player is currently viewing the content of this chest.");

              complete = false;
            } else {
              this.HandleChestGetContents(playerLocal, chestLocation, skipInteractions: true);
              complete = true;
            }

            playerLocal.SendTileSquare(chest.Location);
            return new CommandInteractionResult {IsHandled = true, IsInteractionCompleted = complete};
              };
        }