From 7a811246680d3503156ae50958669714b0869c48 Mon Sep 17 00:00:00 2001 From: hcesaropz <61612754+hcesaropz@users.noreply.github.com> Date: Wed, 19 Aug 2026 05:51:46 +0800 Subject: [PATCH] refactor: Brigadier commands --- src/main/java/pw/kaboom/extras/Main.java | 60 +++-- .../extras/commands/BrigadierCommand.java | 18 ++ .../extras/commands/CommandBroadcastMM.java | 34 --- .../commands/CommandBroadcastMiniMessage.java | 47 ++++ .../commands/CommandBroadcastRainbow.java | 58 +++-- .../extras/commands/CommandBroadcastRaw.java | 52 +++++ .../commands/CommandBroadcastVanilla.java | 101 ++++----- .../extras/commands/CommandClearChat.java | 56 +++-- .../extras/commands/CommandConsole.java | 52 +++-- .../commands/CommandDestroyEntities.java | 84 ++++--- .../extras/commands/CommandEnchantAll.java | 85 ++++--- .../extras/commands/CommandGetJSON.java | 82 ++++--- .../extras/commands/CommandGetJSONMM.java | 80 ++++--- .../extras/commands/CommandJumpscare.java | 103 +++++---- .../kaboom/extras/commands/CommandKaboom.java | 96 +++++--- .../kaboom/extras/commands/CommandPing.java | 119 +++++----- .../kaboom/extras/commands/CommandPrefix.java | 129 +++++++---- .../extras/commands/CommandPumpkin.java | 100 +++++---- .../extras/commands/CommandServerInfo.java | 207 ++++++++++-------- .../kaboom/extras/commands/CommandSkin.java | 97 +++++--- .../kaboom/extras/commands/CommandSpawn.java | 46 ++-- .../kaboom/extras/commands/CommandSpidey.java | 92 ++++---- .../extras/commands/CommandTellraw.java | 32 --- .../extras/commands/CommandUsername.java | 153 +++++++++---- .../kaboom/extras/commands/package-info.java | 4 + .../extras/modules/player/PlayerCommand.java | 2 +- src/main/resources/plugin.yml | 76 +------ 27 files changed, 1210 insertions(+), 855 deletions(-) create mode 100644 src/main/java/pw/kaboom/extras/commands/BrigadierCommand.java delete mode 100644 src/main/java/pw/kaboom/extras/commands/CommandBroadcastMM.java create mode 100644 src/main/java/pw/kaboom/extras/commands/CommandBroadcastMiniMessage.java create mode 100644 src/main/java/pw/kaboom/extras/commands/CommandBroadcastRaw.java delete mode 100644 src/main/java/pw/kaboom/extras/commands/CommandTellraw.java create mode 100644 src/main/java/pw/kaboom/extras/commands/package-info.java diff --git a/src/main/java/pw/kaboom/extras/Main.java b/src/main/java/pw/kaboom/extras/Main.java index bb4b791d..6cf24a7c 100644 --- a/src/main/java/pw/kaboom/extras/Main.java +++ b/src/main/java/pw/kaboom/extras/Main.java @@ -1,5 +1,9 @@ package pw.kaboom.extras; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import io.papermc.paper.command.brigadier.Commands; +import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; import io.papermc.paper.registry.keys.BlockTypeKeys; import org.bukkit.WorldCreator; import org.bukkit.WorldType; @@ -7,6 +11,7 @@ import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.messaging.Messenger; +import pw.kaboom.extras.commands.BrigadierCommand; import pw.kaboom.extras.commands.*; import pw.kaboom.extras.modules.block.BlockCheck; import pw.kaboom.extras.modules.block.BlockPhysics; @@ -21,6 +26,7 @@ import pw.kaboom.extras.util.FlatLayers; import java.io.File; +import java.util.List; public final class Main extends JavaPlugin { public static Main PLUGIN; @@ -46,27 +52,39 @@ public void onEnable() { prefixConfig = YamlConfiguration.loadConfiguration(prefixConfigFile); /* Commands */ - this.getCommand("broadcastrainbow").setExecutor(new CommandBroadcastRainbow()); - this.getCommand("broadcastminimessage").setExecutor(new CommandBroadcastMM()); - this.getCommand("broadcastvanilla").setExecutor(new CommandBroadcastVanilla()); - this.getCommand("clearchat").setExecutor(new CommandClearChat()); - this.getCommand("console").setExecutor(new CommandConsole()); - this.getCommand("destroyentities").setExecutor(new CommandDestroyEntities()); - this.getCommand("enchantall").setExecutor(new CommandEnchantAll()); - this.getCommand("getjson").setExecutor(new CommandGetJSON()); - this.getCommand("getjsonmm").setExecutor(new CommandGetJSONMM()); - this.getCommand("jumpscare").setExecutor(new CommandJumpscare()); - this.getCommand("kaboom").setExecutor(new CommandKaboom()); - this.getCommand("ping").setExecutor(new CommandPing()); - this.getCommand("prefix").setExecutor(new CommandPrefix()); - this.getCommand("pumpkin").setExecutor(new CommandPumpkin()); - this.getCommand("serverinfo").setExecutor(new CommandServerInfo()); - this.getCommand("skin").setExecutor(new CommandSkin()); - this.getCommand("spawn").setExecutor(new CommandSpawn()); - this.getCommand("spidey").setExecutor(new CommandSpidey()); - this.getCommand("tellraw").setExecutor(new CommandTellraw()); - this.getCommand("username").setExecutor(new CommandUsername()); - + this.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS,event->{ + final Commands registrar = event.registrar(); + List commands = List.of( + new CommandBroadcastMiniMessage(), + new CommandBroadcastRainbow(), + new CommandBroadcastVanilla(), + new CommandClearChat(), + new CommandConsole(), + new CommandDestroyEntities(), + new CommandEnchantAll(), + new CommandGetJSON(), + new CommandGetJSONMM(), + new CommandJumpscare(), + new CommandKaboom(), + new CommandPing(), + new CommandPrefix(), + new CommandPumpkin(), + new CommandServerInfo(), + new CommandSkin(), + new CommandSpawn(), + new CommandSpidey(), + new CommandUsername() + ); + for (BrigadierCommand command : commands) { + LiteralArgumentBuilder builder + = Commands.literal(command.getLabel()); + command.build(builder); + registrar.register( + builder.build(),command.getDescription(),command.getAliases() + ); + } + }); + /* Block-related modules */ BlockPhysics.init(this); diff --git a/src/main/java/pw/kaboom/extras/commands/BrigadierCommand.java b/src/main/java/pw/kaboom/extras/commands/BrigadierCommand.java new file mode 100644 index 00000000..4282d651 --- /dev/null +++ b/src/main/java/pw/kaboom/extras/commands/BrigadierCommand.java @@ -0,0 +1,18 @@ +package pw.kaboom.extras.commands; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import io.papermc.paper.command.brigadier.CommandSourceStack; + +import java.util.List; + +public interface BrigadierCommand { + String getLabel(); + + String getDescription(); + + default List getAliases() { + return List.of(); + } + + void build(final LiteralArgumentBuilder builder); +} diff --git a/src/main/java/pw/kaboom/extras/commands/CommandBroadcastMM.java b/src/main/java/pw/kaboom/extras/commands/CommandBroadcastMM.java deleted file mode 100644 index 75dbafeb..00000000 --- a/src/main/java/pw/kaboom/extras/commands/CommandBroadcastMM.java +++ /dev/null @@ -1,34 +0,0 @@ -package pw.kaboom.extras.commands; - -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import net.kyori.adventure.text.minimessage.MiniMessage; -import org.bukkit.Bukkit; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.jspecify.annotations.NonNull; - -public final class CommandBroadcastMM implements CommandExecutor { - private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage(); - - public boolean onCommand(final @NonNull CommandSender sender, - final @NonNull Command command, - final @NonNull String label, - final String[] args) { - if (args.length == 0) { - sender.sendMessage(Component - .text("Usage: /" + label + " ", NamedTextColor.RED)); - return true; - } - - final Component component = MINI_MESSAGE.deserialize(String.join(" ", args)); - - for (Player onlinePlayer: Bukkit.getOnlinePlayers()) { - onlinePlayer.sendMessage(component); - } - - return true; - } -} diff --git a/src/main/java/pw/kaboom/extras/commands/CommandBroadcastMiniMessage.java b/src/main/java/pw/kaboom/extras/commands/CommandBroadcastMiniMessage.java new file mode 100644 index 00000000..d26eed94 --- /dev/null +++ b/src/main/java/pw/kaboom/extras/commands/CommandBroadcastMiniMessage.java @@ -0,0 +1,47 @@ +package pw.kaboom.extras.commands; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.Bukkit; + +import java.util.List; + +import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; +import static io.papermc.paper.command.brigadier.Commands.argument; + +public final class CommandBroadcastMiniMessage implements BrigadierCommand { + private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage(); + + @Override + public String getLabel() { + return "broadcastminimessage"; + } + + @Override + public String getDescription() { + return "Broadcasts a deserialized MiniMessage component"; + } + + @Override + public List getAliases() { + return List.of("broadcastmm", "bcmm"); + } + + @Override + public void build(final LiteralArgumentBuilder builder) { + builder + .requires(src -> src.getSender().hasPermission("extras.broadcastminimessage")) + .then(argument("message", greedyString()) + .executes(ctx -> { + final String mm = StringArgumentType.getString(ctx, "message"); + final Component component = MINI_MESSAGE.deserialize(mm); + Bukkit.broadcast(component); + return Command.SINGLE_SUCCESS; + }) + ); + } +} diff --git a/src/main/java/pw/kaboom/extras/commands/CommandBroadcastRainbow.java b/src/main/java/pw/kaboom/extras/commands/CommandBroadcastRainbow.java index 97f55dc2..7f412dba 100644 --- a/src/main/java/pw/kaboom/extras/commands/CommandBroadcastRainbow.java +++ b/src/main/java/pw/kaboom/extras/commands/CommandBroadcastRainbow.java @@ -1,35 +1,45 @@ package pw.kaboom.extras.commands; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import io.papermc.paper.command.brigadier.CommandSourceStack; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Bukkit; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.jspecify.annotations.NonNull; +import org.checkerframework.checker.units.qual.C; -public final class CommandBroadcastRainbow implements CommandExecutor { +import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; +import static io.papermc.paper.command.brigadier.Commands.argument; + +public final class CommandBroadcastRainbow implements BrigadierCommand { private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage(); - public boolean onCommand(final @NonNull CommandSender sender, - final @NonNull Command command, - final @NonNull String label, - final String[] args) { - if (args.length == 0) { - sender.sendMessage(Component - .text("Usage: /" + label + " ", - NamedTextColor.RED)); - return true; - } - final String strippedTags = MINI_MESSAGE.stripTags(String.join(" ", args)); - final Component component = MINI_MESSAGE.deserialize("" + strippedTags); + @Override + public String getLabel() { + return "broadcastrainbow"; + } - for (Player onlinePlayer: Bukkit.getOnlinePlayers()) { - onlinePlayer.sendMessage(component); - } + @Override + public String getDescription() { + return "Broadcasts a rainbow message"; + } - return true; + @Override + public void build(final LiteralArgumentBuilder builder) { + builder + .requires(src -> + src.getSender().hasPermission("extras.broadcastrainbow") + ) + .then(argument("message", greedyString()) + .executes(ctx -> { + final String mm = StringArgumentType.getString(ctx, "message"); + final String strippedTags = MINI_MESSAGE.stripTags(mm); + final Component component = MINI_MESSAGE.deserialize("" + + strippedTags); + Bukkit.broadcast(component); + return Command.SINGLE_SUCCESS; + }) + ); } -} +} \ No newline at end of file diff --git a/src/main/java/pw/kaboom/extras/commands/CommandBroadcastRaw.java b/src/main/java/pw/kaboom/extras/commands/CommandBroadcastRaw.java new file mode 100644 index 00000000..69e9e212 --- /dev/null +++ b/src/main/java/pw/kaboom/extras/commands/CommandBroadcastRaw.java @@ -0,0 +1,52 @@ +package pw.kaboom.extras.commands; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.bukkit.Bukkit; + +import java.util.List; + +import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; +import static io.papermc.paper.command.brigadier.Commands.argument; + +public final class CommandBroadcastRaw implements BrigadierCommand { + private static final LegacyComponentSerializer SERIALIZER = + LegacyComponentSerializer.legacyAmpersand(); + + + @Override + public String getLabel() { + return "broadcastraw"; + } + + @Override + public String getDescription() { + return "Broadcasts raw text to the server"; + } + + @Override + public List getAliases() { + return List.of("bcraw", "tellraw"); + } + + @Override + public void build(final LiteralArgumentBuilder builder) { + builder + .requires(src -> + src.getSender().hasPermission("extras.broadcastraw") + ) + .then(argument("message", greedyString()) + .executes(ctx -> { + Bukkit.broadcast( + SERIALIZER.deserialize( + StringArgumentType.getString(ctx, "message") + ) + ); + return Command.SINGLE_SUCCESS; + }) + ); + } +} diff --git a/src/main/java/pw/kaboom/extras/commands/CommandBroadcastVanilla.java b/src/main/java/pw/kaboom/extras/commands/CommandBroadcastVanilla.java index 455d76f8..85297857 100644 --- a/src/main/java/pw/kaboom/extras/commands/CommandBroadcastVanilla.java +++ b/src/main/java/pw/kaboom/extras/commands/CommandBroadcastVanilla.java @@ -1,58 +1,61 @@ package pw.kaboom.extras.commands; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import io.papermc.paper.command.brigadier.CommandSourceStack; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextDecoration; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.bukkit.Bukkit; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.entity.Player; -import org.jspecify.annotations.NonNull; - -import java.util.Collection; - -public final class CommandBroadcastVanilla implements CommandExecutor { - private static final LegacyComponentSerializer LEGACY_COMPONENT_SERIALIZER = - LegacyComponentSerializer - .legacyAmpersand(); - - public boolean onCommand(final @NonNull CommandSender sender, - final @NonNull Command command, - final @NonNull String label, - final String[] args) { - if (args.length == 0) { - sender.sendMessage(Component - .text("Usage: /" + label + " ", - NamedTextColor.RED)); - return true; - } - - final Component senderName = sender.name(); - final String input = String.join(" ", args); - final Component component = LEGACY_COMPONENT_SERIALIZER.deserialize(input); - final Component broadcastComponent = - Component.translatable("chat.type.admin", senderName, component) - .decorate(TextDecoration.ITALIC) - .color(NamedTextColor.GRAY); - - sender.sendMessage(component); - - final Collection onlinePlayers = Bukkit.getOnlinePlayers(); - - for (final Player onlinePlayer : onlinePlayers) { - if (onlinePlayer.equals(sender)) { - continue; - } - - onlinePlayer.sendMessage(broadcastComponent); - } - - final ConsoleCommandSender consoleCommandSender = Bukkit.getConsoleSender(); - consoleCommandSender.sendMessage(broadcastComponent); - - return true; + +import java.util.List; + +import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; +import static io.papermc.paper.command.brigadier.Commands.argument; + +public final class CommandBroadcastVanilla implements BrigadierCommand { + private static final LegacyComponentSerializer LEGACY_COMPONENT_SERIALIZER + = LegacyComponentSerializer.legacyAmpersand(); + + @Override + public String getLabel() { + return "broadcastvanilla"; + } + + @Override + public String getDescription() { + return "Broadcasts text in vanilla style"; + } + + @Override + public List getAliases() { + return List.of("bcv"); + } + + @Override + public void build(final LiteralArgumentBuilder builder) { + builder + .requires(src -> + src.getSender().hasPermission("extras.broadcastraw") + ) + .then(argument("message", greedyString()) + .executes(ctx -> { + final Component senderName = ctx.getSource().getSender().name(); + final String input = StringArgumentType.getString(ctx, "message"); + final Component component = + LEGACY_COMPONENT_SERIALIZER.deserialize(input); + final Component broadcastComponent = + Component.translatable("chat.type.admin", + senderName, + component + ) + .decorate(TextDecoration.ITALIC) + .color(NamedTextColor.GRAY); + Bukkit.broadcast(broadcastComponent); + return Command.SINGLE_SUCCESS; + }) + ); } } diff --git a/src/main/java/pw/kaboom/extras/commands/CommandClearChat.java b/src/main/java/pw/kaboom/extras/commands/CommandClearChat.java index 3c87e609..1bede220 100644 --- a/src/main/java/pw/kaboom/extras/commands/CommandClearChat.java +++ b/src/main/java/pw/kaboom/extras/commands/CommandClearChat.java @@ -1,34 +1,52 @@ package pw.kaboom.extras.commands; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import io.papermc.paper.command.brigadier.CommandSourceStack; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.jspecify.annotations.NonNull; - -public final class CommandClearChat implements CommandExecutor { - public boolean onCommand(final @NonNull CommandSender sender, - final @NonNull Command command, - final @NonNull String label, - final String[] args) { + +import java.util.List; + +public final class CommandClearChat implements BrigadierCommand { + private static final Component CLEAR_CHAT_COMPONENT; + + static { final int maxMessages = 100; Component clearChatComponent = Component.empty(); - for (int i = 0; i < maxMessages; i++) { clearChatComponent = clearChatComponent .append(Component.newline()); } + CLEAR_CHAT_COMPONENT = clearChatComponent + .append(Component.text("The chat has been cleared") + .color(NamedTextColor.DARK_GREEN)); + } - for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { - onlinePlayer.sendMessage(clearChatComponent - .append(Component - .text("The chat has been cleared", - NamedTextColor.DARK_GREEN))); - } - return true; + @Override + public String getLabel() { + return "clearchat"; + } + + @Override + public String getDescription() { + return "Clears messages from the chat"; + } + + @Override + public List getAliases() { + return List.of("cc"); + } + + @Override + public void build(final LiteralArgumentBuilder builder) { + builder + .requires(src -> src.getSender().hasPermission("extras.clearchat")) + .executes(_ -> { + Bukkit.broadcast(CLEAR_CHAT_COMPONENT); + return Command.SINGLE_SUCCESS; + }); } } diff --git a/src/main/java/pw/kaboom/extras/commands/CommandConsole.java b/src/main/java/pw/kaboom/extras/commands/CommandConsole.java index adf96416..a058b1de 100644 --- a/src/main/java/pw/kaboom/extras/commands/CommandConsole.java +++ b/src/main/java/pw/kaboom/extras/commands/CommandConsole.java @@ -1,30 +1,38 @@ package pw.kaboom.extras.commands; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import io.papermc.paper.command.brigadier.CommandSourceStack; import org.bukkit.Bukkit; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.jspecify.annotations.NonNull; import pw.kaboom.extras.util.Utility; -public final class CommandConsole implements CommandExecutor { - public boolean onCommand(final @NonNull CommandSender sender, - final @NonNull Command command, - final @NonNull String label, - final String[] args) { - if (args.length == 0) { - sender.sendMessage(Component - .text("Usage: /" + label + " ", - NamedTextColor.RED)); - return true; - } +import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; +import static io.papermc.paper.command.brigadier.Commands.argument; - Bukkit.dispatchCommand( - Bukkit.getConsoleSender(), - "minecraft:say " + Utility.translateLegacyColors(String.join(" ", args)) - ); - return true; +public final class CommandConsole implements BrigadierCommand { + @Override + public String getLabel() { + return "console"; + } + + @Override + public String getDescription() { + return "Broadcasts a message as the console"; + } + + @Override + public void build(final LiteralArgumentBuilder builder) { + builder + .requires(src -> src.getSender().hasPermission("extras.console")) + .then(argument("message", greedyString()).executes(ctx -> { + Bukkit.dispatchCommand( + Bukkit.getConsoleSender(), + "minecraft:say " + Utility.translateLegacyColors( + StringArgumentType.getString(ctx, "message") + ) + ); + return Command.SINGLE_SUCCESS; + })); } } diff --git a/src/main/java/pw/kaboom/extras/commands/CommandDestroyEntities.java b/src/main/java/pw/kaboom/extras/commands/CommandDestroyEntities.java index 115de81b..37bc4a5c 100644 --- a/src/main/java/pw/kaboom/extras/commands/CommandDestroyEntities.java +++ b/src/main/java/pw/kaboom/extras/commands/CommandDestroyEntities.java @@ -1,46 +1,62 @@ package pw.kaboom.extras.commands; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import io.papermc.paper.command.brigadier.CommandSourceStack; import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; import org.bukkit.World; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; -import org.jspecify.annotations.NonNull; -public final class CommandDestroyEntities implements CommandExecutor { +import java.util.List; + +public final class CommandDestroyEntities implements BrigadierCommand { + @Override - public boolean onCommand(final @NonNull CommandSender sender, - final @NonNull Command command, - final @NonNull String label, - final String[] args) { - int entityCount = 0; - int worldCount = 0; - - for (World world : Bukkit.getWorlds()) { - for (Entity entity : world.getEntities()) { - if (!EntityType.PLAYER.equals(entity.getType())) { - try { - entity.remove(); - entityCount++; - } catch (Exception ignored) { - // Broken entity - continue; + public String getLabel() { + return "destroyentities"; + } + + @Override + public String getDescription() { + return "Destroys all entities in every world"; + } + + @Override + public List getAliases() { + return List.of("de"); + } + + @Override + public void build(LiteralArgumentBuilder builder) { + builder + .requires(src -> src.getSender().hasPermission("extras.destroyentities")) + .executes(ctx -> { + int entityCount = 0; + int worldCount = 0; + + for (World world : Bukkit.getWorlds()) { + for (Entity entity : world.getEntities()) { + if (!EntityType.PLAYER.equals(entity.getType())) { + try { + entity.remove(); + entityCount++; + } catch (Exception _) { + // Broken entity + } + } + } + worldCount++; } - } - } - worldCount++; - } - - sender.sendMessage( - Component.text("Successfully destroyed ") - .append(Component.text(entityCount)) - .append(Component.text(" entities in ")) - .append(Component.text(worldCount)) - .append(Component.text(" worlds")) - ); - return true; + + ctx.getSource().getSender().sendMessage( + Component.text("Successfully destroyed ") + .append(Component.text(entityCount)) + .append(Component.text(" entities in ")) + .append(Component.text(worldCount)) + .append(Component.text(" worlds")) + ); + return entityCount; + }); } } diff --git a/src/main/java/pw/kaboom/extras/commands/CommandEnchantAll.java b/src/main/java/pw/kaboom/extras/commands/CommandEnchantAll.java index 381ac845..5c748a46 100644 --- a/src/main/java/pw/kaboom/extras/commands/CommandEnchantAll.java +++ b/src/main/java/pw/kaboom/extras/commands/CommandEnchantAll.java @@ -1,45 +1,64 @@ package pw.kaboom.extras.commands; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.LiteralMessage; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import io.papermc.paper.command.brigadier.MessageComponentSerializer; import io.papermc.paper.registry.RegistryAccess; import io.papermc.paper.registry.RegistryKey; import net.kyori.adventure.text.Component; import org.bukkit.Material; import org.bukkit.Registry; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import org.jspecify.annotations.NonNull; - -public final class CommandEnchantAll implements CommandExecutor { - public boolean onCommand(final @NonNull CommandSender sender, - final @NonNull Command command, - final @NonNull String label, - final String[] args) { - if (!(sender instanceof final Player player)) { - sender.sendMessage(Component - .text("Command has to be run by a player")); - return true; - } - - final ItemStack item = player.getInventory().getItemInMainHand(); - - if (Material.AIR.equals(item.getType())) { - player.sendMessage(Component - .text("Please hold an item in your hand to enchant it")); - return true; - } - - final Registry<@NotNull Enchantment> registry = RegistryAccess.registryAccess() - .getRegistry(RegistryKey.ENCHANTMENT); - for (Enchantment enchantment : registry) { - item.addUnsafeEnchantment(enchantment, Short.MAX_VALUE); - } - player.sendMessage(Component - .text("I killed Martin.")); - return true; + +public final class CommandEnchantAll implements BrigadierCommand { + private static final SimpleCommandExceptionType EX_NOT_PLAYER = + new SimpleCommandExceptionType( + new LiteralMessage("This command must be called by a player")); + private static final SimpleCommandExceptionType EX_NO_ITEM = + new SimpleCommandExceptionType( + new LiteralMessage("Please hold an item in your hand to enchant it")); + + @Override + public String getLabel() { + return "enchantall"; + } + + @Override + public String getDescription() { + return "Adds every enchantment to a held item"; + } + + @Override + public void build(final LiteralArgumentBuilder builder) { + builder + .requires(src -> + src.getSender() instanceof Player + && src.getSender().hasPermission("extras.enchantall") + ) + .executes(ctx -> { + if (!(ctx.getSource().getSender() instanceof final Player player)) { + // should be impossible, see above + throw EX_NOT_PLAYER.create(); + } + + final ItemStack item = player.getInventory().getItemInMainHand(); + + if (Material.AIR.equals(item.getType())) { + throw EX_NO_ITEM.create(); + } + + final Registry registry = RegistryAccess.registryAccess() + .getRegistry(RegistryKey.ENCHANTMENT); + for (final Enchantment enchantment : registry) { + item.addUnsafeEnchantment(enchantment, Short.MAX_VALUE); + } + player.sendMessage(Component.text("I killed Martin.")); + return Command.SINGLE_SUCCESS; + }); } } diff --git a/src/main/java/pw/kaboom/extras/commands/CommandGetJSON.java b/src/main/java/pw/kaboom/extras/commands/CommandGetJSON.java index b9b68195..933ed5da 100644 --- a/src/main/java/pw/kaboom/extras/commands/CommandGetJSON.java +++ b/src/main/java/pw/kaboom/extras/commands/CommandGetJSON.java @@ -1,39 +1,59 @@ package pw.kaboom.extras.commands; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import io.papermc.paper.command.brigadier.CommandSourceStack; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.format.NamedTextColor; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.kyori.adventure.text.serializer.json.JSONComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.jspecify.annotations.NonNull; - -public final class CommandGetJSON implements CommandExecutor { - public boolean onCommand(final @NonNull CommandSender sender, - final @NonNull Command command, - final @NonNull String label, - final String[] args) { - if (args.length == 0) { - sender.sendMessage(Component - .text("Usage: /" + label + " ", NamedTextColor.RED)); - return true; - } - - final String message = String.join(" ", args); - Component createdComponent = LegacyComponentSerializer - .legacyAmpersand() - .deserialize(message); - - String asJson = GsonComponentSerializer.gson().serialize(createdComponent); - - Component feedback = Component.empty() - .append(Component.text("Your component as JSON (click to copy): ")) - .append(Component.text(asJson, NamedTextColor.GREEN)) - .clickEvent(ClickEvent.copyToClipboard(asJson)); - - sender.sendMessage(feedback); - return true; + +import java.util.List; + +import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; +import static io.papermc.paper.command.brigadier.Commands.argument; + +public final class CommandGetJSON implements BrigadierCommand { + private static final JSONComponentSerializer SERIALIZER = JSONComponentSerializer.json(); + + @Override + public String getLabel() { + return "getjson"; + } + + @Override + public String getDescription() { + return "Gets the JSON of a deserialized legacy component"; + } + + @Override + public List getAliases() { + return List.of("gj"); + } + + @Override + public void build(final LiteralArgumentBuilder builder) { + builder + .requires(src -> src.getSender().hasPermission("extras.getjson")) + .then(argument("message", greedyString()) + .executes(ctx -> { + final Component createdComponent = LegacyComponentSerializer + .legacyAmpersand() + .deserialize(StringArgumentType.getString(ctx, "message")); + + final String asJson = SERIALIZER.serialize(createdComponent); + + final Component feedback = Component.empty() + .append(Component.text("Your component as JSON (click to " + + "copy): ")) + .append(Component.text(asJson, NamedTextColor.GREEN)) + .clickEvent(ClickEvent.copyToClipboard(asJson)); + + ctx.getSource().getSender().sendMessage(feedback); + return Command.SINGLE_SUCCESS; + }) + ); } } diff --git a/src/main/java/pw/kaboom/extras/commands/CommandGetJSONMM.java b/src/main/java/pw/kaboom/extras/commands/CommandGetJSONMM.java index 394d5ae7..fc4c9e16 100644 --- a/src/main/java/pw/kaboom/extras/commands/CommandGetJSONMM.java +++ b/src/main/java/pw/kaboom/extras/commands/CommandGetJSONMM.java @@ -1,39 +1,59 @@ package pw.kaboom.extras.commands; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import io.papermc.paper.command.brigadier.CommandSourceStack; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.jspecify.annotations.NonNull; - -public final class CommandGetJSONMM implements CommandExecutor { - public boolean onCommand(final @NonNull CommandSender sender, - final @NonNull Command command, - final @NonNull String label, - final String[] args) { - if (args.length == 0) { - sender.sendMessage(Component - .text("Usage: /" + label + " ", NamedTextColor.RED)); - return true; - } - - final String message = String.join(" ", args); - Component createdComponent = MiniMessage.miniMessage() - .deserialize(message); - - String asJson = GsonComponentSerializer.gson() - .serialize(createdComponent); - - Component feedback = Component.empty() - .append(Component.text("Your component as JSON (click to copy): ")) - .append(Component.text(asJson, NamedTextColor.GREEN)) - .clickEvent(ClickEvent.copyToClipboard(asJson)); - - sender.sendMessage(feedback); - return true; + +import java.util.List; + +import static io.papermc.paper.command.brigadier.Commands.argument; + +public final class CommandGetJSONMM implements BrigadierCommand { + @Override + public String getLabel() { + return "getjsonmm"; + } + + @Override + public String getDescription() { + return "Gets the JSON of a deserialized MiniMessage component"; + } + + @Override + public List getAliases() { + return List.of("jmm"); + } + + @Override + public void build(final LiteralArgumentBuilder builder) { + builder + .requires(src -> + src.getSender().hasPermission("extras.getjsonmm") + ) + .then(argument("message", StringArgumentType.greedyString()) + .executes(ctx -> { + final Component createdComponent = MiniMessage + .miniMessage().deserialize( + StringArgumentType.getString(ctx, "message") + ); + + final String asJson = GsonComponentSerializer.gson() + .serialize(createdComponent); + + final Component feedback = Component.empty() + .append(Component.text("Your component as JSON (click" + + " to copy): ")) + .append(Component.text(asJson, NamedTextColor.GREEN)) + .clickEvent(ClickEvent.copyToClipboard(asJson)); + + ctx.getSource().getSender().sendMessage(feedback); + return Command.SINGLE_SUCCESS; + })); } } diff --git a/src/main/java/pw/kaboom/extras/commands/CommandJumpscare.java b/src/main/java/pw/kaboom/extras/commands/CommandJumpscare.java index 8567eb17..78380700 100644 --- a/src/main/java/pw/kaboom/extras/commands/CommandJumpscare.java +++ b/src/main/java/pw/kaboom/extras/commands/CommandJumpscare.java @@ -1,17 +1,66 @@ package pw.kaboom.extras.commands; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.Bukkit; import org.bukkit.Particle; import org.bukkit.Sound; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.jspecify.annotations.NonNull; -public final class CommandJumpscare implements CommandExecutor { +import java.util.List; + +import static io.papermc.paper.command.brigadier.Commands.argument; +import static io.papermc.paper.command.brigadier.argument.ArgumentTypes.players; + +public final class CommandJumpscare implements BrigadierCommand { + @Override + public String getLabel() { + return "jumpscare"; + } + + @Override + public String getDescription() { + return "Scares a player"; + } + + @Override + public void build(final LiteralArgumentBuilder builder) { + builder + .requires(src -> + src.getSender().hasPermission("extras.jumpscare") + ) + .then(argument("players", players()) + .executes(ctx -> { + final PlayerSelectorArgumentResolver selector = ctx.getArgument( + "players", + PlayerSelectorArgumentResolver.class + ); + final List targets = selector.resolve(ctx.getSource()); + for (final Player target : targets) { + createJumpscare(target); + } + if (targets.size() == 1) { + ctx.getSource().getSender().sendMessage( + Component.text("Successfully created jumpscare for " + + "player \"") + .append(Component.text( + targets.getFirst().getName()) + ) + .append(Component.text("\"")) + ); + } else { + ctx.getSource().getSender().sendMessage( + Component.text("Successfully created jumpscare for " + + targets.size() + + " players") + ); + } + return targets.size(); + }) + ); + } + private void createJumpscare(final Player player) { final int count = 4; player.spawnParticle(Particle.ELDER_GUARDIAN, player.getLocation(), count); @@ -21,44 +70,4 @@ private void createJumpscare(final Player player) { player.playSound(player.getLocation(), Sound.ENTITY_ENDERMAN_SCREAM, 1, 0); } } - - public boolean onCommand(final @NonNull CommandSender sender, - final @NonNull Command command, - final @NonNull String label, - final String[] args) { - if (args.length == 0) { - sender.sendMessage(Component - .text("Usage: /" + label + " ", - NamedTextColor.RED)); - return true; - } - - if (args[0].equals("*") || args[0].equals("**")) { - for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { - createJumpscare(onlinePlayer); - } - sender.sendMessage(Component - .text("Successfully created jumpscare for every player")); - return true; - } - - final Player target = Bukkit.getPlayer(args[0]); - - if (target == null) { - sender.sendMessage( - Component.text("Player \"") - .append(Component.text(args[0])) - .append(Component.text("\" not found")) - ); - return true; - } - - createJumpscare(target); - sender.sendMessage( - Component.text("Successfully created jumpscare for player \"") - .append(Component.text(target.getName())) - .append(Component.text("\"")) - ); - return true; - } } diff --git a/src/main/java/pw/kaboom/extras/commands/CommandKaboom.java b/src/main/java/pw/kaboom/extras/commands/CommandKaboom.java index e733ba36..8a0bf46a 100644 --- a/src/main/java/pw/kaboom/extras/commands/CommandKaboom.java +++ b/src/main/java/pw/kaboom/extras/commands/CommandKaboom.java @@ -1,58 +1,82 @@ package pw.kaboom.extras.commands; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import io.papermc.paper.command.brigadier.MessageComponentSerializer; import net.kyori.adventure.text.Component; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -import org.jspecify.annotations.NonNull; import java.util.concurrent.ThreadLocalRandom; -public final class CommandKaboom implements CommandExecutor { +public final class CommandKaboom implements BrigadierCommand { + private static final MessageComponentSerializer SERIALIZER = + MessageComponentSerializer.message(); + private static final SimpleCommandExceptionType EX_NOT_PLAYER = + new SimpleCommandExceptionType(SERIALIZER.serialize(Component.text("This " + + "command must be called by a player"))); - public boolean onCommand(final @NonNull CommandSender sender, - final @NonNull Command command, - final @NonNull String label, - final String[] args) { - if (!(sender instanceof final Player player)) { - sender.sendMessage(Component - .text("Command has to be run by a player")); - return true; - } + @Override + public String getLabel() { + return "kaboom"; + } + + @Override + public String getDescription() { + return "I wonder..."; + } - boolean explode = ThreadLocalRandom.current().nextBoolean(); + @Override + public void build(final LiteralArgumentBuilder builder) { + builder + .requires(src -> + src.getSender().hasPermission("extras.kaboom") + && src.getSender() instanceof Player + ) + .executes(ctx -> { + if (!(ctx.getSource().getSender() instanceof final Player player)) { + throw EX_NOT_PLAYER.create(); + } + final boolean explode = ThreadLocalRandom.current().nextBoolean(); - if (explode) { - final Location location = player.getLocation(); - final World world = player.getWorld(); - final int explosionCount = 20; - final int power = 8; + if (explode) { + final Location location = player.getLocation(); + final World world = player.getWorld(); + final int explosionCount = 20; + final int power = 8; - world.createExplosion(location, power, true, true); + world.createExplosion(location, power, true, true); - for (int i = 0; i < explosionCount; i++) { - final double posX = location.getX() + ThreadLocalRandom.current().nextInt(-15, 15); - final double posY = location.getY() + ThreadLocalRandom.current().nextInt(-6, 6); - final double posZ = location.getZ() + ThreadLocalRandom.current().nextInt(-15, 15); + for (int i = 0; i < explosionCount; i++) { + final double posX = + location.getX() + ThreadLocalRandom.current().nextInt(-15 + , 15); + final double posY = + location.getY() + ThreadLocalRandom.current().nextInt(-6, + 6); + final double posZ = + location.getZ() + ThreadLocalRandom.current().nextInt(-15 + , 15); - final Location explodeLocation = new Location(world, posX, posY, posZ); - final int power2 = 4; + final Location explodeLocation = new Location(world, posX, posY, posZ); + final int power2 = 4; - world.createExplosion(explodeLocation, power2, true, true); - explodeLocation.getBlock().setType(Material.LAVA); - } + world.createExplosion(explodeLocation, power2, true, true); + explodeLocation.getBlock().setType(Material.LAVA); + } - player.sendMessage(Component.text("Forgive me :c")); - return true; - } + player.sendMessage(Component.text("Forgive me :c")); + return Command.SINGLE_SUCCESS; + } - player.getInventory().setItemInMainHand(new ItemStack(Material.CAKE)); - player.sendMessage(Component.text("Have a nice day :)")); - return true; + player.getInventory().setItemInMainHand(new ItemStack(Material.CAKE)); + player.sendMessage(Component.text("Have a nice day :)")); + return Command.SINGLE_SUCCESS; + }); } } diff --git a/src/main/java/pw/kaboom/extras/commands/CommandPing.java b/src/main/java/pw/kaboom/extras/commands/CommandPing.java index e8b146c9..46663850 100644 --- a/src/main/java/pw/kaboom/extras/commands/CommandPing.java +++ b/src/main/java/pw/kaboom/extras/commands/CommandPing.java @@ -1,66 +1,83 @@ package pw.kaboom.extras.commands; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import io.papermc.paper.command.brigadier.MessageComponentSerializer; +import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.Bukkit; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; +import net.kyori.adventure.text.format.TextColor; import org.bukkit.entity.Player; -import org.jspecify.annotations.NonNull; -public final class CommandPing implements CommandExecutor { +import java.util.List; - public boolean onCommand(final @NonNull CommandSender sender, - final @NonNull Command command, - final @NonNull String label, - final String[] args) { - Player target; +import static io.papermc.paper.command.brigadier.Commands.argument; +import static io.papermc.paper.command.brigadier.argument.ArgumentTypes.player; - if (args.length == 0) { - target = (Player) sender; - } else { - target = Bukkit.getPlayer(args[0]); - } +public final class CommandPing implements BrigadierCommand { + private static final SimpleCommandExceptionType EX_NOT_PLAYER = + new SimpleCommandExceptionType(MessageComponentSerializer.message().serialize( + Component.text("This command must be called by a player"))); - if (target == null) { - sender.sendMessage(Component - .text("Player \"" + args[0] + "\" not found")); - return true; - } + @Override + public String getLabel() { + return "ping"; + } - final int ping = target.getPing(); - final int d = (int) Math.floor((float) ping / 100); - NamedTextColor highlighting = NamedTextColor.WHITE; + @Override + public String getDescription() { + return "Gets your ping"; + } - switch (d) { - case 0: - highlighting = NamedTextColor.GREEN; - break; - case 1: - case 2: - case 3: - case 4: - highlighting = NamedTextColor.YELLOW; - break; - case 5: - highlighting = NamedTextColor.RED; - break; - default: - if (d > 5) { - highlighting = NamedTextColor.DARK_RED; - } - break; - } + @Override + public List getAliases() { + return List.of("delay", "ms"); + } - Component namePrefix = args.length == 0 - ? Component.text("Your") - : Component.text(target.getName()).append(Component.text("'s")); + @Override + public void build(final LiteralArgumentBuilder builder) { + builder + .requires(src -> src.getSender().hasPermission("extras.ping")) + .executes(ctx -> { + if (!(ctx.getSource().getSender() instanceof final Player player)) { + throw EX_NOT_PLAYER.create(); + } + final int ping = player.getPing(); + ctx.getSource().getSender().sendMessage( + Component.empty() + .append(Component.text("Your ping is ")) + .append(Component.text(ping + "ms.", getColor(ping))) + ); + return Command.SINGLE_SUCCESS; + }) + .then(argument("player", player()) + .executes(ctx -> { + final PlayerSelectorArgumentResolver resolver = ctx.getArgument( + "player", + PlayerSelectorArgumentResolver.class + ); + final Player player = resolver.resolve(ctx.getSource()).getFirst(); + final int ping = player.getPing(); + ctx.getSource().getSender().sendMessage( + Component.text(player.getName()) + .append(Component.text("'s ping is ")) + .append(Component.text(ping + "ms.", + getColor(ping))) + ); + return Command.SINGLE_SUCCESS; + }) + ); + } - sender.sendMessage(namePrefix - .append(Component.text(" ping is ")) - .append(Component.text(ping, highlighting)) - .append(Component.text("ms.", highlighting))); - return true; + private TextColor getColor(final int ping) { + final int d = ping / 100; + return switch (d) { + case 0 -> NamedTextColor.GREEN; + case 1, 2, 3, 4 -> NamedTextColor.YELLOW; + case 5 -> NamedTextColor.RED; + default -> NamedTextColor.DARK_RED; + }; } } diff --git a/src/main/java/pw/kaboom/extras/commands/CommandPrefix.java b/src/main/java/pw/kaboom/extras/commands/CommandPrefix.java index 56f767fc..cb05b7ea 100644 --- a/src/main/java/pw/kaboom/extras/commands/CommandPrefix.java +++ b/src/main/java/pw/kaboom/extras/commands/CommandPrefix.java @@ -1,55 +1,94 @@ package pw.kaboom.extras.commands; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.LiteralMessage; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import io.papermc.paper.command.brigadier.CommandSourceStack; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.jspecify.annotations.NonNull; +import org.bukkit.plugin.java.JavaPlugin; +import org.slf4j.Logger; +import pw.kaboom.extras.Main; import pw.kaboom.extras.modules.player.PlayerPrefix; -public final class CommandPrefix implements CommandExecutor { +import java.io.IOException; +import java.util.List; + +import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; +import static io.papermc.paper.command.brigadier.Commands.argument; +import static io.papermc.paper.command.brigadier.Commands.literal; + +public final class CommandPrefix implements BrigadierCommand { private static final int MAX_PREFIX_LENGTH = 1024; + private static final SimpleCommandExceptionType EX_NOT_PLAYER = + new SimpleCommandExceptionType(new LiteralMessage( + "This command must be called by a player")); + private static final SimpleCommandExceptionType EX_SOMETHING = + new SimpleCommandExceptionType(new LiteralMessage( + "Something went wrong while saving the prefix. Please check console.")); + + private static final Logger log = JavaPlugin.getPlugin(Main.class).getSLF4JLogger(); + + @Override + public String getLabel() { + return "prefix"; + } + + @Override + public String getDescription() { + return "Changes your tag"; + } + + @Override + public List getAliases() { + return List.of("rank", "tag"); + } - public boolean onCommand(final @NonNull CommandSender sender, - final @NonNull Command cmd, - final @NonNull String label, - final String[] args) { - if (!(sender instanceof final Player player)) { - sender.sendMessage(Component - .text("Command has to be run by a player")); - return true; - } - - if (args.length == 0) { - player.sendMessage(Component - .text("Usage: /" + label + " ", - NamedTextColor.RED)); - return true; - } - - try { - if ("off".equalsIgnoreCase(args[0])) { - PlayerPrefix.removePrefix(player); - player.sendMessage(Component - .text("You no longer have a tag")); - } else { - String legacyPrefix = String.join(" ", args); - if (legacyPrefix.length() > MAX_PREFIX_LENGTH) { - legacyPrefix = legacyPrefix.substring(0, MAX_PREFIX_LENGTH); - } - - final Component prefix = PlayerPrefix.setPrefix(player, legacyPrefix); - player.sendMessage(Component.text("You now have the tag: ") - .append(prefix)); - } - } catch (Exception exception) { - player.sendMessage(Component - .text("Something went wrong while saving the prefix. " + - "Please check console.")); - exception.printStackTrace(); - } - return true; + @Override + public void build(final LiteralArgumentBuilder builder) { + builder + .requires(src -> + src.getSender().hasPermission("extras.prefix") + && src.getSender() instanceof Player + ) + // TODO: bit of a code smell here with how much + // is just copied twice but I don't care right now + .then(literal("off") + .executes(ctx -> { + if (!(ctx.getSource().getSender() instanceof final Player player)) { + throw EX_NOT_PLAYER.create(); + } + try { + PlayerPrefix.removePrefix(player); + return Command.SINGLE_SUCCESS; + } catch (final IOException e) { + log.error("Exception while saving prefix", e); + throw EX_SOMETHING.create(); + } + }) + ) + .then(argument("prefix", greedyString()) + .executes(ctx -> { + if (!(ctx.getSource().getSender() instanceof final Player player)) { + throw EX_NOT_PLAYER.create(); + } + String legacyPrefix = StringArgumentType.getString(ctx, "prefix"); + if (legacyPrefix.length() > MAX_PREFIX_LENGTH) { + legacyPrefix = legacyPrefix.substring(0, MAX_PREFIX_LENGTH); + } + try { + final Component prefix = PlayerPrefix.setPrefix(player, + legacyPrefix); + player.sendMessage(Component.text("You now have the tag: ") + .append(prefix)); + return Command.SINGLE_SUCCESS; + } catch (final IOException e) { + log.error("Exception while saving prefix", e); + throw EX_SOMETHING.create(); + } + }) + ); } } diff --git a/src/main/java/pw/kaboom/extras/commands/CommandPumpkin.java b/src/main/java/pw/kaboom/extras/commands/CommandPumpkin.java index 0718c17a..c65a8284 100644 --- a/src/main/java/pw/kaboom/extras/commands/CommandPumpkin.java +++ b/src/main/java/pw/kaboom/extras/commands/CommandPumpkin.java @@ -1,57 +1,65 @@ package pw.kaboom.extras.commands; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.Bukkit; import org.bukkit.Material; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -import org.jspecify.annotations.NonNull; -public final class CommandPumpkin implements CommandExecutor { - private void placePumpkin(final Player player) { - player.getInventory().setHelmet(new ItemStack(Material.CARVED_PUMPKIN)); +import java.util.List; + +import static io.papermc.paper.command.brigadier.Commands.argument; +import static io.papermc.paper.command.brigadier.argument.ArgumentTypes.players; + +public final class CommandPumpkin implements BrigadierCommand { + + @Override + public String getLabel() { + return "pumpkin"; } - public boolean onCommand(final @NonNull CommandSender sender, - final @NonNull Command command, - final @NonNull String label, - final String[] args) { - if (args.length == 0) { - sender.sendMessage(Component - .text("Usage: /" + label + " ", - NamedTextColor.RED)); - return true; - } - - if (args[0].equals("*") || args[0].equals("**")) { - for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { - placePumpkin(onlinePlayer); - } - sender.sendMessage(Component.text("Everyone is now a pumpkin")); - return true; - } - - final Player target = Bukkit.getPlayer(args[0]); - - if (target == null) { - sender.sendMessage( - Component.text("Player \"") - .append(Component.text(args[0])) - .append(Component.text("\" not found")) - ); - return true; - } - - placePumpkin(target); - sender.sendMessage( - Component.text("Player \"") - .append(Component.text(target.getName())) - .append(Component.text("\" is now a pumpkin")) - ); - return true; + @Override + public String getDescription() { + return "Places a pumpkin on a player's head"; + } + + @Override + public void build(final LiteralArgumentBuilder builder) { + builder + .requires(src -> src.getSender().hasPermission("extras.pumpkin")) + .then(argument("players", players()) + .executes(ctx -> { + final PlayerSelectorArgumentResolver selector = ctx.getArgument( + "players", + PlayerSelectorArgumentResolver.class + ); + final List targets = selector.resolve(ctx.getSource()); + for (final Player target : targets) { + placePumpkin(target); + } + if (targets.size() == 1) { + ctx.getSource().getSender().sendMessage( + Component.text("\"") + .append(Component.text( + targets.getFirst().getName()) + ) + .append(Component.text("\" is now a pumpkin")) + ); + } else { + ctx.getSource().getSender().sendMessage( + Component.text(targets.size() + + " players are now pumpkins") + ); + } + return Command.SINGLE_SUCCESS; + }) + ); + } + + private void placePumpkin(final Player player) { + player.getInventory().setHelmet(new ItemStack(Material.CARVED_PUMPKIN)); } } diff --git a/src/main/java/pw/kaboom/extras/commands/CommandServerInfo.java b/src/main/java/pw/kaboom/extras/commands/CommandServerInfo.java index 0d7f5a50..04ffc54f 100644 --- a/src/main/java/pw/kaboom/extras/commands/CommandServerInfo.java +++ b/src/main/java/pw/kaboom/extras/commands/CommandServerInfo.java @@ -1,22 +1,34 @@ package pw.kaboom.extras.commands; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import io.papermc.paper.command.brigadier.CommandSourceStack; import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.objects.ObjectObjectImmutablePair; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.JoinConfiguration; import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; import org.jetbrains.annotations.Nullable; -import org.jspecify.annotations.NonNull; import oshi.SystemInfo; import oshi.hardware.GraphicsCard; import pw.kaboom.extras.util.Utility; import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.OperatingSystemMXBean; +import java.lang.management.RuntimeMXBean; import java.net.InetAddress; - -public final class CommandServerInfo implements CommandExecutor { +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.stream.Stream; + +public final class CommandServerInfo implements BrigadierCommand { + private static final OperatingSystemMXBean OS = + ManagementFactory.getOperatingSystemMXBean(); + private static final RuntimeMXBean RUNTIME = ManagementFactory.getRuntimeMXBean(); + private static final MemoryMXBean MEMORY = ManagementFactory.getMemoryMXBean(); private static final String[] GPU_DEVICES; private static final @Nullable String PROCESSOR_NAME; @@ -33,18 +45,18 @@ public final class CommandServerInfo implements CommandExecutor { // and we're unable to use guard clauses due to // returns not being supported in static blocks. - final @Nullable Pair hardwareInfo = Utility.composeCallable( + @Nullable final Pair hardwareInfo = Utility.composeCallable( systemInfo::getHardware, hardware -> new ObjectObjectImmutablePair<>( - hardware.getGraphicsCards() - .stream() - .map(GraphicsCard::getName) - .toArray(String[]::new), - hardware.getProcessor() - .getProcessorIdentifier() - .getName() - ) + hardware.getGraphicsCards() + .stream() + .map(GraphicsCard::getName) + .toArray(String[]::new), + hardware.getProcessor() + .getProcessorIdentifier() + .getName() + ) ); if (hardwareInfo == null) { @@ -56,93 +68,102 @@ public final class CommandServerInfo implements CommandExecutor { } } - private void sendInfoMessage(final CommandSender target, final String description, - final String value) { - target.sendMessage( - Component.text(description, NamedTextColor.GRAY) - .append(Component.text(": " + value, NamedTextColor.WHITE))); + private static final List ENTRIES = List.of( + InfoEntry.of("Hostname", () -> InetAddress.getLocalHost().getHostName()), + InfoEntry.of("IP address", () -> + InetAddress.getLocalHost().getHostAddress()), + InfoEntry.of("OS name", OS::getName), + InfoEntry.of("OS architecture", OS::getArch), + InfoEntry.of("OS version", OS::getVersion), + InfoEntry.of("Java VM", RUNTIME::getVmName), + InfoEntry.of("Java version", + () -> RUNTIME.getSpecVersion() + " " + RUNTIME.getVmVersion()), + new InfoEntry("CPU model", () -> Stream.ofNullable(PROCESSOR_NAME)), + InfoEntry.of("CPU cores", + () -> String.valueOf(Runtime.getRuntime().availableProcessors())), + InfoEntry.of("CPU load", () -> String.valueOf(OS.getSystemLoadAverage())), + new InfoEntry("GPU device", () -> Arrays.stream(GPU_DEVICES)), + InfoEntry.of("Available memory", () -> mebibytes( + MEMORY.getHeapMemoryUsage().getMax() + + MEMORY.getNonHeapMemoryUsage().getMax())), + InfoEntry.of("Heap memory usage", + () -> mebibytes(MEMORY.getHeapMemoryUsage().getUsed())), + InfoEntry.of("Non-heap memory usage", + () -> mebibytes(MEMORY.getNonHeapMemoryUsage().getUsed())), + InfoEntry.of("Total memory usage", () -> mebibytes( + MEMORY.getHeapMemoryUsage().getUsed() + + MEMORY.getNonHeapMemoryUsage().getUsed())), + InfoEntry.of("Server uptime", CommandServerInfo::uptime) + ); + + + @Override + public String getLabel() { + return "serverinfo"; } - public boolean onCommand(final @NonNull CommandSender sender, - final @NonNull Command command, - final @NonNull String label, - final String[] args) { - try { - sendInfoMessage(sender, "Hostname", - InetAddress.getLocalHost().getHostName() - ); - sendInfoMessage(sender, "IP address", - InetAddress.getLocalHost().getHostAddress() - ); - } catch (Exception ignored) { - } - - sendInfoMessage(sender, "OS name", - ManagementFactory.getOperatingSystemMXBean().getName() - ); - sendInfoMessage(sender, "OS architecture", - ManagementFactory.getOperatingSystemMXBean().getArch() - ); - sendInfoMessage(sender, "OS version", - ManagementFactory.getOperatingSystemMXBean().getVersion() - ); - sendInfoMessage(sender, "Java VM", - ManagementFactory.getRuntimeMXBean().getVmName() - ); - sendInfoMessage(sender, "Java version", - ManagementFactory.getRuntimeMXBean().getSpecVersion() - + " " - + ManagementFactory.getRuntimeMXBean().getVmVersion() - ); + @Override + public String getDescription() { + return "Shows detailed server information"; + } - if (PROCESSOR_NAME != null) { - sendInfoMessage(sender, "CPU model", PROCESSOR_NAME); - } + @Override + public List getAliases() { + return List.of("specs"); + } - sendInfoMessage(sender, "CPU cores", - String.valueOf(Runtime.getRuntime().availableProcessors()) - ); - sendInfoMessage(sender, "CPU load", - String.valueOf(ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage()) - ); + @Override + public void build(final LiteralArgumentBuilder builder) { + builder + .requires(src -> src.getSender().hasPermission("extras.serverinfo")) + .executes(ctx -> { + ctx.getSource().getSender().sendMessage(serverInfo()); + return Command.SINGLE_SUCCESS; + }); + } - for (int i = 0; i < GPU_DEVICES.length; i++) { - sendInfoMessage( - sender, - "GPU device (" + i + ")", - GPU_DEVICES[i] - ); + private record InfoEntry(String label, Callable> values) { + private static InfoEntry of(final String label, final Callable value) { + return new InfoEntry(label, () -> Stream.of(value.call())); } + } - final long heapUsage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed(); - final long nonHeapUsage = ManagementFactory.getMemoryMXBean() - .getNonHeapMemoryUsage().getUsed(); - final long memoryMax = ( - ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getMax() - + ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage().getMax() - ); - final long memoryUsage = (heapUsage + nonHeapUsage); + private static String mebibytes(final long bytes) { + return (bytes / 1024 / 1024) + " MiB"; + } - sendInfoMessage(sender, "Available memory", - (memoryMax / 1024 / 1024) + " MB" - ); - sendInfoMessage(sender, "Heap memory usage", - (heapUsage / 1024 / 1024) + " MB" - ); - sendInfoMessage(sender, "Non-heap memory usage", - (nonHeapUsage / 1024 / 1024) + " MB" - ); - sendInfoMessage(sender, "Total memory usage", - (memoryUsage / 1024 / 1024) + " MB" - ); + private static String uptime() { + final long totalSeconds = RUNTIME.getUptime() / 1000; + return (totalSeconds / 60) + " minute(s) " + + (totalSeconds % 60) + " second(s)"; + } + + private static Component infoLine(final String label, final String value) { + return Component.text(label, NamedTextColor.GRAY) + .append(Component.text(": " + value, NamedTextColor.WHITE)); + } - final long minutes = (ManagementFactory.getRuntimeMXBean().getUptime() / 1000) / 60; - final long seconds = (ManagementFactory.getRuntimeMXBean().getUptime() / 1000) % 60; + private static Component serverInfo() { + final List lines = new ArrayList<>(); + + for (final InfoEntry entry : ENTRIES) { + final List values; + + try { + values = entry.values().call().toList(); + } catch (Exception _) { + continue; + } + + if (values.size() == 1) { + lines.add(infoLine(entry.label(), values.getFirst())); + } else { + for (int i = 0; i < values.size(); i++) { + lines.add(infoLine(entry.label() + " (" + i + ")", values.get(i))); + } + } + } - sendInfoMessage(sender, "Server uptime", - minutes + " minute(s) " - + seconds + " second(s)" - ); - return true; + return Component.join(JoinConfiguration.newlines(), lines); } } diff --git a/src/main/java/pw/kaboom/extras/commands/CommandSkin.java b/src/main/java/pw/kaboom/extras/commands/CommandSkin.java index 71bba4ea..b4fecf84 100644 --- a/src/main/java/pw/kaboom/extras/commands/CommandSkin.java +++ b/src/main/java/pw/kaboom/extras/commands/CommandSkin.java @@ -1,48 +1,77 @@ package pw.kaboom.extras.commands; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.LiteralMessage; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import org.bukkit.Bukkit; import org.bukkit.entity.Player; -import org.jspecify.annotations.NonNull; import pw.kaboom.extras.modules.player.skin.SkinManager; -public final class CommandSkin implements CommandExecutor { +import java.util.List; + +import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; +import static io.papermc.paper.command.brigadier.Commands.argument; +import static io.papermc.paper.command.brigadier.Commands.literal; + +public final class CommandSkin implements BrigadierCommand { + private static final SimpleCommandExceptionType EX_NOT_PLAYER = + new SimpleCommandExceptionType( + new LiteralMessage("This command must be called by a player")); + @Override - public boolean onCommand(final @NonNull CommandSender sender, - final @NonNull Command command, - final @NonNull String label, - final String[] args) { - if (!(sender instanceof final Player player)) { - sender.sendMessage(Component - .text("Command has to be run by a player")); - return true; - } + public String getLabel() { + return "skin"; + } - if (args.length == 0) { - player.sendMessage(Component - .text("Usage: /" + label + " \n/" + label + " off", - NamedTextColor.RED)); - return true; - } + @Override + public String getDescription() { + return "Changes your skin"; + } - final String name = args[0]; + @Override + public void build(final LiteralArgumentBuilder builder) { + builder.requires(src -> + src.getSender().hasPermission("extras.skin") + && src.getSender() instanceof Player + ); - if (name.equalsIgnoreCase("off") || name.equalsIgnoreCase("remove") - || name.equalsIgnoreCase("disable")) { - SkinManager.removeSkin(player, true); - return true; + for (final String alias : List.of("off", "remove", "disable")) { + builder.then(literal(alias).executes(ctx -> { + SkinManager.removeSkin(player(ctx), true); + return Command.SINGLE_SUCCESS; + })); } - - if (name.equalsIgnoreCase("auto") || name.equalsIgnoreCase("default") - || name.equalsIgnoreCase("reset")) { - SkinManager.requestSkin(player, player.getName(), true); - return true; + for (final String alias : List.of("auto", "default", "reset")) { + builder.then(literal(alias).executes(ctx -> { + final Player player = player(ctx); + SkinManager.requestSkin(player, player.getName(), true); + return Command.SINGLE_SUCCESS; + })); } - SkinManager.requestSkin(player, name, true); - return true; + builder.then(argument("username", greedyString()) + .suggests((ctx, sb) -> { + Bukkit.getOnlinePlayers().stream() + .map(Player::getName) + .forEach(sb::suggest); + return sb.buildFuture(); + }) + .executes(ctx -> { + SkinManager.requestSkin( + player(ctx), ctx.getArgument("username", String.class), true); + return Command.SINGLE_SUCCESS; + })); + } + + private static Player player(final CommandContext ctx) + throws CommandSyntaxException { + if (ctx.getSource().getExecutor() instanceof final Player player) { + return player; + } + throw EX_NOT_PLAYER.create(); } } diff --git a/src/main/java/pw/kaboom/extras/commands/CommandSpawn.java b/src/main/java/pw/kaboom/extras/commands/CommandSpawn.java index caa3ece1..c69ee0b8 100644 --- a/src/main/java/pw/kaboom/extras/commands/CommandSpawn.java +++ b/src/main/java/pw/kaboom/extras/commands/CommandSpawn.java @@ -1,28 +1,38 @@ package pw.kaboom.extras.commands; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import io.papermc.paper.command.brigadier.CommandSourceStack; import net.kyori.adventure.text.Component; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerTeleportEvent; -import org.jspecify.annotations.NonNull; import pw.kaboom.extras.util.Utility; -public final class CommandSpawn implements CommandExecutor { - public boolean onCommand(final @NonNull CommandSender sender, - final @NonNull Command command, - final @NonNull String label, - final String[] args) { - if (!(sender instanceof final Player player)) { - sender.sendMessage(Component - .text("Command has to be run by a player")); - return true; - } +public final class CommandSpawn implements BrigadierCommand { + @Override + public String getLabel() { + return "spawn"; + } + + @Override + public String getDescription() { + return "Teleports you to spawn"; + } - Utility.teleportToSpawn(player, PlayerTeleportEvent.TeleportCause.COMMAND); - player.sendMessage(Component - .text("Successfully moved to spawn")); - return true; + @Override + public void build(final LiteralArgumentBuilder builder) { + builder + .requires(src -> + src.getSender().hasPermission("extras.spawn") + && src.getSender() instanceof Player + ) + .executes(ctx -> { + if (!(ctx.getSource().getSender() instanceof final Player player)) { + throw new IllegalStateException("This command must be called by a player"); + } + Utility.teleportToSpawn(player, PlayerTeleportEvent.TeleportCause.COMMAND); + player.sendMessage(Component.text("Successfully moved to spawn")); + return Command.SINGLE_SUCCESS; + }); } } diff --git a/src/main/java/pw/kaboom/extras/commands/CommandSpidey.java b/src/main/java/pw/kaboom/extras/commands/CommandSpidey.java index 8b729e18..51fba084 100644 --- a/src/main/java/pw/kaboom/extras/commands/CommandSpidey.java +++ b/src/main/java/pw/kaboom/extras/commands/CommandSpidey.java @@ -1,48 +1,66 @@ package pw.kaboom.extras.commands; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import io.papermc.paper.command.brigadier.MessageComponentSerializer; import net.kyori.adventure.text.Component; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.util.BlockIterator; import org.bukkit.util.Vector; -import org.jspecify.annotations.NonNull; - -public final class CommandSpidey implements CommandExecutor { - public boolean onCommand(final @NonNull CommandSender sender, - final @NonNull Command command, - final @NonNull String label, - final String[] args) { - if (!(sender instanceof final Player player)) { - sender.sendMessage(Component - .text("Command has to be run by a player")); - return true; - } - - final World world = player.getWorld(); - final Vector start = player.getEyeLocation().toVector(); - final Vector direction = player.getEyeLocation().getDirection(); - final int yOffset = 0; - final int distance = 50; - - final BlockIterator blockIterator = new BlockIterator( - world, - start, - direction, - yOffset, - distance - ); - - while (blockIterator.hasNext()) { - final Block block = blockIterator.next(); - - if (block.getType() != Material.COBWEB && !block.getType().isAir()) break; - block.setType(Material.COBWEB); - } - return true; + +public final class CommandSpidey implements BrigadierCommand { + private static final SimpleCommandExceptionType ERR_NOT_PLAYER = + new SimpleCommandExceptionType(MessageComponentSerializer.message().serialize( + Component.text("This command must be called by a player"))); + + @Override + public String getLabel() { + return "spidey"; + } + + @Override + public String getDescription() { + return "Annoying little spider..."; + } + + @Override + public void build(final LiteralArgumentBuilder builder) { + builder + .requires(src -> + src.getSender().hasPermission("extras.spidey") + && src.getSender() instanceof Player + ) + .executes(ctx -> { + if (!(ctx.getSource().getSender() instanceof final Player player)) { + throw ERR_NOT_PLAYER.create(); + } + + final World world = player.getWorld(); + final Vector start = player.getEyeLocation().toVector(); + final Vector direction = player.getEyeLocation().getDirection(); + final int yOffset = 0; + final int distance = 50; + + final BlockIterator blockIterator = new BlockIterator( + world, + start, + direction, + yOffset, + distance + ); + + while (blockIterator.hasNext()) { + final Block block = blockIterator.next(); + + if (block.getType() != Material.COBWEB && !block.getType().isAir()) break; + block.setType(Material.COBWEB); + } + return Command.SINGLE_SUCCESS; + }); } } diff --git a/src/main/java/pw/kaboom/extras/commands/CommandTellraw.java b/src/main/java/pw/kaboom/extras/commands/CommandTellraw.java deleted file mode 100644 index 61f93565..00000000 --- a/src/main/java/pw/kaboom/extras/commands/CommandTellraw.java +++ /dev/null @@ -1,32 +0,0 @@ -package pw.kaboom.extras.commands; - -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.bukkit.Bukkit; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.jspecify.annotations.NonNull; - -public final class CommandTellraw implements CommandExecutor { - public boolean onCommand(final @NonNull CommandSender sender, - final @NonNull Command command, - final @NonNull String label, - final String[] args) { - if (args.length == 0) { - sender.sendMessage(Component - .text("Usage: /" + label + " ", NamedTextColor.RED)); - return true; - } - - final Component message = LegacyComponentSerializer.legacyAmpersand() - .deserialize(String.join(" ", args)); - - for (Player onlinePlayer: Bukkit.getOnlinePlayers()) { - onlinePlayer.sendMessage(message); - } - return true; - } -} diff --git a/src/main/java/pw/kaboom/extras/commands/CommandUsername.java b/src/main/java/pw/kaboom/extras/commands/CommandUsername.java index 8e43b746..c49ebce9 100644 --- a/src/main/java/pw/kaboom/extras/commands/CommandUsername.java +++ b/src/main/java/pw/kaboom/extras/commands/CommandUsername.java @@ -1,63 +1,123 @@ package pw.kaboom.extras.commands; import com.destroystokyo.paper.profile.PlayerProfile; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.LiteralMessage; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import io.papermc.paper.command.brigadier.CommandSourceStack; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.jspecify.annotations.NonNull; import pw.kaboom.extras.util.Utility; import java.util.HashMap; +import java.util.List; import java.util.Map; -public final class CommandUsername implements CommandExecutor { +import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; +import static io.papermc.paper.command.brigadier.Commands.argument; +import static io.papermc.paper.command.brigadier.Commands.literal; + +public final class CommandUsername implements BrigadierCommand { + private static final SimpleCommandExceptionType EX_NOT_PLAYER = + new SimpleCommandExceptionType( + new LiteralMessage("This command must be called by a player") + ); + private static final SimpleCommandExceptionType EX_RATELIMIT = + new SimpleCommandExceptionType( + new LiteralMessage("Please wait a few seconds before changing your username.") + ); + private static final SimpleCommandExceptionType EX_TAKEN = + new SimpleCommandExceptionType( + new LiteralMessage("A player with that username is already logged in.") + ); + private static final SimpleCommandExceptionType EX_DEFAULT = + new SimpleCommandExceptionType( + new LiteralMessage("You already have your default username.") + ); + private static final DynamicCommandExceptionType EX_SELF = + new DynamicCommandExceptionType(name -> + new LiteralMessage("You already have the username \""+name+"\"") + ); + private static final long RATELIMIT_MILLIS = 2000; private final Map lastUsedMillis = new HashMap<>(); + private final Map originalNames = new HashMap<>(); @Override - public boolean onCommand(final @NonNull CommandSender sender, - final @NonNull Command command, - final @NonNull String label, - final String[] args) { - if (!(sender instanceof final Player player)) { - sender.sendMessage(Component - .text("Command has to be run by a player")); - return true; - } + public String getLabel() { + return "username"; + } - final String nameColor = Utility.translateLegacyColors(String.join(" ", args)); - final String name = nameColor.substring(0, Math.min(16, nameColor.length())); - final long millis = lastUsedMillis.getOrDefault(player, 0L); - final long millisDifference = System.currentTimeMillis() - millis; + @Override + public String getDescription() { + return "Changes your username on the server"; + } - if (args.length == 0) { - player.sendMessage(Component - .text("Usage: /" + label + " ", - NamedTextColor.RED)); - return true; - } + @Override + public void build(final LiteralArgumentBuilder builder) { + builder.requires(src -> + src.getSender().hasPermission("extras.username") + && src.getSender() instanceof Player + ); - if (name.equals(player.getName())) { - player.sendMessage(Component - .text("You already have the username \"" + name + "\"")); - return true; - } + for (final String alias : List.of("auto", "default", "reset")) { + builder.then(literal(alias).executes(ctx -> { + final Player player = player(ctx); + final String original = originalNames.get(player); + + if (original == null || original.equals(player.getName())) { + throw EX_DEFAULT.create(); + } - if (millisDifference <= 2000) { - player.sendMessage(Component - .text("Please wait a few seconds before changing your username.")); - return true; + setUsername(player, original); + originalNames.remove(player); + return Command.SINGLE_SUCCESS; + })); } - for (Player other : Bukkit.getOnlinePlayers()) { - if (!other.getName().equalsIgnoreCase(name)) continue; + builder.then(argument("username", greedyString()) + .executes(ctx -> { + final Player player = player(ctx); - player.sendMessage(Component - .text("A player with that username is already logged in.")); - return true; + final String nameColor = + Utility.translateLegacyColors( + StringArgumentType.getString( + ctx, + "username" + ) + ); + final String name = nameColor.substring(0, Math.min(16, + nameColor.length())); + + if (name.equals(player.getName())) { + throw EX_SELF.create(name); + } + + originalNames.putIfAbsent(player, player.getName()); + setUsername(player, name); + return Command.SINGLE_SUCCESS; + })); + } + + private void setUsername(final Player player, final String name) + throws CommandSyntaxException { + final long millis = lastUsedMillis.getOrDefault(player, 0L); + final long millisDifference = System.currentTimeMillis() - millis; + + if (millisDifference <= RATELIMIT_MILLIS) { + throw EX_RATELIMIT.create(); + } + + for (final Player other : Bukkit.getOnlinePlayers()) { + if (other.equals(player) || !other.getName().equalsIgnoreCase(name)) { + continue; + } + throw EX_TAKEN.create(); } // Preserve UUIDs, as changing them breaks clients @@ -67,10 +127,17 @@ public boolean onCommand(final @NonNull CommandSender sender, lastUsedMillis.put(player, System.currentTimeMillis()); player.sendMessage( - Component.text("Successfully set your username to \"") - .append(Component.text(name)) - .append(Component.text("\"")) + Component.text("Successfully set your username to \"") + .append(Component.text(name)) + .append(Component.text("\"")) ); - return true; + } + + private static Player player(final CommandContext ctx) + throws CommandSyntaxException { + if (ctx.getSource().getSender() instanceof final Player player) { + return player; + } + throw EX_NOT_PLAYER.create(); } } diff --git a/src/main/java/pw/kaboom/extras/commands/package-info.java b/src/main/java/pw/kaboom/extras/commands/package-info.java new file mode 100644 index 00000000..0e0b8b8a --- /dev/null +++ b/src/main/java/pw/kaboom/extras/commands/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package pw.kaboom.extras.commands; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/src/main/java/pw/kaboom/extras/modules/player/PlayerCommand.java b/src/main/java/pw/kaboom/extras/modules/player/PlayerCommand.java index cc3e5d0a..609afe47 100644 --- a/src/main/java/pw/kaboom/extras/modules/player/PlayerCommand.java +++ b/src/main/java/pw/kaboom/extras/modules/player/PlayerCommand.java @@ -13,7 +13,7 @@ import pw.kaboom.extras.modules.server.ServerCommand; public final class PlayerCommand implements Listener { - private static HashMap commandMillisList = new HashMap(); + private static final HashMap commandMillisList = new HashMap< >(); @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) void onPlayerCommandPreprocess(final PlayerCommandPreprocessEvent event) { diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 2db20c4c..357bc24b 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -2,78 +2,4 @@ name: Extras main: pw.kaboom.extras.Main description: Plugin that adds extra functionality to the server. api-version: '1.21' -version: master - -commands: - broadcastminimessage: - aliases: [ broadcastmm, bcmm ] - description: Broadcasts a deserialized MiniMessage component - permission: extras.broadcastminimessage - broadcastrainbow: - aliases: [ bcr ] - description: Broadcasts a rainbow message - permission: extras.broadcastrainbow - broadcastraw: - aliases: [ bcraw, tellraw ] - description: Broadcasts raw text to the server - permission: extras.broadcastraw - broadcastvanilla: - aliases: [ bcv ] - description: Broadcasts text in vanilla style - permission: extras.broadcastvanilla - clearchat: - aliases: [ cc ] - description: Clears messages from the chat - permission: extras.clearchat - console: - description: Broadcasts a message as the console - permission: extras.console - destroyentities: - aliases: [ de ] - description: Destroys all entities in every world - permission: extras.destroyentities - enchantall: - description: Adds every enchantment to a held item - permission: extras.enchantall - getjson: - aliases: [ gj ] - description: Gets the JSON of a deserialized legacy component - permission: extras.getjson - getjsonmm: - aliases: [ gmm ] - description: Gets the JSON of a deserialized MiniMessage component - permission: extras.getjsonmm - jumpscare: - aliases: [ scare ] - description: Scares a player - permission: extras.jumpscare - kaboom: - description: I wonder... - permission: extras.kaboom - ping: - aliases: [delay, ms] - description: Gets your ping - permission: extras.ping - prefix: - aliases: [rank, tag] - description: Changes your tag - permission: extras.prefix - pumpkin: - description: Places a pumpkin on a player's head - permission: extras.pumpkin - serverinfo: - aliases: [ specs ] - description: Shows detailed server information - permission: extras.serverinfo - skin: - description: Changes your skin - permission: extras.skin - spawn: - description: Teleports you to spawn - permission: extras.spawn - spidey: - description: Annoying little spider... - permission: extras.spidey - username: - description: Changes your username on the server - permission: extras.username +version: master \ No newline at end of file