diff --git a/bukkit/src/main/java/com/blank038/servermarket/api/entity/MarketData.java b/bukkit/src/main/java/com/blank038/servermarket/api/entity/MarketData.java index 47dfcea..4071656 100644 --- a/bukkit/src/main/java/com/blank038/servermarket/api/entity/MarketData.java +++ b/bukkit/src/main/java/com/blank038/servermarket/api/entity/MarketData.java @@ -14,6 +14,7 @@ import com.blank038.servermarket.api.handler.filter.interfaces.IFilter; import com.blank038.servermarket.internal.gui.impl.MarketGui; import com.blank038.servermarket.internal.i18n.I18n; +import com.blank038.servermarket.internal.provider.ActionProvider; import com.blank038.servermarket.internal.util.TextUtil; import lombok.Getter; import lombok.Setter; @@ -22,6 +23,7 @@ import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; import java.io.File; import java.text.DecimalFormat; @@ -116,8 +118,8 @@ public MarketData(File file) { /** * 获取玩家在权限节点上的值 * - * @param section target section - * @param player target player + * @param section target section + * @param player target player * @param compareBig is bigger than * @return 最终值 */ @@ -136,6 +138,19 @@ public double getPermsValueForPlayer(ConfigurationSection section, Player player return tax; } + public void action(Player player, String uuid, ClickType clickType, int page, FilterHandler filter) { + if (!ServerMarket.getStorageHandler().hasSale(this.sourceId, uuid)) { + player.sendMessage(I18n.getStrAndHeader("error-sale")); + return; + } + Optional optionalSaleItem = ServerMarket.getStorageHandler().getSaleItem(this.sourceId, uuid); + if (optionalSaleItem.isPresent()) { + ActionProvider.runAction(this, player, uuid, optionalSaleItem.get(), clickType, page, filter); + } else { + player.sendMessage(I18n.getStrAndHeader("error-sale")); + } + } + public void tryBuySale(Player buyer, String uuid, boolean shift, int page, FilterHandler filter) { // 判断商品是否存在 if (!ServerMarket.getStorageHandler().hasSale(this.sourceId, uuid)) { @@ -145,59 +160,8 @@ public void tryBuySale(Player buyer, String uuid, boolean shift, int page, Filte Optional optionalSaleItem = ServerMarket.getStorageHandler().getSaleItem(this.sourceId, uuid); if (optionalSaleItem.isPresent()) { SaleCache saleItem = optionalSaleItem.get(); - if (saleItem.getOwnerUUID().equals(buyer.getUniqueId().toString())) { - if (shift) { - ServerMarket.getStorageHandler().removeSaleItem(this.sourceId, uuid) - .ifPresent((sale) -> { - ServerMarket.getStorageHandler().addItemToStore(buyer.getUniqueId(), sale.getSaleItem(), "unsale"); - buyer.sendMessage(I18n.getStrAndHeader("unsale")); - new MarketGui(this.marketKey, page, filter).openGui(buyer); - }); - } else { - buyer.sendMessage(I18n.getStrAndHeader("shift-unsale")); - } - return; - } if (shift) { - if (saleItem.getPrice() == 0) { - buyer.sendMessage(I18n.getStrAndHeader("error-sale")); - return; - } - if (BaseEconomy.getEconomyBridge(this.paymentType).balance(buyer, this.economyType) < saleItem.getPrice()) { - buyer.sendMessage(I18n.getStrAndHeader("lack-money") - .replace("%economy%", this.economyName)); - return; - } - Optional optional = ServerMarket.getStorageHandler().removeSaleItem(this.sourceId, uuid); - if (optional.isPresent()) { - saleItem = optional.get(); - BaseEconomy.getEconomyBridge(this.paymentType).take(buyer, this.economyType, saleItem.getPrice()); - Player seller = Bukkit.getPlayer(UUID.fromString(saleItem.getOwnerUUID())); - if (seller != null && seller.isOnline()) { - double tax = saleItem.getPrice() * this.getPermsValueForPlayer(this.getTaxSection(), seller, false); - double last = saleItem.getPrice() - tax; - DecimalFormat df = new DecimalFormat("#0.00"); - BaseEconomy.getEconomyBridge(this.paymentType).give(seller, this.economyType, last); - seller.sendMessage(I18n.getStrAndHeader("sale-sell") - .replace("%economy%", this.economyName) - .replace("%money%", df.format(saleItem.getPrice())) - .replace("%last%", df.format(last))); - // send taxes - ServerMarketApi.sendTaxes(paymentType, this.economyType, tax); - } else { - ServerMarketApi.addOfflineTransaction(saleItem.getOwnerUUID(), this.paymentType, this.economyType, saleItem.getPrice(), this.marketKey); - } - // 给予购买者物品 - ServerMarket.getStorageHandler().addItemToStore(buyer.getUniqueId(), saleItem, "buy"); - // call PlayerSaleEvent.Buy - PlayerSaleEvent.Buy event = new PlayerSaleEvent.Buy(buyer, this, saleItem); - Bukkit.getPluginManager().callEvent(event); - // 发送购买消息至购买者 - buyer.sendMessage(I18n.getStrAndHeader("buy-item")); - new MarketGui(this.marketKey, page, filter).openGui(buyer); - } else { - buyer.sendMessage(I18n.getStrAndHeader("error-sale")); - } + } else { buyer.sendMessage(I18n.getStrAndHeader("shift-buy")); } diff --git a/bukkit/src/main/java/com/blank038/servermarket/internal/data/DataContainer.java b/bukkit/src/main/java/com/blank038/servermarket/internal/data/DataContainer.java index a281237..cbf3f9d 100644 --- a/bukkit/src/main/java/com/blank038/servermarket/internal/data/DataContainer.java +++ b/bukkit/src/main/java/com/blank038/servermarket/internal/data/DataContainer.java @@ -1,6 +1,7 @@ package com.blank038.servermarket.internal.data; import com.blank038.servermarket.api.handler.sort.SortHandler; +import com.blank038.servermarket.internal.enums.ActionType; import com.blank038.servermarket.internal.plugin.ServerMarket; import com.blank038.servermarket.internal.command.virtual.VirtualMarketCommand; import com.blank038.servermarket.api.entity.MarketData; @@ -15,6 +16,7 @@ import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; import org.bukkit.plugin.SimplePluginManager; import java.io.File; @@ -32,6 +34,7 @@ public class DataContainer { SORT_TYPE_DISPLAY_NAME = new HashMap<>(); public static final Map MARKET_DATA = new HashMap<>(); public static final Map SORT_HANDLER_MAP = new HashMap<>(); + public static final Map ACTION_TYPE_MAP = new HashMap<>(); public static void loadData() { ServerMarket.getInstance().saveResource("types.yml", "types.yml", false, (file) -> { @@ -71,6 +74,27 @@ public static void loadData() { Arrays.stream(file.listFiles()).iterator().forEachRemaining(MarketData::new); // Register virtual command DataContainer.registerVirtualMarketCommands(); + // load actions + DataContainer.loadActions(); + } + + /** + * Load the market actions. + */ + private static void loadActions() { + ServerMarket.getInstance().saveResource("action/market.yml", "action/market.yml", false, (file) -> { + ACTION_TYPE_MAP.clear(); + FileConfiguration data = YamlConfiguration.loadConfiguration(file); + data.getKeys(false).forEach((key) -> { + try { + ClickType type = ClickType.valueOf(key.toUpperCase()); + ActionType action = ActionType.valueOf(data.getString(key).toUpperCase()); + ACTION_TYPE_MAP.put(type, action); + } catch (Exception e) { + ServerMarket.getInstance().getLogger().log(Level.WARNING, e, () -> "Failed to load market action: " + key); + } + }); + }); } private static void registerVirtualMarketCommands() { @@ -125,4 +149,8 @@ public static void setSaleTypes(SaleCache saleItem) { saleItem.setSaleTypes(types); } } + + public static boolean isLegacy() { + return !MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_13_R1); + } } diff --git a/bukkit/src/main/java/com/blank038/servermarket/internal/enums/ActionType.java b/bukkit/src/main/java/com/blank038/servermarket/internal/enums/ActionType.java new file mode 100644 index 0000000..8b71f69 --- /dev/null +++ b/bukkit/src/main/java/com/blank038/servermarket/internal/enums/ActionType.java @@ -0,0 +1,101 @@ +package com.blank038.servermarket.internal.enums; + +import com.blank038.servermarket.api.ServerMarketApi; +import com.blank038.servermarket.api.entity.MarketData; +import com.blank038.servermarket.api.event.PlayerSaleEvent; +import com.blank038.servermarket.api.handler.filter.FilterHandler; +import com.blank038.servermarket.internal.cache.sale.SaleCache; +import com.blank038.servermarket.internal.economy.BaseEconomy; +import com.blank038.servermarket.internal.gui.impl.ConfirmPurchaseGui; +import com.blank038.servermarket.internal.gui.impl.MarketGui; +import com.blank038.servermarket.internal.i18n.I18n; +import com.blank038.servermarket.internal.plugin.ServerMarket; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.text.DecimalFormat; +import java.util.Optional; +import java.util.UUID; + +public enum ActionType { + PURCHASE((marketData, buyer, uuid, saleCache, page, filter) -> { + if (saleCache.getOwnerUUID().equals(buyer.getUniqueId().toString())) { + buyer.sendMessage(I18n.getStrAndHeader("is-owner")); + return; + } + if (saleCache.getPrice() == 0) { + buyer.sendMessage(I18n.getStrAndHeader("error-sale")); + return; + } + if (BaseEconomy.getEconomyBridge(marketData.getPaymentType()).balance(buyer, marketData.getEconomyType()) < saleCache.getPrice()) { + buyer.sendMessage(I18n.getStrAndHeader("lack-money").replace("%economy%", marketData.getEconomyName())); + return; + } + Optional optional = ServerMarket.getStorageHandler().removeSaleItem(marketData.getSourceId(), uuid); + if (optional.isPresent()) { + SaleCache saleItem = optional.get(); + BaseEconomy.getEconomyBridge(marketData.getPaymentType()).take(buyer, marketData.getEconomyType(), saleItem.getPrice()); + Player seller = Bukkit.getPlayer(UUID.fromString(saleItem.getOwnerUUID())); + if (seller != null && seller.isOnline()) { + double tax = saleItem.getPrice() * marketData.getPermsValueForPlayer(marketData.getTaxSection(), seller, false); + double last = saleItem.getPrice() - tax; + DecimalFormat df = new DecimalFormat("#0.00"); + BaseEconomy.getEconomyBridge(marketData.getPaymentType()).give(seller, marketData.getEconomyType(), last); + seller.sendMessage(I18n.getStrAndHeader("sale-sell") + .replace("%economy%", marketData.getEconomyName()) + .replace("%money%", df.format(saleItem.getPrice())) + .replace("%last%", df.format(last))); + // send taxes + ServerMarketApi.sendTaxes(marketData.getPaymentType(), marketData.getEconomyName(), tax); + } else { + ServerMarketApi.addOfflineTransaction(saleItem.getOwnerUUID(), marketData.getPaymentType(), + marketData.getEconomyType(), saleItem.getPrice(), marketData.getMarketKey()); + } + // give sale item to buyer + ServerMarket.getStorageHandler().addItemToStore(buyer.getUniqueId(), saleItem, "buy"); + // call PlayerSaleEvent.Buy + PlayerSaleEvent.Buy event = new PlayerSaleEvent.Buy(buyer, marketData, saleItem); + Bukkit.getPluginManager().callEvent(event); + // send message to buyer + buyer.sendMessage(I18n.getStrAndHeader("buy-item")); + new MarketGui(marketData.getMarketKey(), page, filter).openGui(buyer); + } else { + buyer.sendMessage(I18n.getStrAndHeader("error-sale")); + } + }), + UNSALE((marketData, buyer, uuid, saleCache, page, filter) -> { + if (saleCache.getOwnerUUID().equals(buyer.getUniqueId().toString())) { + ServerMarket.getStorageHandler().removeSaleItem(marketData.getSourceId(), uuid) + .ifPresent((sale) -> { + ServerMarket.getStorageHandler().addItemToStore(buyer.getUniqueId(), sale.getSaleItem(), "unsale"); + buyer.sendMessage(I18n.getStrAndHeader("unsale")); + new MarketGui(marketData.getMarketKey(), page, filter).openGui(buyer); + }); + } else { + buyer.sendMessage(I18n.getStrAndHeader("not-owner")); + } + }), + CONFIRM_PURCHASE((marketData, buyer, uuid, saleCache, page, filter) -> { + if (saleCache.getOwnerUUID().equals(buyer.getUniqueId().toString())) { + buyer.sendMessage(I18n.getStrAndHeader("is-owner")); + return; + } + new ConfirmPurchaseGui().open(marketData, buyer, uuid, saleCache, page, filter); + }); + + private final ActionConsumer consumer; + + ActionType(ActionConsumer consumer) { + this.consumer = consumer; + } + + public void run(MarketData marketData, Player player, String uuid, SaleCache saleCache, int page, FilterHandler filter) { + consumer.run(marketData, player, uuid, saleCache, page, filter); + } + + @FunctionalInterface + interface ActionConsumer { + + void run(MarketData marketData, Player player, String uuid, SaleCache saleCache, int page, FilterHandler filter); + } +} diff --git a/bukkit/src/main/java/com/blank038/servermarket/internal/gui/AbstractGui.java b/bukkit/src/main/java/com/blank038/servermarket/internal/gui/AbstractGui.java index 56d5498..5632af1 100644 --- a/bukkit/src/main/java/com/blank038/servermarket/internal/gui/AbstractGui.java +++ b/bukkit/src/main/java/com/blank038/servermarket/internal/gui/AbstractGui.java @@ -2,7 +2,6 @@ import com.blank038.servermarket.api.ServerMarketApi; import com.blank038.servermarket.internal.plugin.ServerMarket; -import org.bukkit.Bukkit; import java.util.HashMap; import java.util.Map; diff --git a/bukkit/src/main/java/com/blank038/servermarket/internal/gui/impl/ConfirmPurchaseGui.java b/bukkit/src/main/java/com/blank038/servermarket/internal/gui/impl/ConfirmPurchaseGui.java new file mode 100644 index 0000000..6fb058c --- /dev/null +++ b/bukkit/src/main/java/com/blank038/servermarket/internal/gui/impl/ConfirmPurchaseGui.java @@ -0,0 +1,100 @@ +package com.blank038.servermarket.internal.gui.impl; + +import com.aystudio.core.bukkit.util.common.CommonUtil; +import com.aystudio.core.bukkit.util.inventory.GuiModel; +import com.blank038.servermarket.api.entity.MarketData; +import com.blank038.servermarket.api.handler.filter.FilterHandler; +import com.blank038.servermarket.internal.cache.sale.SaleCache; +import com.blank038.servermarket.internal.data.DataContainer; +import com.blank038.servermarket.internal.enums.ActionType; +import com.blank038.servermarket.internal.gui.AbstractGui; +import com.blank038.servermarket.internal.i18n.I18n; +import com.blank038.servermarket.internal.plugin.ServerMarket; +import com.blank038.servermarket.internal.util.ItemUtil; +import com.blank038.servermarket.internal.util.TextUtil; +import de.tr7zw.nbtapi.NBTItem; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Blank038 + */ +public class ConfirmPurchaseGui extends AbstractGui { + + public void open(MarketData marketData, Player player, String uuid, SaleCache saleCache, int page, FilterHandler filter) { + String key = DataContainer.isLegacy() ? "gui/confirm_purchase_legacy.yml" : "gui/confirm_purchase.yml"; + ServerMarket.getInstance().saveResource(key, "gui/confirm_purchase.yml", false, (file) -> { + FileConfiguration data = YamlConfiguration.loadConfiguration(file); + GuiModel model = new GuiModel(data.getString("title"), data.getInt("size")); + + this.initializeDisplayItem(model, data); + model.setItem(data.getInt("item-slot"), saleCache.getSaleItem().clone()); + + model.registerListener(ServerMarket.getInstance()); + model.execute((e) -> { + e.setCancelled(true); + if (e.getClickedInventory() != e.getInventory()) { + return; + } + ItemStack itemStack = e.getCurrentItem(); + if (itemStack == null || itemStack.getType() == Material.AIR) { + return; + } + Player clicker = (Player) e.getWhoClicked(); + if (this.isCooldown(clicker.getUniqueId())) { + clicker.sendMessage(I18n.getStrAndHeader("cooldown")); + return; + } + NBTItem nbtItem = new NBTItem(itemStack); + if (!nbtItem.hasTag("ConfirmAction")) { + return; + } + switch (nbtItem.getString("ConfirmAction")) { + case "confirm": + ActionType.PURCHASE.run(marketData, clicker, uuid, saleCache, page, filter); + break; + case "cancel": + new MarketGui(marketData.getMarketKey(), page, filter).openGui(clicker); + break; + default: + break; + } + }); + model.openInventory(player); + }); + } + + private void initializeDisplayItem(GuiModel model, FileConfiguration data) { + if (data.contains("items")) { + for (String key : data.getConfigurationSection("items").getKeys(false)) { + ConfigurationSection section = data.getConfigurationSection("items." + key); + ItemStack itemStack = ItemUtil.generateItem(section.getString("type"), + section.getInt("amount"), + (short) section.getInt("data"), + section.getInt("customModel", -1)); + ItemMeta itemMeta = itemStack.getItemMeta(); + itemMeta.setDisplayName(TextUtil.formatHexColor(section.getString("name"))); + List list = new ArrayList<>(section.getStringList("lore")); + list.replaceAll(TextUtil::formatHexColor); + itemMeta.setLore(list); + itemStack.setItemMeta(itemMeta); + if (section.contains("action")) { + NBTItem nbtItem = new NBTItem(itemStack); + nbtItem.setString("ConfirmAction", section.getString("action")); + itemStack = nbtItem.getItem(); + } + for (int i : CommonUtil.formatSlots(section.getString("slot"))) { + model.setItem(i, itemStack); + } + } + } + } +} diff --git a/bukkit/src/main/java/com/blank038/servermarket/internal/gui/impl/MarketGui.java b/bukkit/src/main/java/com/blank038/servermarket/internal/gui/impl/MarketGui.java index 7f84474..4fe0ddd 100644 --- a/bukkit/src/main/java/com/blank038/servermarket/internal/gui/impl/MarketGui.java +++ b/bukkit/src/main/java/com/blank038/servermarket/internal/gui/impl/MarketGui.java @@ -98,11 +98,7 @@ public void openGui(Player player) { break; } SaleCache saleItem = saleList.get(i); - if (saleItem == null) { - --index; - continue; - } - if ((filter != null && !filter.check(saleItem))) { + if (saleItem == null || (filter != null && !filter.check(saleItem))) { --index; continue; } @@ -124,10 +120,8 @@ public void openGui(Player player) { NBTItem nbtItem = new NBTItem(itemStack); String key = nbtItem.getString("SaleUUID"), action = nbtItem.getString("MarketAction"); if (key != null && !key.isEmpty()) { - // 购买商品 - DataContainer.MARKET_DATA.get(this.sourceMarketKey).tryBuySale(clicker, key, e.isShiftClick(), lastPage, filter); + DataContainer.MARKET_DATA.get(this.sourceMarketKey).action(clicker, key, e.getClick(), lastPage, filter); } else if (action != null && !action.isEmpty()) { - // 判断交互方式 switch (action) { case "up": if (lastPage == 1) { diff --git a/bukkit/src/main/java/com/blank038/servermarket/internal/provider/ActionProvider.java b/bukkit/src/main/java/com/blank038/servermarket/internal/provider/ActionProvider.java new file mode 100644 index 0000000..8d15cbb --- /dev/null +++ b/bukkit/src/main/java/com/blank038/servermarket/internal/provider/ActionProvider.java @@ -0,0 +1,21 @@ +package com.blank038.servermarket.internal.provider; + +import com.blank038.servermarket.api.entity.MarketData; +import com.blank038.servermarket.api.handler.filter.FilterHandler; +import com.blank038.servermarket.internal.cache.sale.SaleCache; +import com.blank038.servermarket.internal.data.DataContainer; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; + +/** + * @author Blank038 + */ +public class ActionProvider { + + public static void runAction(MarketData marketData, Player player, String uuid, SaleCache saleCache, ClickType clickType, int page, FilterHandler filter) { + if (!DataContainer.ACTION_TYPE_MAP.containsKey(clickType)) { + return; + } + DataContainer.ACTION_TYPE_MAP.get(clickType).run(marketData, player, uuid, saleCache, page, filter); + } +} diff --git a/bukkit/src/main/resources/action/market.yml b/bukkit/src/main/resources/action/market.yml new file mode 100644 index 0000000..aa8c45f --- /dev/null +++ b/bukkit/src/main/resources/action/market.yml @@ -0,0 +1,4 @@ +#right: purchase +left: purchase +shift_right: unsale +shift_left: confirm_purchase \ No newline at end of file diff --git a/bukkit/src/main/resources/gui/confirm_purchase.yml b/bukkit/src/main/resources/gui/confirm_purchase.yml new file mode 100644 index 0000000..f9bcd8f --- /dev/null +++ b/bukkit/src/main/resources/gui/confirm_purchase.yml @@ -0,0 +1,19 @@ +title: "&8商品购买确认" +size: 27 +item-slot: 13 +items: + confirm: + type: GREEN_STAINED_GLASS_PANE + amount: 1 + #custom-data: 0 + action: confirm + name: "&a确认" + slot: 10 + lore: [ ] + cancel: + type: RED_STAINED_GLASS_PANE + amount: 1 + action: cancel + name: "&c取消" + slot: 16 + lore: [ ] \ No newline at end of file diff --git a/bukkit/src/main/resources/gui/confirm_purchase_legacy.yml b/bukkit/src/main/resources/gui/confirm_purchase_legacy.yml new file mode 100644 index 0000000..18cd536 --- /dev/null +++ b/bukkit/src/main/resources/gui/confirm_purchase_legacy.yml @@ -0,0 +1,20 @@ +title: "&8商品购买确认" +size: 27 +item-slot: 13 +items: + confirm: + type: STAINED_GLASS_PANE + amount: 1 + data: 5 + action: confirm + name: "&a确认" + slot: 10 + lore: [ ] + cancel: + type: STAINED_GLASS_PANE + amount: 1 + data: 14 + action: cancel + name: "&c取消" + slot: 16 + lore: [ ] \ No newline at end of file