From c04142cf2ab669d4bae16686594b2dee76cabd44 Mon Sep 17 00:00:00 2001 From: Len <40720638+destro174@users.noreply.github.com> Date: Sun, 13 Nov 2022 00:59:53 +0100 Subject: [PATCH 1/6] Rework commands --- build.gradle.kts | 8 - .../com/alttd/playershops/PlayerShops.java | 4 +- .../commands/PlayerShopCommand.java | 141 ++++++++++++++++++ .../commands/PlayerShopCommands.java | 58 +++++++ .../playershops/commands/ShopCommand.java | 56 ------- .../playershops/commands/Subcommand.java | 19 +++ .../{ => subcommands}/CheckStockCommand.java | 60 +++++--- .../commands/subcommands/OpenCommand.java | 24 +++ .../commands/subcommands/ReloadCommand.java | 19 +++ 9 files changed, 306 insertions(+), 83 deletions(-) create mode 100644 src/main/java/com/alttd/playershops/commands/PlayerShopCommand.java create mode 100644 src/main/java/com/alttd/playershops/commands/PlayerShopCommands.java delete mode 100644 src/main/java/com/alttd/playershops/commands/ShopCommand.java create mode 100644 src/main/java/com/alttd/playershops/commands/Subcommand.java rename src/main/java/com/alttd/playershops/commands/{ => subcommands}/CheckStockCommand.java (65%) create mode 100644 src/main/java/com/alttd/playershops/commands/subcommands/OpenCommand.java create mode 100644 src/main/java/com/alttd/playershops/commands/subcommands/ReloadCommand.java diff --git a/build.gradle.kts b/build.gradle.kts index a21fdfd..7930b0f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -106,14 +106,6 @@ bukkit { authors = listOf("destro174") depend = listOf("Vault") - commands { - register("playershop") { - description = "This is a test command!" - aliases = listOf("shop") - permission = "playershops.command.playershop" - } - } - permissions { register("playershops.admin") { description = "Admin permission for the ${rootProject.name} plugin." diff --git a/src/main/java/com/alttd/playershops/PlayerShops.java b/src/main/java/com/alttd/playershops/PlayerShops.java index f571cef..e65c256 100644 --- a/src/main/java/com/alttd/playershops/PlayerShops.java +++ b/src/main/java/com/alttd/playershops/PlayerShops.java @@ -1,6 +1,6 @@ package com.alttd.playershops; -import com.alttd.playershops.commands.ShopCommand; +import com.alttd.playershops.commands.PlayerShopCommands; import com.alttd.playershops.config.Config; import com.alttd.playershops.config.DatabaseConfig; import com.alttd.playershops.config.MessageConfig; @@ -102,7 +102,7 @@ public class PlayerShops extends JavaPlugin { } private void registerCommands() { - getCommand("playershop").setExecutor(new ShopCommand()); + PlayerShopCommands.registerCommands(); } public void reloadConfigs() { diff --git a/src/main/java/com/alttd/playershops/commands/PlayerShopCommand.java b/src/main/java/com/alttd/playershops/commands/PlayerShopCommand.java new file mode 100644 index 0000000..2d97efe --- /dev/null +++ b/src/main/java/com/alttd/playershops/commands/PlayerShopCommand.java @@ -0,0 +1,141 @@ +package com.alttd.playershops.commands; + +import com.alttd.playershops.commands.subcommands.CheckStockCommand; +import com.alttd.playershops.commands.subcommands.OpenCommand; +import com.alttd.playershops.commands.subcommands.ReloadCommand; +import com.google.common.base.Functions; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import it.unimi.dsi.fastutil.Pair; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; +import org.bukkit.plugin.PluginManager; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.stream.Collectors; + +import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.format.NamedTextColor.RED; + +public class PlayerShopCommand extends Command { + + public static final String BASE_PERM = "playershops.command"; // TODO load from config + + // subcommand label -> subcommand + private static final Map SUBCOMMANDS = PlayerShopCommands.make(() -> { + final Map, Subcommand> commands = new HashMap<>(); + + commands.put(Set.of("reload"), new ReloadCommand()); + commands.put(Set.of("checkstock"), new CheckStockCommand()); + commands.put(Set.of("open"), new OpenCommand()); + + return commands.entrySet().stream() + .flatMap(entry -> entry.getKey().stream().map(s -> Map.entry(s, entry.getValue()))) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + }); + // alias -> subcommand label + private static final Map ALIASES = PlayerShopCommands.make(() -> { + final Map> aliases = new HashMap<>(); + + aliases.put("reload", Set.of("reloadconfig")); + + return aliases.entrySet().stream() + .flatMap(entry -> entry.getValue().stream().map(s -> Map.entry(s, entry.getKey()))) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + }); + + public PlayerShopCommand() { + super("playershop"); + this.description = "PlayerShop related commands"; + this.usageMessage = "/playershop [" + String.join(" | ", SUBCOMMANDS.keySet()) + "]"; + final List permissions = new ArrayList<>(); + permissions.add(BASE_PERM); + permissions.addAll(SUBCOMMANDS.keySet().stream().map(s -> BASE_PERM + "." + s).toList()); + this.setPermission(String.join(";", permissions)); + final PluginManager pluginManager = Bukkit.getServer().getPluginManager(); + for (final String perm : permissions) { + pluginManager.addPermission(new Permission(perm, PermissionDefault.OP)); + } + } + + private static boolean testPermission(final CommandSender sender, final String permission) { + if (sender.hasPermission(BASE_PERM + "." + permission)) { + return true; + } + sender.sendMessage(Bukkit.permissionMessage()); + return false; + } + + @Override + public @NotNull List tabComplete( + final @NotNull CommandSender sender, + final @NotNull String alias, + final String[] args, + final @Nullable Location location + ) throws IllegalArgumentException { + if (args.length <= 1) { + return PlayerShopCommands.getListMatchingLast(sender, args, SUBCOMMANDS.keySet(), BASE_PERM); + } + + final @Nullable Pair subCommand = resolveCommand(args[0]); + if (subCommand != null) { + return subCommand.second().tabComplete(sender, subCommand.first(), Arrays.copyOfRange(args, 1, args.length)); + } + + return Collections.emptyList(); + } + + @Override + public boolean execute( + final @NotNull CommandSender sender, + final @NotNull String commandLabel, + final String[] args + ) { + if (!testPermission(sender)) { + return true; + } + + if (args.length == 0) { + sender.sendMessage(text("Usage: " + this.usageMessage, RED)); + return false; + } + final @Nullable Pair subCommand = resolveCommand(args[0]); + + if (subCommand == null) { + sender.sendMessage(text("Usage: " + this.usageMessage, RED)); + return false; + } + + if (!testPermission(sender, subCommand.first())) { + return true; + } + final String[] choppedArgs = Arrays.copyOfRange(args, 1, args.length); + return subCommand.second().execute(sender, subCommand.first(), choppedArgs); + } + + private static @Nullable Pair resolveCommand(String label) { + label = label.toLowerCase(Locale.ENGLISH); + @Nullable Subcommand subCommand = SUBCOMMANDS.get(label); + if (subCommand == null) { + final @Nullable String command = ALIASES.get(label); + if (command != null) { + label = command; + subCommand = SUBCOMMANDS.get(command); + } + } + + if (subCommand != null) { + return Pair.of(label, subCommand); + } + + return null; + } + + +} diff --git a/src/main/java/com/alttd/playershops/commands/PlayerShopCommands.java b/src/main/java/com/alttd/playershops/commands/PlayerShopCommands.java new file mode 100644 index 0000000..90e9569 --- /dev/null +++ b/src/main/java/com/alttd/playershops/commands/PlayerShopCommands.java @@ -0,0 +1,58 @@ +package com.alttd.playershops.commands; + +import com.google.common.base.Functions; +import com.google.common.collect.Lists; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +import java.util.*; +import java.util.function.Supplier; + +public class PlayerShopCommands { + + private PlayerShopCommands() { + } + + private static final Map COMMANDS = new HashMap<>(); + static { + COMMANDS.put("playershop", new PlayerShopCommand()); + } + + public static void registerCommands() { + COMMANDS.forEach((s, command) -> { + Bukkit.getCommandMap().register(s, command.getName(), command); + }); + } + + // Code from Mojang - copyright them | Altered to fit our needs + public static List getListMatchingLast(final CommandSender sender, final String[] args, final String basePermission, final String... matches) { + return getListMatchingLast(sender, args, Arrays.asList(matches), basePermission); + } + + public static boolean matches(final String s, final String s1) { + return s1.regionMatches(true, 0, s, 0, s.length()); + } + + public static List getListMatchingLast(final CommandSender sender, final String[] strings, final Collection collection, final String basePermission) { + String last = strings[strings.length - 1]; + ArrayList results = Lists.newArrayList(); + + if (!collection.isEmpty()) { + + for (String s1 : collection.stream().map(Functions.toStringFunction()).toList()) { + if (matches(last, s1) && (sender.hasPermission(basePermission + "." + s1))) { + results.add(s1); + } + } + + } + + return results; + } + + public static T make(Supplier factory) { + return factory.get(); + } + // end copy stuff +} diff --git a/src/main/java/com/alttd/playershops/commands/ShopCommand.java b/src/main/java/com/alttd/playershops/commands/ShopCommand.java deleted file mode 100644 index 52ea77b..0000000 --- a/src/main/java/com/alttd/playershops/commands/ShopCommand.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.alttd.playershops.commands; - -import com.alttd.playershops.PlayerShops; -import com.alttd.playershops.gui.HomeGui; -import com.alttd.playershops.utils.Util; -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 java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -// expose brig in Galaxy? -public class ShopCommand implements CommandExecutor, TabCompleter { - - @Override - public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - if (!(sender instanceof Player player)) return true; - if (args.length == 0) { - return true; - } - switch (args[0].toLowerCase()) { - case "reload": - PlayerShops.getInstance().reloadConfigs(); - break; - case "open": - HomeGui gui = new HomeGui(player.getUniqueId()); - gui.open(); - break; - case "checkstock": - if (!player.hasPermission("playershops.command.playershop.checkstock")) { - sender.sendMessage(Util.parseMiniMessage("playershops.command.playershop.checkstock'>You do not have permission for this command")); - break; - } - new CheckStockCommand(PlayerShops.getInstance(), player, args); - break; - default: - sender.sendMessage("invalid command useage"); - break; - } - return false; - } - - @Override - public List onTabComplete(CommandSender sender, Command cmd, String label, String[] args) { - if (args.length == 1) { - return Stream.of("reload", "open") - .filter(arg -> arg.startsWith(args[0].toLowerCase())) - .collect(Collectors.toList()); - } - return null; - } -} diff --git a/src/main/java/com/alttd/playershops/commands/Subcommand.java b/src/main/java/com/alttd/playershops/commands/Subcommand.java new file mode 100644 index 0000000..25261af --- /dev/null +++ b/src/main/java/com/alttd/playershops/commands/Subcommand.java @@ -0,0 +1,19 @@ +package com.alttd.playershops.commands; + +import org.bukkit.command.CommandSender; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; + +import java.util.Collections; +import java.util.List; + +@DefaultQualifier(NonNull.class) +public interface Subcommand { + + boolean execute(CommandSender sender, String subCommand, String[] args); + + default List tabComplete(final CommandSender sender, final String subCommand, final String[] args) { + return Collections.emptyList(); + } + +} diff --git a/src/main/java/com/alttd/playershops/commands/CheckStockCommand.java b/src/main/java/com/alttd/playershops/commands/subcommands/CheckStockCommand.java similarity index 65% rename from src/main/java/com/alttd/playershops/commands/CheckStockCommand.java rename to src/main/java/com/alttd/playershops/commands/subcommands/CheckStockCommand.java index 4f2c20d..177b12c 100644 --- a/src/main/java/com/alttd/playershops/commands/CheckStockCommand.java +++ b/src/main/java/com/alttd/playershops/commands/subcommands/CheckStockCommand.java @@ -1,6 +1,9 @@ -package com.alttd.playershops.commands; +package com.alttd.playershops.commands.subcommands; import com.alttd.playershops.PlayerShops; +import com.alttd.playershops.commands.PlayerShopCommand; +import com.alttd.playershops.commands.PlayerShopCommands; +import com.alttd.playershops.commands.Subcommand; import com.alttd.playershops.shop.PlayerShop; import com.alttd.playershops.utils.ShopUtil; import com.alttd.playershops.utils.Util; @@ -8,34 +11,58 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import org.bukkit.Location; +import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; -public class CheckStockCommand { - private int playerX; - private int playerZ; - private int radius; +@DefaultQualifier(NonNull.class) +public class CheckStockCommand implements Subcommand { + + @Override + public boolean execute(CommandSender sender, String subCommand, String[] args) { + return this.doStockCheck(sender, args); + } + + @Override + public List tabComplete(final CommandSender sender, final String subCommand, final String[] args) { + // TODO give some default tab completions + if (args.length == 1) { + return PlayerShopCommands.getListMatchingLast(sender, args, PlayerShopCommand.BASE_PERM + ".checkstock", "help"); + } else if (args.length == 2) { +// return PlayerShopCommands.getListMatchingLast(sender, args, "radius"); + return Collections.emptyList(); + } + return Collections.emptyList(); + } + private int minimumStock = -1; - private final PlayerShops plugin; - public CheckStockCommand(PlayerShops plugin, Player player, String[] args) { - this.plugin = plugin; + + public boolean doStockCheck(final CommandSender sender, final String[] args) { + if (!(sender instanceof Player player)) { + sender.sendMessage("Only players can use this command."); + return false; + } if (args.length != 2 && args.length != 3) { player.sendMessage(Util.parseMiniMessage("Invalid command syntax, use /checkstock [minimum stock]")); - return; + return false; } + int radius; try { radius = Integer.parseInt(args[1]); } catch (NumberFormatException e) { player.sendMessage(Util.parseMiniMessage("radius has to be a valid number, use /checkstock [minimum stock]")); - return; + return false; } if (radius > 100 || radius <= 0) { player.sendMessage(Util.parseMiniMessage("Please keep the radius between 1 and 100")); - return; + return false; } if (args.length == 3) { @@ -43,17 +70,16 @@ public class CheckStockCommand { minimumStock = Integer.parseInt(args[2]); } catch (NumberFormatException e) { player.sendMessage(Util.parseMiniMessage("minium stock has to be a valid number, use /checkstock [minimum stock]")); - return; + return false; } } - playerX = player.getLocation().getBlockX(); - playerZ = player.getLocation().getBlockZ(); - List stockList = checkStock(); + List stockList = checkStock(player.getLocation().getBlockX(), player.getLocation().getBlockZ(), radius); sendStockMessage(player, stockList); + return true; } - private List checkStock() { - List shops = plugin.getShopHandler().getShopsInRadius(playerX, playerZ, radius); + private List checkStock(int x, int z, int radius) { + List shops = PlayerShops.getInstance().getShopHandler().getShopsInRadius(x, z, radius); if (minimumStock != -1) shops = shops.stream().filter(shop -> shop.getRemainingStock() < minimumStock).collect(Collectors.toList()); return shops.stream().map(shop -> { diff --git a/src/main/java/com/alttd/playershops/commands/subcommands/OpenCommand.java b/src/main/java/com/alttd/playershops/commands/subcommands/OpenCommand.java new file mode 100644 index 0000000..b3f3990 --- /dev/null +++ b/src/main/java/com/alttd/playershops/commands/subcommands/OpenCommand.java @@ -0,0 +1,24 @@ +package com.alttd.playershops.commands.subcommands; + +import com.alttd.playershops.commands.Subcommand; +import com.alttd.playershops.gui.HomeGui; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; + +@DefaultQualifier(NonNull.class) +public class OpenCommand implements Subcommand { + + @Override + public boolean execute(final CommandSender sender, final String subCommand, final String[] args) { + if (!(sender instanceof Player player)) { + sender.sendMessage("Only players can use this command."); + return false; + } + HomeGui gui = new HomeGui(player.getUniqueId()); + gui.open(); + return true; + } + +} diff --git a/src/main/java/com/alttd/playershops/commands/subcommands/ReloadCommand.java b/src/main/java/com/alttd/playershops/commands/subcommands/ReloadCommand.java new file mode 100644 index 0000000..a918416 --- /dev/null +++ b/src/main/java/com/alttd/playershops/commands/subcommands/ReloadCommand.java @@ -0,0 +1,19 @@ +package com.alttd.playershops.commands.subcommands; + +import com.alttd.playershops.PlayerShops; +import com.alttd.playershops.commands.Subcommand; +import org.bukkit.command.CommandSender; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; + +@DefaultQualifier(NonNull.class) +public class ReloadCommand implements Subcommand { + + @Override + public boolean execute(final CommandSender sender, final String subCommand, final String[] args) { + PlayerShops.getInstance().reloadConfigs(); + // Todo message when config is reloaded + return true; + } + +} From d3ba5be0403ed7c58105c26f4dfdbd52999497d0 Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Sun, 22 Jan 2023 01:47:35 +0100 Subject: [PATCH 2/6] Made stock checking work for everyone --- .../subcommands/CheckStockCommand.java | 25 +++++++++++++------ .../commands/subcommands/OpenCommand.java | 3 ++- .../listener/TransactionListener.java | 7 +----- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/alttd/playershops/commands/subcommands/CheckStockCommand.java b/src/main/java/com/alttd/playershops/commands/subcommands/CheckStockCommand.java index 177b12c..31f1036 100644 --- a/src/main/java/com/alttd/playershops/commands/subcommands/CheckStockCommand.java +++ b/src/main/java/com/alttd/playershops/commands/subcommands/CheckStockCommand.java @@ -19,6 +19,7 @@ import org.checkerframework.framework.qual.DefaultQualifier; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.Stream; @DefaultQualifier(NonNull.class) public class CheckStockCommand implements Subcommand { @@ -44,7 +45,7 @@ public class CheckStockCommand implements Subcommand { public boolean doStockCheck(final CommandSender sender, final String[] args) { if (!(sender instanceof Player player)) { - sender.sendMessage("Only players can use this command."); + sender.sendMessage(Util.parseMiniMessage("Only players can use this command.")); return false; } if (args.length != 2 && args.length != 3) { @@ -69,20 +70,30 @@ public class CheckStockCommand implements Subcommand { try { minimumStock = Integer.parseInt(args[2]); } catch (NumberFormatException e) { - player.sendMessage(Util.parseMiniMessage("minium stock has to be a valid number, use /checkstock [minimum stock]")); + player.sendMessage(Util.parseMiniMessage("minimum stock has to be a valid number, use /checkstock [minimum stock]")); return false; } } - List stockList = checkStock(player.getLocation().getBlockX(), player.getLocation().getBlockZ(), radius); + List stockList = checkStock(player.getLocation().getBlockX(), player.getLocation().getBlockZ(), radius, player); sendStockMessage(player, stockList); return true; } - private List checkStock(int x, int z, int radius) { + /** + * @param x Coordinate to center search on + * @param z Coordinate to center search on + * @param radius Range to check in + * @param caller Player who is checking for stock, can only see the stock from their shops unless they have base_perm.checkstock.bypass + * @return A list of Stock that the player is allowed to see + */ + private List checkStock(int x, int z, int radius, Player caller) { List shops = PlayerShops.getInstance().getShopHandler().getShopsInRadius(x, z, radius); + Stream playerShopStream = shops.stream(); if (minimumStock != -1) - shops = shops.stream().filter(shop -> shop.getRemainingStock() < minimumStock).collect(Collectors.toList()); - return shops.stream().map(shop -> { + playerShopStream = playerShopStream.filter(shop -> shop.getRemainingStock() < minimumStock); + if (!caller.hasPermission(PlayerShopCommand.BASE_PERM + ".checkstock.any")) + playerShopStream = playerShopStream.filter(shop -> shop.getOwnerUUID().equals(caller.getUniqueId())); + return playerShopStream.map(shop -> { Location signLocation = shop.getSignLocation(); return new Stock(shop.getRemainingStock(), signLocation.getBlockX(), @@ -110,7 +121,7 @@ public class CheckStockCommand implements Subcommand { } if (component == null) if (minimumStock == -1) - player.sendMessage(Util.parseMiniMessage("No shops found in specified radius.")); + player.sendMessage(Util.parseMiniMessage("No shops that you have permission to view found in specified radius.")); else player.sendMessage(Util.parseMiniMessage( "No shops with less than stock found.", diff --git a/src/main/java/com/alttd/playershops/commands/subcommands/OpenCommand.java b/src/main/java/com/alttd/playershops/commands/subcommands/OpenCommand.java index b3f3990..56be407 100644 --- a/src/main/java/com/alttd/playershops/commands/subcommands/OpenCommand.java +++ b/src/main/java/com/alttd/playershops/commands/subcommands/OpenCommand.java @@ -2,6 +2,7 @@ package com.alttd.playershops.commands.subcommands; import com.alttd.playershops.commands.Subcommand; import com.alttd.playershops.gui.HomeGui; +import com.alttd.playershops.utils.Util; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.checkerframework.checker.nullness.qual.NonNull; @@ -13,7 +14,7 @@ public class OpenCommand implements Subcommand { @Override public boolean execute(final CommandSender sender, final String subCommand, final String[] args) { if (!(sender instanceof Player player)) { - sender.sendMessage("Only players can use this command."); + sender.sendMessage(Util.parseMiniMessage("Only players can use this command.")); return false; } HomeGui gui = new HomeGui(player.getUniqueId()); diff --git a/src/main/java/com/alttd/playershops/listener/TransactionListener.java b/src/main/java/com/alttd/playershops/listener/TransactionListener.java index b6a4480..82df724 100644 --- a/src/main/java/com/alttd/playershops/listener/TransactionListener.java +++ b/src/main/java/com/alttd/playershops/listener/TransactionListener.java @@ -11,14 +11,10 @@ import com.alttd.playershops.shop.TransactionError; import com.alttd.playershops.utils.Logger; import com.alttd.playershops.utils.ShopUtil; import com.alttd.playershops.utils.Util; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.Material; import org.bukkit.Tag; import org.bukkit.block.Block; import org.bukkit.entity.Player; @@ -26,7 +22,6 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.EquipmentSlot; -import org.bukkit.inventory.ItemStack; /** * Dedicated class to listen to transactions for shops. @@ -85,7 +80,7 @@ public class TransactionListener extends EventListener { } if (event.getAction() == Action.LEFT_CLICK_BLOCK) { - if (event.getPlayer().isSneaking() && player.hasPermission("playershop.shop.check-stock")) + if (event.getPlayer().isSneaking() && (player.hasPermission("playershop.shop.check-stock") || playerShop.getOwnerUUID().equals(player.getUniqueId()))) giveStockInfo(playerShop, player); else giveInfo(playerShop, player); From da5e514422988d3925a9a667a6fce521a0aaca2f Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Sun, 22 Jan 2023 21:37:00 +0100 Subject: [PATCH 3/6] Fix incorrect number of arguments in command Fix check stock command checking total items left, not total amount of purchases left --- .../commands/subcommands/CheckStockCommand.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/alttd/playershops/commands/subcommands/CheckStockCommand.java b/src/main/java/com/alttd/playershops/commands/subcommands/CheckStockCommand.java index 31f1036..27dfb0b 100644 --- a/src/main/java/com/alttd/playershops/commands/subcommands/CheckStockCommand.java +++ b/src/main/java/com/alttd/playershops/commands/subcommands/CheckStockCommand.java @@ -48,14 +48,14 @@ public class CheckStockCommand implements Subcommand { sender.sendMessage(Util.parseMiniMessage("Only players can use this command.")); return false; } - if (args.length != 2 && args.length != 3) { + if (args.length != 1 && args.length != 2) { player.sendMessage(Util.parseMiniMessage("Invalid command syntax, use /checkstock [minimum stock]")); return false; } int radius; try { - radius = Integer.parseInt(args[1]); + radius = Integer.parseInt(args[0]); } catch (NumberFormatException e) { player.sendMessage(Util.parseMiniMessage("radius has to be a valid number, use /checkstock [minimum stock]")); return false; @@ -66,9 +66,9 @@ public class CheckStockCommand implements Subcommand { return false; } - if (args.length == 3) { + if (args.length == 2) { try { - minimumStock = Integer.parseInt(args[2]); + minimumStock = Integer.parseInt(args[1]); } catch (NumberFormatException e) { player.sendMessage(Util.parseMiniMessage("minimum stock has to be a valid number, use /checkstock [minimum stock]")); return false; @@ -90,12 +90,12 @@ public class CheckStockCommand implements Subcommand { List shops = PlayerShops.getInstance().getShopHandler().getShopsInRadius(x, z, radius); Stream playerShopStream = shops.stream(); if (minimumStock != -1) - playerShopStream = playerShopStream.filter(shop -> shop.getRemainingStock() < minimumStock); + playerShopStream = playerShopStream.filter(shop -> shop.getRemainingStock() / shop.getAmount() < minimumStock); if (!caller.hasPermission(PlayerShopCommand.BASE_PERM + ".checkstock.any")) playerShopStream = playerShopStream.filter(shop -> shop.getOwnerUUID().equals(caller.getUniqueId())); return playerShopStream.map(shop -> { Location signLocation = shop.getSignLocation(); - return new Stock(shop.getRemainingStock(), + return new Stock(shop.getRemainingStock() / shop.getAmount(), signLocation.getBlockX(), signLocation.getBlockY(), signLocation.getBlockZ(), From 55cb197b0b384471cecdef131efcdf6e5490ccba Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Sun, 22 Jan 2023 22:04:46 +0100 Subject: [PATCH 4/6] Changed getStock to only return the actual stock they have left instead of the total amount of items left (and protected it) --- .../playershops/listener/TransactionListener.java | 4 ++-- .../java/com/alttd/playershops/shop/PlayerShop.java | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/alttd/playershops/listener/TransactionListener.java b/src/main/java/com/alttd/playershops/listener/TransactionListener.java index 82df724..560a394 100644 --- a/src/main/java/com/alttd/playershops/listener/TransactionListener.java +++ b/src/main/java/com/alttd/playershops/listener/TransactionListener.java @@ -111,7 +111,7 @@ public class TransactionListener extends EventListener { switch (playerShop.getType()) { case SELL -> { type = Placeholder.unparsed("type", "Sell"); - stock = Placeholder.parsed("stock", "" + (playerShop.getRemainingStock() / playerShop.getAmount())); + stock = Placeholder.parsed("stock", "" + (playerShop.getRemainingStock())); } case BUY -> { type = Placeholder.unparsed("type", "Buy"); @@ -119,7 +119,7 @@ public class TransactionListener extends EventListener { } case GAMBLE -> { type = Placeholder.unparsed("type", "Gamble"); - stock = Placeholder.parsed("stock", "" + (playerShop.getRemainingStock() / playerShop.getAmount())); + stock = Placeholder.parsed("stock", "" + (playerShop.getRemainingStock())); } default -> { type = Placeholder.unparsed("type", "UNKNOWN"); diff --git a/src/main/java/com/alttd/playershops/shop/PlayerShop.java b/src/main/java/com/alttd/playershops/shop/PlayerShop.java index 737a77a..495fc3e 100644 --- a/src/main/java/com/alttd/playershops/shop/PlayerShop.java +++ b/src/main/java/com/alttd/playershops/shop/PlayerShop.java @@ -84,8 +84,16 @@ public class PlayerShop { return playerShop; } + /** + * @return total amount of purchases left until the shop can't sell/buy anymore + */ public int getRemainingStock() { - return InventoryUtils.countItems(getInventory(), getItemStack()); + int totalItems = InventoryUtils.countItems(getInventory(), getItemStack()); + if (totalItems == 0) + return 0; + if (getAmount() == 0) + return 0; + return totalItems / getAmount(); } public int getRemainingSpace() { From 59ef4c23eac8db1e6b89dc320326ec6964e5ca4b Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Sun, 22 Jan 2023 22:05:14 +0100 Subject: [PATCH 5/6] Added a way to highlight shops that are low on stock --- .../subcommands/CheckStockCommand.java | 64 +++++++++++++++++-- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/alttd/playershops/commands/subcommands/CheckStockCommand.java b/src/main/java/com/alttd/playershops/commands/subcommands/CheckStockCommand.java index 27dfb0b..09af2b7 100644 --- a/src/main/java/com/alttd/playershops/commands/subcommands/CheckStockCommand.java +++ b/src/main/java/com/alttd/playershops/commands/subcommands/CheckStockCommand.java @@ -7,12 +7,15 @@ import com.alttd.playershops.commands.Subcommand; import com.alttd.playershops.shop.PlayerShop; import com.alttd.playershops.utils.ShopUtil; import com.alttd.playershops.utils.Util; +import com.destroystokyo.paper.ParticleBuilder; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import org.bukkit.Location; +import org.bukkit.Particle; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.framework.qual.DefaultQualifier; @@ -76,12 +79,62 @@ public class CheckStockCommand implements Subcommand { } List stockList = checkStock(player.getLocation().getBlockX(), player.getLocation().getBlockZ(), radius, player); sendStockMessage(player, stockList); + highlightLowStock(player, stockList); return true; } + //Stole this code from AlttdUtility by ___Kappa___ and reworked it slightly + private void highlightLowStock(Player player, List stockList) { + ParticleBuilder particleBuilder = new ParticleBuilder(Particle.REDSTONE); + particleBuilder.color(255, 255, 255); + particleBuilder.receivers(player); + particleBuilder.count(3); + + List collect = stockList.stream().map(stock -> { + Location centerOfBlock = stock.shopCenter; + Location[] cornersOfBlock = new Location[8]; + cornersOfBlock[0] = centerOfBlock.clone().add(0.5, 0.5, 0.5); + cornersOfBlock[1] = centerOfBlock.clone().add(0.5, 0.5, -0.5); + cornersOfBlock[2] = centerOfBlock.clone().add(-0.5, 0.5, 0.5); + cornersOfBlock[3] = centerOfBlock.clone().add(-0.5, 0.5, -0.5); + cornersOfBlock[4] = centerOfBlock.clone().add(0.5, -0.5, 0.5); + cornersOfBlock[5] = centerOfBlock.clone().add(0.5, -0.5, -0.5); + cornersOfBlock[6] = centerOfBlock.clone().add(-0.5, -0.5, 0.5); + cornersOfBlock[7] = centerOfBlock.clone().add(-0.5, -0.5, -0.5); + return cornersOfBlock; + }).toList(); + + + for (int i = 0; i < 10; i++) { + new ParticleSpawnTask(collect, particleBuilder).runTaskLater(PlayerShops.getInstance(), i * 10); + } + } + + private static class ParticleSpawnTask extends BukkitRunnable { + + private final List chestLocations; + private final ParticleBuilder particleBuilder; + + public ParticleSpawnTask(List chestLocations, ParticleBuilder particleBuilder) { + this.chestLocations = chestLocations; + this.particleBuilder = particleBuilder; + } + + @Override + public void run() { + for (Location[] particleLocations : chestLocations) { + for (Location location : particleLocations) { + particleBuilder.location(location); + particleBuilder.spawn(); + } + } + } + } + //End stolen code + /** - * @param x Coordinate to center search on - * @param z Coordinate to center search on + * @param x Coordinate to shopCenter search on + * @param z Coordinate to shopCenter search on * @param radius Range to check in * @param caller Player who is checking for stock, can only see the stock from their shops unless they have base_perm.checkstock.bypass * @return A list of Stock that the player is allowed to see @@ -90,12 +143,13 @@ public class CheckStockCommand implements Subcommand { List shops = PlayerShops.getInstance().getShopHandler().getShopsInRadius(x, z, radius); Stream playerShopStream = shops.stream(); if (minimumStock != -1) - playerShopStream = playerShopStream.filter(shop -> shop.getRemainingStock() / shop.getAmount() < minimumStock); + playerShopStream = playerShopStream.filter(shop -> shop.getRemainingStock() < minimumStock); if (!caller.hasPermission(PlayerShopCommand.BASE_PERM + ".checkstock.any")) playerShopStream = playerShopStream.filter(shop -> shop.getOwnerUUID().equals(caller.getUniqueId())); return playerShopStream.map(shop -> { Location signLocation = shop.getSignLocation(); - return new Stock(shop.getRemainingStock() / shop.getAmount(), + return new Stock(shop.getRemainingStock(), + shop.getShopLocation().toCenterLocation(), signLocation.getBlockX(), signLocation.getBlockY(), signLocation.getBlockZ(), @@ -130,5 +184,5 @@ public class CheckStockCommand implements Subcommand { player.sendMessage(component); } - private record Stock(int stock, int x, int y, int z, Component itemNameComponent) {} + private record Stock(int stock, Location shopCenter, int x, int y, int z, Component itemNameComponent) {} } From af59775ac3458a25a8b8b37817dc86094223c84a Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Sun, 4 Jun 2023 21:34:21 +0200 Subject: [PATCH 6/6] Fix getRemainingStock not accounting for shop type (it assumed all shops were buy shops) --- .../alttd/playershops/shop/PlayerShop.java | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/alttd/playershops/shop/PlayerShop.java b/src/main/java/com/alttd/playershops/shop/PlayerShop.java index 495fc3e..79aa5b9 100644 --- a/src/main/java/com/alttd/playershops/shop/PlayerShop.java +++ b/src/main/java/com/alttd/playershops/shop/PlayerShop.java @@ -2,10 +2,7 @@ package com.alttd.playershops.shop; import com.alttd.playershops.PlayerShops; import com.alttd.playershops.events.*; -import com.alttd.playershops.utils.EconomyUtils; -import com.alttd.playershops.utils.InventoryUtils; -import com.alttd.playershops.utils.ShopUtil; -import com.alttd.playershops.utils.Util; +import com.alttd.playershops.utils.*; import lombok.Getter; import lombok.Setter; import net.kyori.adventure.text.minimessage.MiniMessage; @@ -88,12 +85,26 @@ public class PlayerShop { * @return total amount of purchases left until the shop can't sell/buy anymore */ public int getRemainingStock() { - int totalItems = InventoryUtils.countItems(getInventory(), getItemStack()); - if (totalItems == 0) - return 0; - if (getAmount() == 0) - return 0; - return totalItems / getAmount(); + switch (type) { + case SELL -> { + int totalItems = InventoryUtils.countItems(getInventory(), getItemStack()); + if (totalItems == 0 || getAmount() == 0) + return 0; + return totalItems / getAmount(); + } + case BUY -> { + if (balance == 0 || getAmount() == 0) + return 0; + return (int) (balance / getPrice()); + } + case GAMBLE -> { + Logger.warn("Tried to call getRemainingStock on unimplemented GAMBLE type"); + return 0; //not implemented since gamble shops don't exist yet + } + default -> { + return 0; + } + } } public int getRemainingSpace() {