diff --git a/src/main/java/land/chipmunk/chipmunkbot/ChipmunkBot.java b/src/main/java/land/chipmunk/chipmunkbot/ChipmunkBot.java index 87d118d..d441977 100644 --- a/src/main/java/land/chipmunk/chipmunkbot/ChipmunkBot.java +++ b/src/main/java/land/chipmunk/chipmunkbot/ChipmunkBot.java @@ -13,8 +13,9 @@ public class ChipmunkBot extends Client { @Getter private final TabCompletePlugin tabComplete; @Getter private final QueryPlugin query; @Getter private final PlayerListPlugin playerList; + @Getter private final CommandSpyPlugin commandSpy; @Getter private final CommandManager commandManager; - @Getter private final ChatCommandHandler chatCommandHandler; + @Getter private final PlayerCommandHandler playerCommandHandler; @Getter private final PositionManager position; @Getter private final CommandCore core; @Getter private final SelfCarePlugin selfCare; @@ -30,8 +31,9 @@ public class ChipmunkBot extends Client { this.tabComplete = new TabCompletePlugin(this); this.query = new QueryPlugin(this); this.playerList = new PlayerListPlugin(this); + this.commandSpy = new CommandSpyPlugin(this); this.commandManager = new CommandManager(this); - this.chatCommandHandler = new ChatCommandHandler(this, options); + this.playerCommandHandler = new PlayerCommandHandler(this, options); this.position = new PositionManager(this); this.core = new CommandCore(this, options); this.selfCare = new SelfCarePlugin(this); diff --git a/src/main/java/land/chipmunk/chipmunkbot/commands/MusicCommand.java b/src/main/java/land/chipmunk/chipmunkbot/commands/MusicCommand.java index 3ac5c53..e27eba2 100644 --- a/src/main/java/land/chipmunk/chipmunkbot/commands/MusicCommand.java +++ b/src/main/java/land/chipmunk/chipmunkbot/commands/MusicCommand.java @@ -149,7 +149,7 @@ public class MusicCommand { public int list (CommandContext context, Path path) throws CommandSyntaxException { final CommandSource source = context.getSource(); final ChipmunkBot client = source.client(); - final String prefix = client.chatCommandHandler().prefix(); + final String prefix = client.playerCommandHandler().prefix(); final File directory = path.toFile(); final String[] filenames = directory.list(); diff --git a/src/main/java/land/chipmunk/chipmunkbot/plugins/ChatCommandHandler.java b/src/main/java/land/chipmunk/chipmunkbot/plugins/ChatCommandHandler.java deleted file mode 100644 index 68f1e16..0000000 --- a/src/main/java/land/chipmunk/chipmunkbot/plugins/ChatCommandHandler.java +++ /dev/null @@ -1,45 +0,0 @@ -package land.chipmunk.chipmunkbot.plugins; - -import land.chipmunk.chipmunkbot.ChipmunkBot; -import land.chipmunk.chipmunkbot.Configuration; -import land.chipmunk.chipmunkbot.command.PlayerCommandSource; -import land.chipmunk.chipmunkbot.data.chat.PlayerMessage; -import land.chipmunk.chipmunkbot.util.ComponentUtilities; -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 lombok.Getter; -import lombok.Setter; - -public class ChatCommandHandler extends ChatPlugin.Listener { - private ChipmunkBot client; - @Getter @Setter private String prefix; - - public ChatCommandHandler (ChipmunkBot client, Configuration.Bot options) { - this.client = client; - this.prefix = options.commands.prefix; - client.chat().addListener((ChatPlugin.Listener) this); - } - - @Override - public void playerMessageReceived (PlayerMessage message) { - final Component contents = message.contents(); - if (contents == null) return; - final String contentsString = ComponentUtilities.stringify(contents); - if (!contentsString.startsWith(prefix)) return; - final String commandString = contentsString.substring(prefix.length()); - - 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/CommandSpyPlugin.java b/src/main/java/land/chipmunk/chipmunkbot/plugins/CommandSpyPlugin.java new file mode 100644 index 0000000..0e97f9c --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkbot/plugins/CommandSpyPlugin.java @@ -0,0 +1,88 @@ +package land.chipmunk.chipmunkbot.plugins; + +import land.chipmunk.chipmunkbot.ChipmunkBot; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.NamedTextColor; +import land.chipmunk.chipmunkbot.data.MutablePlayerListEntry; +import lombok.Getter; +import lombok.Setter; +import java.util.List; +import java.util.ArrayList; + +public class CommandSpyPlugin extends ChatPlugin.Listener { + private final ChipmunkBot client; + @Getter private List listeners = new ArrayList<>(); + @Getter @Setter private boolean enabled = false; + + private static final Style ENABLED_STYLE = Style.style(NamedTextColor.YELLOW); + private static final Style DISABLED_STYLE = Style.style(NamedTextColor.AQUA); + + private static final Component COMMANDSPY_ENABLED_COMPONENT = Component.text("Successfully enabled CommandSpy"); + private static final Component COMMANDSPY_DISABLED_COMPONENT = Component.text("Successfully disabled CommandSpy"); + private static final Component COMMAND_SEPARATOR_COMPONENT = Component.text(": "); + private static final Component SIGN_CREATED_TEXT_COMPONENT = Component.text(" created a sign with contents:"); + private static final Component SIGN_LINE_SEPARATOR_COMPONENT = Component.text("\n "); + + public CommandSpyPlugin (ChipmunkBot client) { + this.client = client; + client.chat().addListener((ChatPlugin.Listener) this); + } + + @Override + public void systemMessageReceived (Component component, boolean overlay) { + if (overlay || !(component instanceof final TextComponent t_component)) return; + + if (component.equals(COMMANDSPY_ENABLED_COMPONENT)) { this.enabled = true; return; } + if (component.equals(COMMANDSPY_DISABLED_COMPONENT)) { this.enabled = false; return; } + + final boolean enabled = component.style().equals(ENABLED_STYLE); + if (!enabled && !component.style().equals(DISABLED_STYLE)) return; + + final String username = t_component.content(); + final MutablePlayerListEntry sender = client.playerList().getEntry(username); + if (sender == null) return; + + final List children = component.children(); + + if (children.size() == 2) { + // Command + final Component separator = children.get(0); + final Component prefixedCommand = children.get(1); + if ( + !(separator instanceof final TextComponent t_separator) || + !(prefixedCommand instanceof final TextComponent t_prefixedCommand) || + !separator.equals(COMMAND_SEPARATOR_COMPONENT) || + !prefixedCommand.style().isEmpty() + ) return; + + final String command = t_prefixedCommand.content(); + if (command.length() < 1 || command.charAt(0) != '/') return; + final String rawCommand = command.substring(1); + + for (Listener listener : listeners) listener.commandReceived(sender, rawCommand, enabled); + } else if (children.size() == 9) { + // Sign created + final Component[] lines = new Component[4]; + + if (!children.get(0).equals(SIGN_CREATED_TEXT_COMPONENT)) return; + + for (int i = 0; i < lines.length; i++) { + int separatorChildIndex = (i * 2) + 1; + if (!children.get(separatorChildIndex).equals(SIGN_LINE_SEPARATOR_COMPONENT)) return; + lines[i] = children.get(separatorChildIndex + 1); + } + + for (Listener listener : listeners) listener.signCreated(sender, lines, enabled); + } + } + + public static class Listener { + public void commandReceived (MutablePlayerListEntry sender, String command, boolean senderHasCommandSpy) {} + public void signCreated (MutablePlayerListEntry sender, Component[] lines, boolean senderHasCommandSpy) {} + } + + public void addListener (Listener listener) { listeners.add(listener); } + public void removeListener (Listener listener) { listeners.remove(listener); } +} diff --git a/src/main/java/land/chipmunk/chipmunkbot/plugins/PlayerCommandHandler.java b/src/main/java/land/chipmunk/chipmunkbot/plugins/PlayerCommandHandler.java new file mode 100644 index 0000000..0370f3b --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkbot/plugins/PlayerCommandHandler.java @@ -0,0 +1,66 @@ +package land.chipmunk.chipmunkbot.plugins; + +import land.chipmunk.chipmunkbot.ChipmunkBot; +import land.chipmunk.chipmunkbot.Options; +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.util.ComponentUtilities; +import land.chipmunk.chipmunkbot.plugins.CommandManager; +import land.chipmunk.chipmunkbot.data.MutablePlayerListEntry; +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; + +public class PlayerCommandHandler { + private ChipmunkBot client; + @Getter @Setter private String prefix; + @Getter @Setter private String cspyPrefix; + + public PlayerCommandHandler (ChipmunkBot client, Options options) { + this.client = client; + this.prefix = options.commands.prefix; + this.cspyPrefix = options.commands.cspyPrefix != null ? options.commands.cspyPrefix : this.prefix; + + // TODO: Make this less messy (I might just rewrite how I do events eventually) + client.chat().addListener(new ChatPlugin.Listener() { @Override public void playerMessageReceived (PlayerMessage message) { handleMessage(message); } }); + client.commandSpy().addListener(new CommandSpyPlugin.Listener() { @Override public void commandReceived (MutablePlayerListEntry sender, String command, boolean senderHasCommandSpy) { handleCommand(sender, command); } }); + } + + public void handleMessage (PlayerMessage message) { + final Component contents = message.contents(); + if (contents == null) return; + final String contentsString = ComponentUtilities.stringify(contents); + if (!contentsString.startsWith(prefix)) return; + final String commandString = contentsString.substring(prefix.length()); + + final PlayerCommandSource source = new PlayerCommandSource(client, message.sender()); + this.tryExecuteCommand(source, commandString); + } + + public void handleCommand (MutablePlayerListEntry sender, String command) { + if (!command.startsWith(cspyPrefix)) return; + final String commandString = command.substring(cspyPrefix.length()); + + final PlayerCommandSource source = new PlayerCommandSource(client, sender); + this.tryExecuteCommand(source, commandString); + } + + private void tryExecuteCommand (CommandSource source, String command) { + try { + CommandDispatcher dispatcher = client.commandManager().dispatcher(); + dispatcher.execute(command, 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/SelfCarePlugin.java b/src/main/java/land/chipmunk/chipmunkbot/plugins/SelfCarePlugin.java index 0d3a261..3e79016 100644 --- a/src/main/java/land/chipmunk/chipmunkbot/plugins/SelfCarePlugin.java +++ b/src/main/java/land/chipmunk/chipmunkbot/plugins/SelfCarePlugin.java @@ -48,12 +48,13 @@ public class SelfCarePlugin extends SessionAdapter { }; timer = new Timer(); - timer.schedule(task, 70, 70); + timer.schedule(task, 75, 75); } public void tick () { - if (gamemode != GameMode.CREATIVE) client.chat().command("minecraft:gamemode creative"); if (permissionLevel < 2) client.chat().command("minecraft:op @s[type=player]"); + else if (gamemode != GameMode.CREATIVE) client.chat().command("minecraft:gamemode creative"); + else if (!client.commandSpy().enabled()) client.chat().command("c on"); } @Override diff --git a/src/main/resources/default_config.json b/src/main/resources/default_config.json index 50867b7..19c7de2 100644 --- a/src/main/resources/default_config.json +++ b/src/main/resources/default_config.json @@ -11,7 +11,8 @@ "reconnectDelay": 1000, "commands": { - "prefix": "default." + "prefix": "default.", + "cspyPrefix": "default." }, "core": {