From eddbe38e0ec3adfdf635ebf29318f79f083e47a0 Mon Sep 17 00:00:00 2001 From: Chip <65827213+ChipmunkMC@users.noreply.github.com> Date: Thu, 19 Jan 2023 17:20:10 -0500 Subject: [PATCH] commands mabe * Added commands (**might change later**) * Fixed in stringifying translatable components * Fixed the kaboom chat parser a tiny bit --- .../command/BuiltInExceptions.java | 82 +++++++++++++++++++ .../chipmunk/chipmunkbot/command/Command.java | 13 +++ .../chipmunkbot/command/CommandSource.java | 25 ++++-- .../command/CommandSyntaxErrorTypeMap.java | 66 --------------- .../chipmunkbot/command/ComponentMessage.java | 3 +- .../command/ConsoleCommandSource.java | 14 ++-- .../command/PlayerCommandSource.java | 36 ++++++++ .../chipmunkbot/commands/HelpCommand.java | 58 +++++++++++++ .../chipmunkbot/commands/RunCommand.java | 33 ++++++++ .../chipmunkbot/commands/TestCommand.java | 24 ++++++ .../plugins/ChatCommandHandler.java | 20 ++++- .../chipmunkbot/plugins/ChatPlugin.java | 10 +++ .../chipmunkbot/plugins/CommandManager.java | 62 +++++++++++++- .../plugins/CommandManagerPlugin.java | 18 ---- .../systemChat/KaboomChatParser.java | 2 +- .../{chat => util}/ComponentUtilities.java | 4 +- .../chipmunkbot/util/UUIDUtilities.java | 33 ++++++++ 17 files changed, 399 insertions(+), 104 deletions(-) create mode 100644 src/main/java/land/chipmunk/chipmunkbot/command/BuiltInExceptions.java create mode 100644 src/main/java/land/chipmunk/chipmunkbot/command/Command.java delete mode 100644 src/main/java/land/chipmunk/chipmunkbot/command/CommandSyntaxErrorTypeMap.java create mode 100644 src/main/java/land/chipmunk/chipmunkbot/command/PlayerCommandSource.java create mode 100644 src/main/java/land/chipmunk/chipmunkbot/commands/HelpCommand.java create mode 100644 src/main/java/land/chipmunk/chipmunkbot/commands/RunCommand.java create mode 100644 src/main/java/land/chipmunk/chipmunkbot/commands/TestCommand.java delete mode 100644 src/main/java/land/chipmunk/chipmunkbot/plugins/CommandManagerPlugin.java rename src/main/java/land/chipmunk/chipmunkbot/{chat => util}/ComponentUtilities.java (97%) create mode 100644 src/main/java/land/chipmunk/chipmunkbot/util/UUIDUtilities.java diff --git a/src/main/java/land/chipmunk/chipmunkbot/command/BuiltInExceptions.java b/src/main/java/land/chipmunk/chipmunkbot/command/BuiltInExceptions.java new file mode 100644 index 0000000..6c4a48a --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkbot/command/BuiltInExceptions.java @@ -0,0 +1,82 @@ +package land.chipmunk.chipmunkbot.command; + +import com.mojang.brigadier.Message; +import com.mojang.brigadier.exceptions.BuiltInExceptionProvider; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.CommandExceptionType; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; +import net.kyori.adventure.text.TextComponent; + +import java.util.Map; +import java.util.HashMap; + +public class BuiltInExceptions implements BuiltInExceptionProvider { + private static final Dynamic2CommandExceptionType DOUBLE_TOO_SMALL = new Dynamic2CommandExceptionType((found, min) -> message("argument.double.low", text(min), text(found))); + private static final Dynamic2CommandExceptionType DOUBLE_TOO_BIG = new Dynamic2CommandExceptionType((found, max) -> message("argument.double.big", text(max), text(found))); + + private static final Dynamic2CommandExceptionType FLOAT_TOO_SMALL = new Dynamic2CommandExceptionType((found, min) -> message("argument.float.low", text(min), text(found))); + private static final Dynamic2CommandExceptionType FLOAT_TOO_BIG = new Dynamic2CommandExceptionType((found, max) -> message("argument.float.big", text(max), text(found))); + + private static final Dynamic2CommandExceptionType INTEGER_TOO_SMALL = new Dynamic2CommandExceptionType((found, min) -> message("argument.integer.low", text(min), text(found))); + private static final Dynamic2CommandExceptionType INTEGER_TOO_BIG = new Dynamic2CommandExceptionType((found, max) -> message("argument.integer.big", text(max), text(found))); + + private static final Dynamic2CommandExceptionType LONG_TOO_SMALL = new Dynamic2CommandExceptionType((found, min) -> message("argument.long.low", text(min), text(found))); + private static final Dynamic2CommandExceptionType LONG_TOO_BIG = new Dynamic2CommandExceptionType((found, max) -> message("argument.long.big", text(max), text(found))); + + private static final DynamicCommandExceptionType LITERAL_INCORRECT = new DynamicCommandExceptionType(expected -> message("argument.literal.incorrect", text(expected))); + + private static final SimpleCommandExceptionType READER_EXPECTED_START_OF_QUOTE = new SimpleCommandExceptionType(message("parsing.quote.expected.start")); + private static final SimpleCommandExceptionType READER_EXPECTED_END_OF_QUOTE = new SimpleCommandExceptionType(message("parsing.quote.expected.end")); + private static final DynamicCommandExceptionType READER_INVALID_ESCAPE = new DynamicCommandExceptionType(character -> message("parsing.quote.escape", text(character))); + private static final DynamicCommandExceptionType READER_INVALID_BOOL = new DynamicCommandExceptionType(value -> message("parsing.bool.invalid", text(value))); + private static final DynamicCommandExceptionType READER_INVALID_INT = new DynamicCommandExceptionType(value -> message("parsing.int.invalid", text(value))); + private static final SimpleCommandExceptionType READER_EXPECTED_INT = new SimpleCommandExceptionType(message("parsing.int.expected")); + private static final DynamicCommandExceptionType READER_INVALID_LONG = new DynamicCommandExceptionType(value -> message("parsing.long.invalid", text(value))); + private static final SimpleCommandExceptionType READER_EXPECTED_LONG = new SimpleCommandExceptionType(message("parsing.long.expected")); + private static final DynamicCommandExceptionType READER_INVALID_DOUBLE = new DynamicCommandExceptionType(value -> message("parsing.double.invalid", text(value))); + private static final SimpleCommandExceptionType READER_EXPECTED_DOUBLE = new SimpleCommandExceptionType(message("parsing.double.expected")); + private static final DynamicCommandExceptionType READER_INVALID_FLOAT = new DynamicCommandExceptionType(value -> message("parsing.float.invalid", text(value))); + private static final SimpleCommandExceptionType READER_EXPECTED_FLOAT = new SimpleCommandExceptionType(message("parsing.float.expected")); + private static final SimpleCommandExceptionType READER_EXPECTED_BOOL = new SimpleCommandExceptionType(message("parsing.bool.expected")); + private static final DynamicCommandExceptionType READER_EXPECTED_SYMBOL = new DynamicCommandExceptionType(symbol -> message("parsing.expected", text(symbol))); + + private static final SimpleCommandExceptionType DISPATCHER_UNKNOWN_COMMAND = new SimpleCommandExceptionType(message("command.unknown.command")); + private static final SimpleCommandExceptionType DISPATCHER_UNKNOWN_ARGUMENT = new SimpleCommandExceptionType(message("command.unknown.argument")); + private static final SimpleCommandExceptionType DISPATCHER_EXPECTED_ARGUMENT_SEPARATOR = new SimpleCommandExceptionType(message("command.expected.separator")); + private static final DynamicCommandExceptionType DISPATCHER_PARSE_EXCEPTION = new DynamicCommandExceptionType(_message -> message("command.exception", text(_message))); + + @Override public Dynamic2CommandExceptionType doubleTooLow () { return DOUBLE_TOO_SMALL; } + @Override public Dynamic2CommandExceptionType doubleTooHigh () { return DOUBLE_TOO_BIG; } + @Override public Dynamic2CommandExceptionType floatTooLow () { return FLOAT_TOO_SMALL; } + @Override public Dynamic2CommandExceptionType floatTooHigh () { return FLOAT_TOO_BIG; } + @Override public Dynamic2CommandExceptionType integerTooLow () { return INTEGER_TOO_SMALL; } + @Override public Dynamic2CommandExceptionType integerTooHigh () { return INTEGER_TOO_BIG; } + @Override public Dynamic2CommandExceptionType longTooLow () { return LONG_TOO_SMALL; } + @Override public Dynamic2CommandExceptionType longTooHigh () { return LONG_TOO_BIG; } + @Override public DynamicCommandExceptionType literalIncorrect () { return LITERAL_INCORRECT; } + @Override public SimpleCommandExceptionType readerExpectedStartOfQuote () { return READER_EXPECTED_START_OF_QUOTE; } + @Override public SimpleCommandExceptionType readerExpectedEndOfQuote () { return READER_EXPECTED_END_OF_QUOTE; } + @Override public DynamicCommandExceptionType readerInvalidEscape () { return READER_INVALID_ESCAPE; } + @Override public DynamicCommandExceptionType readerInvalidBool () { return READER_INVALID_BOOL; } + @Override public DynamicCommandExceptionType readerInvalidInt () { return READER_INVALID_INT; } + @Override public SimpleCommandExceptionType readerExpectedInt () { return READER_EXPECTED_INT; } + @Override public DynamicCommandExceptionType readerInvalidLong () { return READER_INVALID_LONG; } + @Override public SimpleCommandExceptionType readerExpectedLong () { return READER_EXPECTED_LONG; } + @Override public DynamicCommandExceptionType readerInvalidDouble () { return READER_INVALID_DOUBLE; } + @Override public SimpleCommandExceptionType readerExpectedDouble () { return READER_EXPECTED_DOUBLE; } + @Override public DynamicCommandExceptionType readerInvalidFloat () { return READER_INVALID_FLOAT; } + @Override public SimpleCommandExceptionType readerExpectedFloat () { return READER_EXPECTED_FLOAT; } + @Override public SimpleCommandExceptionType readerExpectedBool () { return READER_EXPECTED_BOOL; } + @Override public DynamicCommandExceptionType readerExpectedSymbol () { return READER_EXPECTED_SYMBOL; } + @Override public SimpleCommandExceptionType dispatcherUnknownCommand () { return DISPATCHER_UNKNOWN_COMMAND; } + @Override public SimpleCommandExceptionType dispatcherUnknownArgument () { return DISPATCHER_UNKNOWN_ARGUMENT; } + @Override public SimpleCommandExceptionType dispatcherExpectedArgumentSeparator () { return DISPATCHER_EXPECTED_ARGUMENT_SEPARATOR; } + @Override public DynamicCommandExceptionType dispatcherParseException () { return DISPATCHER_PARSE_EXCEPTION; } + + private static Message message (final String key, final ComponentLike... args) { return ComponentMessage.wrap(Component.translatable(key, args)); } + private static TextComponent text (final Object text) { return Component.text(String.valueOf(text)); } +} diff --git a/src/main/java/land/chipmunk/chipmunkbot/command/Command.java b/src/main/java/land/chipmunk/chipmunkbot/command/Command.java new file mode 100644 index 0000000..615703f --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkbot/command/Command.java @@ -0,0 +1,13 @@ +package land.chipmunk.chipmunkbot.command; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import lombok.Data; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Command { + private LiteralArgumentBuilder builder; +} diff --git a/src/main/java/land/chipmunk/chipmunkbot/command/CommandSource.java b/src/main/java/land/chipmunk/chipmunkbot/command/CommandSource.java index 6f851f3..90389fd 100644 --- a/src/main/java/land/chipmunk/chipmunkbot/command/CommandSource.java +++ b/src/main/java/land/chipmunk/chipmunkbot/command/CommandSource.java @@ -1,12 +1,27 @@ package land.chipmunk.chipmunkbot.command; -import com.mojang.brigadier.Message; +import land.chipmunk.chipmunkbot.data.MutablePlayerListEntry; +import land.chipmunk.chipmunkbot.ChipmunkBot; import net.kyori.adventure.text.Component; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import lombok.Getter; +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class CommandSource { + @Getter private final ChipmunkBot client; -public interface CommandSource { // ? Should I support message objects? - void sendOutput (Component message, boolean broadcast); - void sendOutput (Component message); + public void sendOutput (Component message, boolean broadcast) {} + public void sendOutput (Component message) { sendOutput(message, true); } - Component displayName (); + public Component displayName () { return Component.empty(); } + + public MutablePlayerListEntry player () { return null; } + + public MutablePlayerListEntry playerOrThrow () { + MutablePlayerListEntry player = player(); + // if (player == null) throw new CommandSyntaxException(Component.text("Command must be executed by a player")); + return player; + } } diff --git a/src/main/java/land/chipmunk/chipmunkbot/command/CommandSyntaxErrorTypeMap.java b/src/main/java/land/chipmunk/chipmunkbot/command/CommandSyntaxErrorTypeMap.java deleted file mode 100644 index b42be54..0000000 --- a/src/main/java/land/chipmunk/chipmunkbot/command/CommandSyntaxErrorTypeMap.java +++ /dev/null @@ -1,66 +0,0 @@ -package land.chipmunk.chipmunkbot.command; - -import com.mojang.brigadier.Message; -import com.mojang.brigadier.exceptions.BuiltInExceptionProvider; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.exceptions.CommandExceptionType; -import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; -import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; -import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.ComponentLike; -import net.kyori.adventure.text.TextComponent; - -import java.util.Map; -import java.util.HashMap; - -public class CommandSyntaxErrorTypeMap { - private CommandSyntaxErrorTypeMap () { - } - - // Just some methods for stuff repeated a lot - private static Message message (final String key, final ComponentLike... args) { return ComponentMessage.wrap(Component.translatable(key, args)); } - private static TextComponent text (final Object text) { return Component.text(String.valueOf(text)); } - private static TextComponent text (final int text) { return Component.text(text); } - private static TextComponent text (final char text) { return Component.text(String.valueOf(text)); } - - public static Map map = new HashMap<>(); - - static { - final BuiltInExceptionProvider builtIn = CommandSyntaxException.BUILT_IN_EXCEPTIONS; - - map.put(builtIn.doubleTooLow(), new Dynamic2CommandExceptionType((found, min) -> message("argument.double.low", text(min), text(found)))); - map.put(builtIn.doubleTooHigh(), new Dynamic2CommandExceptionType((found, max) -> message("argument.double.big", text(max), text(found)))); - - map.put(builtIn.floatTooLow(), new Dynamic2CommandExceptionType((found, min) -> message("argument.float.low", text(min), text(found)))); - map.put(builtIn.floatTooHigh(), new Dynamic2CommandExceptionType((found, max) -> message("argument.float.big", text(max), text(found)))); - - map.put(builtIn.integerTooLow(), new Dynamic2CommandExceptionType((found, min) -> message("argument.integer.low", text(min), text(found)))); - map.put(builtIn.integerTooHigh(), new Dynamic2CommandExceptionType((found, max) -> message("argument.integer.big", text(max), text(found)))); - - map.put(builtIn.longTooLow(), new Dynamic2CommandExceptionType((found, min) -> message("argument.long.low", text(min), text(found)))); - map.put(builtIn.longTooHigh(), new Dynamic2CommandExceptionType((found, max) -> message("argument.long.big", text(max), text(found)))); - - map.put(builtIn.literalIncorrect(), new DynamicCommandExceptionType(expected -> message("argument.literal.incorrect", text(expected)))); - - map.put(builtIn.readerExpectedStartOfQuote(), new SimpleCommandExceptionType(message("parsing.quote.expected.start"))); - map.put(builtIn.readerExpectedEndOfQuote(), new SimpleCommandExceptionType(message("parsing.quote.expected.end"))); - map.put(builtIn.readerInvalidEscape(), new DynamicCommandExceptionType(character -> message("parsing.quote.escape", text(character)))); - map.put(builtIn.readerInvalidBool(), new DynamicCommandExceptionType(value -> message("parsing.bool.invalid", text(value)))); - map.put(builtIn.readerInvalidInt(), new DynamicCommandExceptionType(value -> message("parsing.int.invalid", text(value)))); - map.put(builtIn.readerExpectedInt(), new SimpleCommandExceptionType(message("parsing.int.expected"))); - map.put(builtIn.readerInvalidLong(), new DynamicCommandExceptionType(value -> message("parsing.long.invalid", text(value)))); - map.put(builtIn.readerExpectedLong(), new SimpleCommandExceptionType(message("parsing.long.expected"))); - map.put(builtIn.readerInvalidDouble(), new DynamicCommandExceptionType(value -> message("parsing.double.invalid", text(value)))); - map.put(builtIn.readerExpectedDouble(), new SimpleCommandExceptionType(message("parsing.double.expected"))); - map.put(builtIn.readerInvalidFloat(), new DynamicCommandExceptionType(value -> message("parsing.float.invalid", text(value)))); - map.put(builtIn.readerExpectedFloat(), new SimpleCommandExceptionType(message("parsing.float.expected"))); - map.put(builtIn.readerExpectedBool(), new SimpleCommandExceptionType(message("parsing.bool.expected"))); - map.put(builtIn.readerExpectedSymbol(), new DynamicCommandExceptionType(symbol -> message("parsing.expected", text(symbol)))); - - map.put(builtIn.dispatcherUnknownCommand(), new SimpleCommandExceptionType(message("command.unknown.command"))); - map.put(builtIn.dispatcherUnknownArgument(), new SimpleCommandExceptionType(message("command.unknown.argument"))); - map.put(builtIn.dispatcherExpectedArgumentSeparator(), new SimpleCommandExceptionType(message("command.expected.separator"))); - map.put(builtIn.dispatcherParseException(), new SimpleCommandExceptionType(message("command.exception"))); - } -} diff --git a/src/main/java/land/chipmunk/chipmunkbot/command/ComponentMessage.java b/src/main/java/land/chipmunk/chipmunkbot/command/ComponentMessage.java index 25bd419..f4f29ff 100644 --- a/src/main/java/land/chipmunk/chipmunkbot/command/ComponentMessage.java +++ b/src/main/java/land/chipmunk/chipmunkbot/command/ComponentMessage.java @@ -1,5 +1,6 @@ package land.chipmunk.chipmunkbot.command; +import land.chipmunk.chipmunkbot.util.ComponentUtilities; import net.kyori.adventure.text.Component; import com.mojang.brigadier.Message; import lombok.Getter; @@ -16,7 +17,7 @@ public class ComponentMessage implements Message { } public String getString () { - return component.toString(); // ? Is this the best way to get the string? + return ComponentUtilities.stringify(component); } public String toString () { diff --git a/src/main/java/land/chipmunk/chipmunkbot/command/ConsoleCommandSource.java b/src/main/java/land/chipmunk/chipmunkbot/command/ConsoleCommandSource.java index 1b19a37..c3cbec4 100644 --- a/src/main/java/land/chipmunk/chipmunkbot/command/ConsoleCommandSource.java +++ b/src/main/java/land/chipmunk/chipmunkbot/command/ConsoleCommandSource.java @@ -1,18 +1,18 @@ package land.chipmunk.chipmunkbot.command; -import com.mojang.brigadier.Message; +import land.chipmunk.chipmunkbot.ChipmunkBot; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; + +public class ConsoleCommandSource extends CommandSource { + public ConsoleCommandSource (ChipmunkBot client) { super(client); } -public class ConsoleCommandSource implements CommandSource { @Override public void sendOutput (Component message, boolean broadcast) { System.out.println(message); - // TODO: broadcast - } - @Override - public void sendOutput (Component message) { - sendOutput(message, true); + if (broadcast) client().chat().tellraw(Component.translatable("chat.type.admin", displayName(), message).color(NamedTextColor.GRAY).decoration(TextDecoration.ITALIC, true)); } @Override diff --git a/src/main/java/land/chipmunk/chipmunkbot/command/PlayerCommandSource.java b/src/main/java/land/chipmunk/chipmunkbot/command/PlayerCommandSource.java new file mode 100644 index 0000000..973aa59 --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkbot/command/PlayerCommandSource.java @@ -0,0 +1,36 @@ +package land.chipmunk.chipmunkbot.command; + +import land.chipmunk.chipmunkbot.data.MutablePlayerListEntry; +import land.chipmunk.chipmunkbot.util.UUIDUtilities; +import land.chipmunk.chipmunkbot.ChipmunkBot; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import java.util.UUID; + +public class PlayerCommandSource extends CommandSource { + private final MutablePlayerListEntry player; + + public PlayerCommandSource (ChipmunkBot client, MutablePlayerListEntry entry) { + super(client); + player = entry; + } + + @Override + public void sendOutput (Component message, boolean broadcast) { + final UUID uuid = player.profile().getId(); + + client().chat().tellraw(message, uuid); + if (broadcast) client().chat().tellraw(Component.translatable("chat.type.admin", displayName(), message).color(NamedTextColor.GRAY).decoration(TextDecoration.ITALIC, true), UUIDUtilities.exclusiveSelector(uuid)); + } + + @Override + public Component displayName () { + final Component explicitDisplayName = player.displayName(); + final Component displayName = explicitDisplayName != null ? explicitDisplayName : Component.text(player.profile().getName()); + + return displayName; + } + + @Override public MutablePlayerListEntry player () { return this.player; } +} diff --git a/src/main/java/land/chipmunk/chipmunkbot/commands/HelpCommand.java b/src/main/java/land/chipmunk/chipmunkbot/commands/HelpCommand.java new file mode 100644 index 0000000..31f7c75 --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkbot/commands/HelpCommand.java @@ -0,0 +1,58 @@ +package land.chipmunk.chipmunkbot.commands; + +import land.chipmunk.chipmunkbot.ChipmunkBot; +import land.chipmunk.chipmunkbot.command.*; +import static land.chipmunk.chipmunkbot.plugins.CommandManager.literal; +import static land.chipmunk.chipmunkbot.plugins.CommandManager.argument; +import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; +import static com.mojang.brigadier.arguments.StringArgumentType.getString; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.tree.CommandNode; +import com.mojang.brigadier.context.CommandContext; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.JoinConfiguration; +import java.util.List; +import java.util.ArrayList; + +public class HelpCommand extends Command { + public HelpCommand () { + super(); + + this.builder( + literal("help") + .executes(this::sendCommandList) + ); + } + + public int sendCommandList (CommandContext context) { + final CommandSource source = context.getSource(); + final ChipmunkBot client = source.client(); + + final CommandDispatcher dispatcher = client.commandManager().dispatcher(); + + source.sendOutput(generateCommandList(dispatcher), false); + + return 1; + } + + public Component generateCommandList (CommandDispatcher dispatcher) { + final List list = new ArrayList<>(); + + for (CommandNode node : dispatcher.getRoot().getChildren()) { + final String name = node.getName(); + final List usages = new ArrayList<>(); + + for (String usage : dispatcher.getAllUsage(node, null, false)) { + final String text = (name + " " + usage).trim(); + usages.add(Component.text(text)); + } + + final HoverEvent hoverEvent = HoverEvent.showText(Component.join(JoinConfiguration.separator(Component.newline()), usages)); + + list.add(Component.text(name).hoverEvent(hoverEvent)); + } + + return Component.translatable("Commands - %s", Component.join(JoinConfiguration.separator(Component.space()), list)); + } +} diff --git a/src/main/java/land/chipmunk/chipmunkbot/commands/RunCommand.java b/src/main/java/land/chipmunk/chipmunkbot/commands/RunCommand.java new file mode 100644 index 0000000..cfe2b1f --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkbot/commands/RunCommand.java @@ -0,0 +1,33 @@ +package land.chipmunk.chipmunkbot.commands; + +import land.chipmunk.chipmunkbot.ChipmunkBot; +import land.chipmunk.chipmunkbot.command.*; +import static land.chipmunk.chipmunkbot.plugins.CommandManager.literal; +import static land.chipmunk.chipmunkbot.plugins.CommandManager.argument; +import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; +import static com.mojang.brigadier.arguments.StringArgumentType.getString; +import com.mojang.brigadier.context.CommandContext; +import net.kyori.adventure.text.Component; + +public class RunCommand extends Command { + public RunCommand () { + super(); + + this.builder( + literal("run") + .then( + argument("command", greedyString()) + .executes(this::run) + ) + ); + } + + public int run (CommandContext context) { + final CommandSource source = context.getSource(); + final ChipmunkBot client = source.client(); + + client.core().run(getString(context, "command")); + + return 1; + } +} diff --git a/src/main/java/land/chipmunk/chipmunkbot/commands/TestCommand.java b/src/main/java/land/chipmunk/chipmunkbot/commands/TestCommand.java new file mode 100644 index 0000000..45758a5 --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkbot/commands/TestCommand.java @@ -0,0 +1,24 @@ +package land.chipmunk.chipmunkbot.commands; + +import land.chipmunk.chipmunkbot.command.*; +import static land.chipmunk.chipmunkbot.plugins.CommandManager.literal; +import com.mojang.brigadier.context.CommandContext; +import net.kyori.adventure.text.Component; + +public class TestCommand extends Command { + public TestCommand () { + super(); + + this.builder( + literal("test") + .executes(this::helloWorld) + ); + } + + public int helloWorld (CommandContext context) { + CommandSource source = context.getSource(); + source.sendOutput(Component.text("Hello, world!")); + + return 1; + } +} diff --git a/src/main/java/land/chipmunk/chipmunkbot/plugins/ChatCommandHandler.java b/src/main/java/land/chipmunk/chipmunkbot/plugins/ChatCommandHandler.java index d5303f0..c453d72 100644 --- a/src/main/java/land/chipmunk/chipmunkbot/plugins/ChatCommandHandler.java +++ b/src/main/java/land/chipmunk/chipmunkbot/plugins/ChatCommandHandler.java @@ -2,11 +2,17 @@ package land.chipmunk.chipmunkbot.plugins; import land.chipmunk.chipmunkbot.ChipmunkBot; import land.chipmunk.chipmunkbot.command.CommandSource; +import land.chipmunk.chipmunkbot.command.PlayerCommandSource; +import land.chipmunk.chipmunkbot.command.ComponentMessage; import land.chipmunk.chipmunkbot.plugins.ChatPlugin; import land.chipmunk.chipmunkbot.data.chat.PlayerMessage; -import land.chipmunk.chipmunkbot.chat.ComponentUtilities; +import land.chipmunk.chipmunkbot.util.ComponentUtilities; +import land.chipmunk.chipmunkbot.plugins.CommandManager; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.Message; import lombok.Getter; import lombok.Setter; @@ -27,6 +33,16 @@ public class ChatCommandHandler extends ChatPlugin.Listener { if (!contentsString.startsWith(prefix)) return; final String commandString = contentsString.substring(prefix.length()); - // System.out.println(ComponentUtilities.stringify(message.parameters().get("sender")) + " issued the command " + commandString); + final PlayerCommandSource source = new PlayerCommandSource(client, message.sender()); + + try { + CommandDispatcher dispatcher = client.commandManager().dispatcher(); + dispatcher.execute(commandString, source); + } catch (CommandSyntaxException exception) { + CommandManager.sendException(source, exception); + } catch (Exception exception) { + exception.printStackTrace(); + source.sendOutput(Component.translatable("command.failed", NamedTextColor.RED), false); + } } } diff --git a/src/main/java/land/chipmunk/chipmunkbot/plugins/ChatPlugin.java b/src/main/java/land/chipmunk/chipmunkbot/plugins/ChatPlugin.java index b4237f9..7dbe00b 100644 --- a/src/main/java/land/chipmunk/chipmunkbot/plugins/ChatPlugin.java +++ b/src/main/java/land/chipmunk/chipmunkbot/plugins/ChatPlugin.java @@ -3,6 +3,7 @@ package land.chipmunk.chipmunkbot.plugins; import land.chipmunk.chipmunkbot.ChipmunkBot; import land.chipmunk.chipmunkbot.data.chat.PlayerMessage; import land.chipmunk.chipmunkbot.data.chat.SystemChatParser; +import land.chipmunk.chipmunkbot.util.UUIDUtilities; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundPlayerChatPacket; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundSystemChatPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundChatPacket; @@ -12,9 +13,11 @@ import com.github.steveice10.packetlib.Session; import com.github.steveice10.packetlib.event.session.SessionAdapter; import com.github.steveice10.packetlib.event.session.SessionListener; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import java.util.BitSet; import java.util.ArrayList; import java.util.List; +import java.util.UUID; import java.time.Instant; import land.chipmunk.chipmunkbot.systemChat.*; @@ -67,6 +70,13 @@ public class ChatPlugin extends SessionAdapter { } } + // ? Should this be here? + public void tellraw (Component message, String targets) { + client.core.run("minecraft:tellraw " + targets + " " + GsonComponentSerializer.gson().serialize(message)); + } + public void tellraw (Component message) { tellraw(message, "@a"); } + public void tellraw (Component message, UUID uuid) { tellraw(message, UUIDUtilities.selector(uuid)); } + public void addListener (Listener listener) { listeners.add(listener); } diff --git a/src/main/java/land/chipmunk/chipmunkbot/plugins/CommandManager.java b/src/main/java/land/chipmunk/chipmunkbot/plugins/CommandManager.java index 273229a..32e5954 100644 --- a/src/main/java/land/chipmunk/chipmunkbot/plugins/CommandManager.java +++ b/src/main/java/land/chipmunk/chipmunkbot/plugins/CommandManager.java @@ -1,19 +1,77 @@ package land.chipmunk.chipmunkbot.plugins; import land.chipmunk.chipmunkbot.ChipmunkBot; +import land.chipmunk.chipmunkbot.command.Command; import land.chipmunk.chipmunkbot.command.CommandSource; -import land.chipmunk.chipmunkbot.chat.ComponentUtilities; +import land.chipmunk.chipmunkbot.command.ComponentMessage; +import land.chipmunk.chipmunkbot.command.BuiltInExceptions; +import land.chipmunk.chipmunkbot.commands.*; 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.event.ClickEvent; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.Message; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import lombok.Getter; import lombok.Setter; public class CommandManager { private ChipmunkBot client; @Getter @Setter private CommandDispatcher dispatcher = new CommandDispatcher<>(); - @Getter @Setter private String prefix = "'"; // TODO: Don't hardcode this + private final Command[] commands = {new TestCommand(), new HelpCommand(), new RunCommand()}; + + static { + // ? Is messing with static properties a good idea? + CommandSyntaxException.BUILT_IN_EXCEPTIONS = new BuiltInExceptions(); + } public CommandManager (ChipmunkBot client) { this.client = client; + + for (Command command : commands) dispatcher.register(command.builder()); } + + public static void sendException (CommandSource source, CommandSyntaxException exception) { + final Message message = exception.getRawMessage(); + Component component; + if (message instanceof ComponentMessage) component = ((ComponentMessage) message).component(); + else component = Component.text(message.getString()); + + source.sendOutput(component.color(NamedTextColor.RED), false); + + final Component context = getContext(exception); + if (context != null) source.sendOutput(context, false); + } + + public static Component getContext (CommandSyntaxException exception) { + final int _cursor = exception.getCursor(); + final String input = exception.getInput(); + + if (input == null || _cursor < 0) { + return null; + } + Component component = Component.empty() + .color(NamedTextColor.GRAY) + .clickEvent(ClickEvent.suggestCommand(input)); + + final int cursor = Math.min(input.length(), _cursor); + + if (cursor > CommandSyntaxException.CONTEXT_AMOUNT) { + component = component.append(Component.text("...")); + } + + component = component + .append(Component.text(input.substring(Math.max(0, cursor - CommandSyntaxException.CONTEXT_AMOUNT), cursor))) + .append(Component.text(input.substring(cursor), NamedTextColor.RED).decoration(TextDecoration.UNDERLINED, true)) + .append(Component.translatable("command.context.here", NamedTextColor.RED).decoration(TextDecoration.ITALIC, true)); + + return Component.empty().color(NamedTextColor.RED).append(component); // ? Should I Impostor Notchian here? + } + + public static LiteralArgumentBuilder literal(String name) { return LiteralArgumentBuilder.literal(name); } + public static RequiredArgumentBuilder argument(String name, ArgumentType type) { return RequiredArgumentBuilder.argument(name, type); } } diff --git a/src/main/java/land/chipmunk/chipmunkbot/plugins/CommandManagerPlugin.java b/src/main/java/land/chipmunk/chipmunkbot/plugins/CommandManagerPlugin.java deleted file mode 100644 index c19edaf..0000000 --- a/src/main/java/land/chipmunk/chipmunkbot/plugins/CommandManagerPlugin.java +++ /dev/null @@ -1,18 +0,0 @@ -package land.chipmunk.chipmunkbot.plugins; - -import land.chipmunk.chipmunkbot.ChipmunkBot; -import land.chipmunk.chipmunkbot.command.CommandSource; -import land.chipmunk.chipmunkbot.chat.ComponentUtilities; -import net.kyori.adventure.text.Component; -import com.mojang.brigadier.CommandDispatcher; -import lombok.Getter; -import lombok.Setter; - -public class CommandManagerPlugin { - private ChipmunkBot client; - @Getter @Setter private CommandDispatcher dispatcher = new CommandDispatcher<>(); - - public CommandManagerPlugin (ChipmunkBot client) { - this.client = client; - } -} diff --git a/src/main/java/land/chipmunk/chipmunkbot/systemChat/KaboomChatParser.java b/src/main/java/land/chipmunk/chipmunkbot/systemChat/KaboomChatParser.java index 74c0cc7..ca1b537 100644 --- a/src/main/java/land/chipmunk/chipmunkbot/systemChat/KaboomChatParser.java +++ b/src/main/java/land/chipmunk/chipmunkbot/systemChat/KaboomChatParser.java @@ -35,7 +35,7 @@ public class KaboomChatParser implements SystemChatParser { } public PlayerMessage parse (TranslatableComponent message) { - if (!message.key().equals("%") || message.args() == null || message.args().size() != 1 || !message.style().equals(empty)) return parse(message.args().get(0)); + if (message.key().equals("%s") && message.args() != null && message.args().size() == 1 && message.style().equals(empty)) return parse(message.args().get(0)); return null; } diff --git a/src/main/java/land/chipmunk/chipmunkbot/chat/ComponentUtilities.java b/src/main/java/land/chipmunk/chipmunkbot/util/ComponentUtilities.java similarity index 97% rename from src/main/java/land/chipmunk/chipmunkbot/chat/ComponentUtilities.java rename to src/main/java/land/chipmunk/chipmunkbot/util/ComponentUtilities.java index d746295..e2488c7 100644 --- a/src/main/java/land/chipmunk/chipmunkbot/chat/ComponentUtilities.java +++ b/src/main/java/land/chipmunk/chipmunkbot/util/ComponentUtilities.java @@ -1,4 +1,4 @@ -package land.chipmunk.chipmunkbot.chat; +package land.chipmunk.chipmunkbot.util; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -80,7 +80,7 @@ public class ComponentUtilities { matcher.appendReplacement(sb, "%"); } else { String idxStr = matcher.group(1); - int idx = (idxStr == null ? i++ : Integer.parseInt(idxStr)) - 1; + int idx = idxStr == null ? i++ : (Integer.parseInt(idxStr) - 1); if (idx >= 0 && idx < message.args().size()) { matcher.appendReplacement(sb, Matcher.quoteReplacement( stringify(message.args().get(idx)) )); } else { diff --git a/src/main/java/land/chipmunk/chipmunkbot/util/UUIDUtilities.java b/src/main/java/land/chipmunk/chipmunkbot/util/UUIDUtilities.java new file mode 100644 index 0000000..ed3645e --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkbot/util/UUIDUtilities.java @@ -0,0 +1,33 @@ +package land.chipmunk.chipmunkbot.util; + +import com.github.steveice10.opennbt.tag.builtin.IntArrayTag; +import java.util.UUID; +import java.nio.ByteBuffer; + +public class UUIDUtilities { + private UUIDUtilities () { + } + + public static int[] intArray (UUID uuid) { + final ByteBuffer buffer = ByteBuffer.wrap(new byte[16]); + buffer.putLong(0, uuid.getMostSignificantBits()); + buffer.putLong(8, uuid.getLeastSignificantBits()); + + final int[] intArray = new int[4]; + for (int i = 0; i < intArray.length; i++) intArray[i] = buffer.getInt(); + + return intArray; + } + + public static IntArrayTag tag (UUID uuid) { + return new IntArrayTag("", intArray(uuid)); + } + + public static String snbt (UUID uuid) { + int[] array = intArray(uuid); + return "[I;" + array[0] + "," + array[1] + "," + array[2] + "," + array[3] + "]"; // TODO: improve lol + } + + public static String selector (UUID uuid) { return "@a[limit=1,nbt={UUID:" + snbt(uuid) + "}]"; } + public static String exclusiveSelector (UUID uuid) { return "@a[nbt=!{UUID:" + snbt(uuid) + "}]"; } +}