From 6ce9951c1210121a87cf58ee5133791bf1a6b851 Mon Sep 17 00:00:00 2001 From: Silvigarabis <73000464+Silvigarabis@users.noreply.github.com> Date: Mon, 29 Jan 2024 10:22:42 +0800 Subject: [PATCH] command: try to resolve command --- .../thesudo/CommandSenderType.java | 2 +- .../silvigarabis/thesudo/CommandUtil.java | 196 ++++++++++++++++++ .../io/github/silvigarabis/thesudo/Sudo.java | 19 ++ .../silvigarabis/thesudo/TheSudoPlugin.java | 21 +- .../thesudo/logcat/AbstractLogCatter.java | 8 + .../logcat/ConsoleLoggerLogCatter.java | 15 ++ .../thesudo/logcat/LogCatter.java | 6 + .../logcat/PlayerMessageLogCatter.java | 14 ++ 8 files changed, 277 insertions(+), 4 deletions(-) create mode 100644 src/main/java/io/github/silvigarabis/thesudo/CommandUtil.java create mode 100644 src/main/java/io/github/silvigarabis/thesudo/logcat/AbstractLogCatter.java create mode 100644 src/main/java/io/github/silvigarabis/thesudo/logcat/ConsoleLoggerLogCatter.java create mode 100644 src/main/java/io/github/silvigarabis/thesudo/logcat/LogCatter.java create mode 100644 src/main/java/io/github/silvigarabis/thesudo/logcat/PlayerMessageLogCatter.java diff --git a/src/main/java/io/github/silvigarabis/thesudo/CommandSenderType.java b/src/main/java/io/github/silvigarabis/thesudo/CommandSenderType.java index 15f12a6..ba7c909 100644 --- a/src/main/java/io/github/silvigarabis/thesudo/CommandSenderType.java +++ b/src/main/java/io/github/silvigarabis/thesudo/CommandSenderType.java @@ -1,5 +1,5 @@ package io.github.silvigarabis.thesudo; public enum CommandSenderType { - CONSOLE, REMOTE_CONSOLE, PLAYER + CONSOLE, REMOTE_CONSOLE, PLAYER, UNKNOWN; } diff --git a/src/main/java/io/github/silvigarabis/thesudo/CommandUtil.java b/src/main/java/io/github/silvigarabis/thesudo/CommandUtil.java new file mode 100644 index 0000000..d0dc687 --- /dev/null +++ b/src/main/java/io/github/silvigarabis/thesudo/CommandUtil.java @@ -0,0 +1,196 @@ +package io.github.silvigarabis.thesudo; + +import java.util.regex.*; +import java.util.*; + +public class CommandUtil { + public static class ArgumentsParseResult { + public boolean hasError = false; + public ArgumentsParseErrorType errorType = ArgumentsParseErrorType.NONE; + public List args = null; + public String rawCommand = null; + public int incompleteEscapeIndex = -1; + public int unterminatedQuoteStartIndex = -1; + public static enum ArgumentsParseErrorType { + NONE, UNTERMINATED_QUOTE, UNTERMINATED_SINGLE_QUOTE, INCOMPLETE_ESCAPE; + } + public ArgumentsParseResult(String rawCommand, List args, int dquotedStartIndex, int squotedStartIndex, int escapeIndex){ + if (escapeIndex != -1){ + incompleteEscapeIndex = escapeIndex; + errorType = ArgumentsParseErrorType.INCOMPLETE_ESCAPE; + } + if (squotedStartIndex != -1){ + unterminatedQuoteStartIndex = squotedStartIndex; + errorType = ArgumentsParseErrorType.UNTERMINATED_SINGLE_QUOTE; + } else if (dquotedStartIndex != -1){ + unterminatedQuoteStartIndex = dquotedStartIndex; + errorType = ArgumentsParseErrorType.UNTERMINATED_QUOTE; + } + if (errorType != ArgumentsParseErrorType.NONE){ + hasError = true; + } + this.args = Collections.unmodifiableList(args); + this.rawCommand = rawCommand; + } + } + + /** + * 解析命令参数。 + */ + public static ArgumentsParseResult parseArguments(String command){ + List args = new ArrayList<>(); + List chars = unicodeSplit(command); + + Pattern matchSpace = Pattern.compile("\\s+"); + + int i = 0; + String dquotedChars = ""; + String squotedChars = ""; + int dquotedStartIndex = -1; + int squotedStartIndex = -1; + int escapeIndex = -1; + while (i < chars.size()){ + while (i < chars.size() && matchSpace.matcher(chars.get(i)).matches()){ + i += 1; + } + String carg = null; + boolean quoted = false; + boolean squoted = false; + boolean dquoted = false; + while ( // an complex condition: chars still available, and no quote or empty char + i < chars.size() + && ( + quoted || squoted || dquoted + || (! matchSpace.matcher(chars.get(i)).matches()) + ) + ){ + boolean valid = false; + String character = chars.get(i); + + if (quoted){ + valid = true; + quoted = false; + escapeIndex = -1; + } else if (character.equals("\\") && !squoted){ + quoted = true; + escapeIndex = i; + } else if (character.equals("\"") && !squoted){ + dquoted = !dquoted; + dquotedStartIndex = dquoted ? i : -1; + } else if (character.equals("'") && !dquoted){ + squoted = !squoted; + squotedStartIndex = squoted ? i : -1; + } else { + valid = true; + } + + if (valid){ + if (carg == null && !(squoted || dquoted)){ + carg = ""; + } + + if (squoted){ + squotedChars += character; + } else if (dquoted){ + dquotedChars += character; + } else { + carg += chars.get(i); + } + + if (!squoted && squotedChars.length() > 0){ + carg += squotedChars; + squotedChars = ""; + } else if (!dquoted && dquotedChars.length() > 0){ + carg += dquotedChars; + dquotedChars = ""; + } + } + + i += 1; + } + + if (carg != null){ + args.add(carg); + } + } + + return new ArgumentsParseResult(command, args, squotedStartIndex, dquotedStartIndex, escapeIndex); + } + + /** + * 简单的解析命令参数,而不处理错误。 + */ + public static List parseArgumentsSimply(String command){ + List args = new ArrayList<>(); + List chars = unicodeSplit(command); + + Pattern matchSpace = Pattern.compile("\\s+"); + + int i = 0; + while (i < chars.size()){ + while (i < chars.size() && matchSpace.matcher(chars.get(i)).matches()){ + i += 1; + } + String carg = null; + boolean qouted = false; + boolean sqouted = false; + boolean dqouted = false; + while ( // an complex condition: chars still available, and no quote or empty char + i < chars.size() + && ( + qouted || sqouted || dqouted + || (! matchSpace.matcher(chars.get(i)).matches()) + ) + ){ + boolean valid = false; + + if (qouted){ + valid = true; + qouted = false; + } else if (chars.get(i).equals("\\") && !sqouted){ + qouted = true; + } else if (chars.get(i).equals("\"") && !sqouted){ + dqouted = !dqouted; + } else if (chars.get(i).equals("'") && !dqouted){ + sqouted = !sqouted; + } else { + valid = true; + } + + if (valid){ + if (carg == null){ + carg = ""; + } + carg += chars.get(i); + } + + if (carg == null && (qouted || sqouted || dqouted)){ + carg = ""; + } + + i += 1; + } + + if (carg != null){ + args.add(carg); + } + } + + return args; + } + + public static List unicodeSplit(String text){ + List unicodeChars = new ArrayList<>(); + + for (int i = 0; i < text.length(); i++){ + int codePoint = text.codePointAt(i); + if (codePoint > 0xffff){ + i += 1; + } + + unicodeChars.add(Character.toString(codePoint)); + } + + return unicodeChars; + } +} diff --git a/src/main/java/io/github/silvigarabis/thesudo/Sudo.java b/src/main/java/io/github/silvigarabis/thesudo/Sudo.java index 00f7556..ea3c20d 100644 --- a/src/main/java/io/github/silvigarabis/thesudo/Sudo.java +++ b/src/main/java/io/github/silvigarabis/thesudo/Sudo.java @@ -1,6 +1,25 @@ package io.github.silvigarabis.thesudo; +import org.bukkit.command.CommandSender; + +import java.util.List; +import java.util.ArrayList; + public class Sudo { public static void executeCommand(LastCommandData command){ + CommandSender sender = command.sender; + List args; + { + CommandUtil.ArgumentsParseResult arguments = CommandUtil.parseArguments(command.command); + if (arguments.hasError){ + return; + } + args = arguments.args; + } + } + public static List startTabComplete(LastCommandData command){ + CommandSender sender = command.sender; + List currentArgs = CommandUtil.parseArgumentsSimply(command.command); + return null; } } diff --git a/src/main/java/io/github/silvigarabis/thesudo/TheSudoPlugin.java b/src/main/java/io/github/silvigarabis/thesudo/TheSudoPlugin.java index 9c1d555..db42c0f 100644 --- a/src/main/java/io/github/silvigarabis/thesudo/TheSudoPlugin.java +++ b/src/main/java/io/github/silvigarabis/thesudo/TheSudoPlugin.java @@ -18,6 +18,7 @@ import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.server.ServerCommandEvent; import org.bukkit.event.server.RemoteServerCommandEvent; +import org.bukkit.event.server.TabCompleteEvent; import org.bukkit.event.Listener; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -58,22 +59,28 @@ public void onDisable(){ //we need raw command text private LastCommandData lastCommand; + private LastCommandData lastTabCompleteRequestCommand; @EventHandler(priority=EventPriority.MONITOR) - public void onPlayerCommandPreprocessing(PlayerCommandPreprocessEvent event){ + public void onPlayerCommandPreprocessingEvent(PlayerCommandPreprocessEvent event){ lastCommand = new LastCommandData(event.getPlayer(), event.getMessage(), CommandSenderType.PLAYER); } @EventHandler(priority=EventPriority.MONITOR) - public void onServerCommand(ServerCommandEvent event){ + public void onServerCommandEvent(ServerCommandEvent event){ lastCommand = new LastCommandData(event.getSender(), event.getCommand(), CommandSenderType.CONSOLE); } @EventHandler(priority=EventPriority.MONITOR) - public void onRemoteServerCommand(RemoteServerCommandEvent event){ + public void onRemoteServerCommandEvent(RemoteServerCommandEvent event){ lastCommand = new LastCommandData(event.getSender(), event.getCommand(), CommandSenderType.REMOTE_CONSOLE); } + @EventHandler(priority=EventPriority.MONITOR) + public void onTabCompleteEvent(TabCompleteEvent event){ + lastTabCompleteRequestCommand = new LastCommandData(event.getSender(), event.getBuffer(), CommandSenderType.UNKNOWN); + } + @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args){ if (lastCommand.sender.equals(sender)){ @@ -84,4 +91,12 @@ public boolean onCommand(CommandSender sender, Command command, String label, St return true; } + public List onTabComplete(CommandSender sender, Command command, String label, String[] args){ + if (lastCommand.sender.equals(sender)){ + return Sudo.startTabComplete(lastTabCompleteRequestCommand); + } else { + sender.sendMessage("命令执行者不匹配,无法解析命令"); + } + return null; + } } diff --git a/src/main/java/io/github/silvigarabis/thesudo/logcat/AbstractLogCatter.java b/src/main/java/io/github/silvigarabis/thesudo/logcat/AbstractLogCatter.java new file mode 100644 index 0000000..0bcfffc --- /dev/null +++ b/src/main/java/io/github/silvigarabis/thesudo/logcat/AbstractLogCatter.java @@ -0,0 +1,8 @@ +package io.github.silvigarabis.thesudo.logcat; + +public abstract class AbstractLogCatter implements LogCatter { + public abstract boolean hasNextLine(); + public abstract String nextLine(); + public void onNextLine(Consumer nextLineCallback){ + } +} diff --git a/src/main/java/io/github/silvigarabis/thesudo/logcat/ConsoleLoggerLogCatter.java b/src/main/java/io/github/silvigarabis/thesudo/logcat/ConsoleLoggerLogCatter.java new file mode 100644 index 0000000..d75867d --- /dev/null +++ b/src/main/java/io/github/silvigarabis/thesudo/logcat/ConsoleLoggerLogCatter.java @@ -0,0 +1,15 @@ +package io.github.silvigarabis.thesudo.logcat; + +import org.bukkit.entity.Player; + +public class ConsoleLoggerLogCatter extends AbstractLogCatter { + public String nextLine(){ + } + public boolean hasNextLine(){ + } + public static ConsoleLoggerLogCatter getLogCatter(){ + return new ConsoleLoggerLogCatter(); + } + private ConsoleLoggerLogCatter(){ + } +} \ No newline at end of file diff --git a/src/main/java/io/github/silvigarabis/thesudo/logcat/LogCatter.java b/src/main/java/io/github/silvigarabis/thesudo/logcat/LogCatter.java new file mode 100644 index 0000000..439eb4d --- /dev/null +++ b/src/main/java/io/github/silvigarabis/thesudo/logcat/LogCatter.java @@ -0,0 +1,6 @@ +package io.github.silvigarabis.thesudo.logcat; + +public interface LogCatter { + boolean hasNextLine(); + String nextLine(); +} \ No newline at end of file diff --git a/src/main/java/io/github/silvigarabis/thesudo/logcat/PlayerMessageLogCatter.java b/src/main/java/io/github/silvigarabis/thesudo/logcat/PlayerMessageLogCatter.java new file mode 100644 index 0000000..45f6b93 --- /dev/null +++ b/src/main/java/io/github/silvigarabis/thesudo/logcat/PlayerMessageLogCatter.java @@ -0,0 +1,14 @@ +package io.github.silvigarabis.thesudo.logcat; + +import org.bukkit.entity.Player; + +public class PlayerMessageLogCatter extends AbstractLogCatter { + public String nextLine(){ + } + public boolean hasNextLine(){ + } + + private Player player; + public PlayerMessageLogCatter(Player player){ + } +} \ No newline at end of file