From 717f1b9a1a7fe430975a06d667c5187d75d74b45 Mon Sep 17 00:00:00 2001 From: adrian Date: Fri, 2 Jan 2026 20:55:50 -0300 Subject: [PATCH] Bumped version to `1.2`. Added `/balance` and `/pay` commands and a config option to enable them. Added safeIs method to Config to match values without case sensitivity. Added getCurrencyText method to Config to generate a formatted string from a currency amount. Value 'format' from config file now supports amount-name replacement. Main now stores the instance of VaultLayer for further use. --- README.md | 5 +- .../github/adrianvic/itemeconomy/Config.java | 14 +- .../io/github/adrianvic/itemeconomy/Main.java | 176 +++++++++--------- .../adrianvic/itemeconomy/VaultLayer.java | 2 +- .../itemeconomy/commands/Balance.java | 49 +++++ .../adrianvic/itemeconomy/commands/Pay.java | 74 ++++++++ src/main/resources/config.yml | 3 +- src/main/resources/plugin.yml | 31 ++- 8 files changed, 261 insertions(+), 93 deletions(-) create mode 100644 src/main/java/io/github/adrianvic/itemeconomy/commands/Balance.java create mode 100644 src/main/java/io/github/adrianvic/itemeconomy/commands/Pay.java diff --git a/README.md b/README.md index 2da59ea..e0fae43 100644 --- a/README.md +++ b/README.md @@ -9,15 +9,18 @@ Features: - Simple logic: Just checks if the user has the item/how many when queried. - Customizable Formatting: Define how your currency is displayed, including singular and plural forms. - Ender Chest support: Items on Ender Chests are counted in the user balance. +- Built-int optional balance and pay commands with support for permissions. ## Configuration Example: ```yaml item: diamond # Define the item to be used as currency. singular: diamond # Singular form of the currency. plural: diamonds # Plural form of the currency. -format: "{}$" # Customize how the currency is displayed in messages. +format: "{} $" # {} will be replaced with the amount and $ either with singular or plural ender_chest: balance # Either none or balance +commands: true # Disabling this will disable /balance and /pay ``` + This configuration will use diamonds as the currency, displayed as {amount}$, e.g., "5 diamonds" or "1 diamond". ## Usage: diff --git a/src/main/java/io/github/adrianvic/itemeconomy/Config.java b/src/main/java/io/github/adrianvic/itemeconomy/Config.java index f564ac4..4b57e6f 100644 --- a/src/main/java/io/github/adrianvic/itemeconomy/Config.java +++ b/src/main/java/io/github/adrianvic/itemeconomy/Config.java @@ -3,6 +3,7 @@ package io.github.adrianvic.itemeconomy; import org.bukkit.Material; import java.util.HashMap; +import java.util.Locale; import java.util.Map; public class Config { @@ -12,10 +13,11 @@ public class Config { public static void loadConfig(UnrealConfig conf) { uConf = conf; entries.put("item", "diamond"); - entries.put("format", "{}$"); + entries.put("format", "{} $"); entries.put("plural", "diamonds"); entries.put("singular", "diamond"); entries.put("ender_chest", "balance"); + entries.put("commands", "true"); Map missingValues = new HashMap<>(); @@ -44,6 +46,16 @@ public class Config { return entries.get(entry).equals(value); } + public static boolean safeIs(String entry, String value) { + return is(entry.toLowerCase(Locale.ROOT), value.toLowerCase(Locale.ROOT)); + } + + public static String getCurrencyText(int amount) { + return entries.get("format") + .replace("{}", String.valueOf(amount)) + .replace("$", (amount != 1) ? entries.get("plural") : entries.get("singular")); + } + public static UnrealConfig getuConf() { return uConf; } diff --git a/src/main/java/io/github/adrianvic/itemeconomy/Main.java b/src/main/java/io/github/adrianvic/itemeconomy/Main.java index 4ff0506..18dc263 100644 --- a/src/main/java/io/github/adrianvic/itemeconomy/Main.java +++ b/src/main/java/io/github/adrianvic/itemeconomy/Main.java @@ -2,6 +2,8 @@ package io.github.adrianvic.itemeconomy; import java.util.*; +import io.github.adrianvic.itemeconomy.commands.Balance; +import io.github.adrianvic.itemeconomy.commands.Pay; import io.github.adrianvic.itemeconomy.commands.Reload; import net.milkbowl.vault.economy.Economy; import org.bukkit.Bukkit; @@ -13,111 +15,117 @@ import org.bukkit.plugin.ServicePriority; import org.bukkit.plugin.java.JavaPlugin; public class Main extends JavaPlugin { - private static Main instance; + private static Main instance; + private static Economy economy; - public void onEnable() { - instance = this; - Config.loadConfig(new UnrealConfig(this, this.getDataFolder(), "config.yml")); - Bukkit.getServicesManager().register(Economy.class, new VaultLayer(), this, ServicePriority.High); - getCommand("itecoreload").setExecutor(new Reload()); - } + public void onEnable() { + instance = this; + Config.loadConfig(new UnrealConfig(this, this.getDataFolder(), "config.yml")); + economy = new VaultLayer(); + Bukkit.getServicesManager().register(Economy.class, economy, this, ServicePriority.High); - public static JavaPlugin getInstance() { - return instance; - } + getCommand("itecoreload").setExecutor(new Reload()); + if (Config.safeIs("commands", "true")) { + getCommand("balance").setExecutor(new Balance()); + getCommand("pay").setExecutor(new Pay()); + } + } - public void onDisable() { - super.onDisable(); - } + public void onDisable() { + super.onDisable(); + } - public enum InventoryID { - INVENTORY, - ENDER_CHEST - } + public static JavaPlugin getInstance() { + return instance; + } - public static Inventory getInventory(Player player, InventoryID inventory) { - Inventory inv = player.getInventory(); + public static Economy getEconomy() { + return economy; + } - switch (inventory) { - case INVENTORY -> { - inv = player.getInventory(); - } - case ENDER_CHEST -> { - if (Config.is("ender_chest", "balance")) { - inv = player.getEnderChest(); - } else { - inv = getInstance().getServer().createInventory(null, 9); + public enum InventoryID { + INVENTORY, + ENDER_CHEST + } + + public static Inventory getInventory(Player player, InventoryID inventory) { + Inventory inv = player.getInventory(); + + switch (inventory) { + case INVENTORY -> inv = player.getInventory(); + case ENDER_CHEST -> { + if (Config.is("ender_chest", "balance")) { + inv = player.getEnderChest(); + } else { + inv = getInstance().getServer().createInventory(null, 9); + } } - } - } + } - return inv; - } + return inv; + } - public static List getInventoryList(Player player, InventoryID inventory) { - Inventory inv = getInventory(player, inventory); - return Arrays.stream(inv.getContents()).map((o) -> o == null ? new ItemStack(Material.AIR) : o).toList(); - } + public static List getInventoryList(Player player, InventoryID inventory) { + Inventory inv = getInventory(player, inventory); + return Arrays.stream(inv.getContents()).map((o) -> o == null ? new ItemStack(Material.AIR) : o).toList(); + } - public static List getInventoryList(Player player) { - return getInventoryList(player, InventoryID.INVENTORY); - } + public static List getInventoryList(Player player) { + return getInventoryList(player, InventoryID.INVENTORY); + } - public static double getBalance(Player player, InventoryID inventory) { - return (double) getInventoryList(player, inventory).stream().filter(Objects::nonNull).filter((i) -> { - return i.getType().equals(Config.ecoItem()); - }).mapToInt(ItemStack::getAmount).sum(); - } + public static double getBalance(Player player, InventoryID inventory) { + return getInventoryList(player, inventory).stream().filter(Objects::nonNull).filter((i) -> i.getType().equals(Config.ecoItem())).mapToInt(ItemStack::getAmount).sum(); + } - public static double getBalance(Player player) { - Double total = 0.0D; + public static double getBalance(Player player) { + double total = 0.0D; - for (InventoryID id : InventoryID.values()) { - total += getBalance(player, id); - } + for (InventoryID id : InventoryID.values()) { + total += getBalance(player, id); + } - return total; - } + return total; + } - public static double getBalance(String player) { - return getBalance(Bukkit.getPlayer(player)); - } + public static double getBalance(String player) { + return getBalance(Bukkit.getPlayer(player)); + } - public static boolean removeItems(Player player, Material type, int amount) { - int remaining = amount; + public static boolean removeItems(Player player, Material type, int amount) { + int remaining = amount; - remaining = removeFrom(player.getInventory(), type, remaining); - if (remaining > 0) { - remaining = removeFrom(player.getEnderChest(), type, remaining); - } + remaining = removeFrom(player.getInventory(), type, remaining); + if (remaining > 0) { + remaining = removeFrom(player.getEnderChest(), type, remaining); + } - return remaining == 0; - } + return remaining == 0; + } - private static int removeFrom(Inventory inv, Material type, int amount) { - if (amount <= 0) return 0; + private static int removeFrom(Inventory inv, Material type, int amount) { + if (amount <= 0) return 0; - for (ItemStack stack : inv.all(type).values()) { - int take = Math.min(stack.getAmount(), amount); - stack.setAmount(stack.getAmount() - take); - amount -= take; - if (amount == 0) break; - } + for (ItemStack stack : inv.all(type).values()) { + int take = Math.min(stack.getAmount(), amount); + stack.setAmount(stack.getAmount() - take); + amount -= take; + if (amount == 0) break; + } - return amount; - } + return amount; + } + + public static void addItems(Player player, Material type, int amount) { + HashMap invOverflow = getInventory(player, InventoryID.INVENTORY).addItem(new ItemStack(type, amount)); + HashMap echestOverflow = getInventory(player, InventoryID.ENDER_CHEST).addItem(new ItemStack(type, invOverflow.values() + .stream() + .mapToInt(ItemStack::getAmount) + .sum())); - public static void addItems(Player player, Material type, int amount) { - HashMap invOverflow = getInventory(player, InventoryID.INVENTORY).addItem(new ItemStack(type, amount)); - HashMap echestOverflow = getInventory(player, InventoryID.ENDER_CHEST).addItem(new ItemStack(type, invOverflow.values() - .stream() - .mapToInt(ItemStack::getAmount) - .sum())); - - - for (ItemStack overflow : echestOverflow.values()){ - player.getWorld().dropItemNaturally(player.getLocation(), overflow); - } - } + for (ItemStack overflow : echestOverflow.values()) { + player.getWorld().dropItemNaturally(player.getLocation(), overflow); + } + } } diff --git a/src/main/java/io/github/adrianvic/itemeconomy/VaultLayer.java b/src/main/java/io/github/adrianvic/itemeconomy/VaultLayer.java index 1dc88eb..7810800 100644 --- a/src/main/java/io/github/adrianvic/itemeconomy/VaultLayer.java +++ b/src/main/java/io/github/adrianvic/itemeconomy/VaultLayer.java @@ -26,7 +26,7 @@ public class VaultLayer implements Economy { } public String format(double amount) { - return Config.get("format").replace("{}", String.valueOf(amount)); + return Config.getCurrencyText((int) amount); } public String currencyNamePlural() { diff --git a/src/main/java/io/github/adrianvic/itemeconomy/commands/Balance.java b/src/main/java/io/github/adrianvic/itemeconomy/commands/Balance.java new file mode 100644 index 0000000..e42b456 --- /dev/null +++ b/src/main/java/io/github/adrianvic/itemeconomy/commands/Balance.java @@ -0,0 +1,49 @@ +package io.github.adrianvic.itemeconomy.commands; + +import io.github.adrianvic.itemeconomy.Config; +import io.github.adrianvic.itemeconomy.Main; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.stream.Collectors; + +public class Balance implements CommandExecutor, TabCompleter { + @Override + public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String @NotNull [] strings) { + if ((commandSender.isOp() || commandSender.hasPermission("iteco.balance.others")) && strings.length > 0) { + double amount = Main.getEconomy().getBalance(strings[0]); + commandSender.sendMessage("%s has %s.".formatted( + strings[0], + Config.getCurrencyText((int) amount) + )); + } else { + if (commandSender instanceof Player player) { + double amount = Main.getEconomy().getBalance(player); + commandSender.sendMessage("You have %s.".formatted( + Config.getCurrencyText((int) amount) + )); + } else { + commandSender.sendMessage("One must be a player to have a balance."); + } + } + return true; + } + + @Override + public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String @NotNull [] args) { + if ((sender.isOp() || sender.hasPermission("iteco.balance.others")) && args.length == 1) { + return Bukkit.getOnlinePlayers() + .stream() + .map(Player::getName) + .collect(Collectors.toList()); + } + return List.of(); + } +} diff --git a/src/main/java/io/github/adrianvic/itemeconomy/commands/Pay.java b/src/main/java/io/github/adrianvic/itemeconomy/commands/Pay.java new file mode 100644 index 0000000..f71a679 --- /dev/null +++ b/src/main/java/io/github/adrianvic/itemeconomy/commands/Pay.java @@ -0,0 +1,74 @@ +package io.github.adrianvic.itemeconomy.commands; + +import io.github.adrianvic.itemeconomy.Config; +import io.github.adrianvic.itemeconomy.Main; +import net.milkbowl.vault.economy.EconomyResponse; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.stream.Collectors; + +public class Pay implements CommandExecutor, TabCompleter { + @Override + public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String @NotNull [] strings) { + if (strings.length < 2) { + commandSender.sendMessage(command.getUsage()); + return false; + } + + int amount; + + try { + amount = Integer.parseInt(strings[1]); + String amountString = Config.getCurrencyText(amount); + + if (commandSender instanceof Player player && Main.getEconomy().has(player, amount)) { + if (Bukkit.getPlayer(strings[0]) instanceof Player target) { + EconomyResponse withdrawResponse = Main.getEconomy().withdrawPlayer(player.getName(), amount); + if (withdrawResponse.transactionSuccess()) { + EconomyResponse depositResponse = Main.getEconomy().depositPlayer(target.getName(), amount); + if (depositResponse.transactionSuccess()) { + commandSender.sendMessage("Transaction of %s to %s was successfully realized.".formatted(amountString, target.getName())); + target.sendMessage("You received %s from %s.".formatted(amountString, player.getName())); + } else { + commandSender.sendMessage("Could not realize transaction: %s".formatted(depositResponse.errorMessage)); + Main.getEconomy().depositPlayer(player.getName(), amount); + } + } else { + commandSender.sendMessage("Could not realize transaction: %s".formatted(withdrawResponse.errorMessage)); + } + } else { + commandSender.sendMessage("Could not find target player."); + } + } else { + commandSender.sendMessage("You don't have enough money."); + } + } catch (NumberFormatException nfe) { + commandSender.sendMessage("The amount you tried to pay is not valid."); + return true; + } + + return true; + } + + @Override + public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String @NotNull [] args) { + if (sender instanceof Player player) { + if (args.length == 1) { + return Bukkit.getOnlinePlayers() + .stream() + .map(Player::getName) + .collect(Collectors.toList()); + } + } + + return List.of(); + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index f4834d2..49262a7 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,4 +1,5 @@ item: "diamond" singular: "diamond" plural: "diamonds" -format: "{}$" \ No newline at end of file +format: "{} $" +commands: "true" \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index eeb2443..e78d2d1 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,11 +1,32 @@ name: ItemEconomy main: io.github.adrianvic.itemeconomy.Main -version: 1.1 -depend: -- Vault +version: 1.2 +depend: [Vault] api-version: '1.21' commands: itecoreload: description: Reloads the config for ItemEconomy - usage: /itecoreload - permission: iteco.reload \ No newline at end of file + usage: "/itecoreload" + permission: iteco.reload + default: op + balance: + description: Prints your balance + usage: "/balance" + aliases: [bal] + permission: iteco.balance + pay: + description: Transfers money from your balance to other player + usage: "/pay " + permission: iteco.pay +permissions: + iteco.reload: + description: "Permission to use the command '/itecoreload'." + default: false + iteco.balance: + description: "Permission to use the command '/balance'." + default: true + children: + iteco.balance.others: false + iteco.pay: + description: "Permission to use the command '/pay'." + default: true \ No newline at end of file