From 932f903455a1f8fcc7039b9a83de8272475a6ad2 Mon Sep 17 00:00:00 2001 From: wzp Date: Thu, 1 Aug 2024 17:45:28 +0800 Subject: [PATCH] feat: adding command system --- .idea/compiler.xml | 2 +- .idea/fileTemplates/includes/Version.txt | 2 +- .idea/misc.xml | 2 +- build.gradle.kts | 49 +++++++-- src/main/java/cn/wzpmc/Main.java | 14 ++- .../api/message/json/JsonMessagePart.java | 1 - .../java/cn/wzpmc/api/plugins/BasePlugin.java | 48 +++++++++ .../wzpmc/api/plugins/IPluginClassLoader.java | 35 ++++++ .../java/cn/wzpmc/api/user/CommandSender.java | 19 ++++ src/main/java/cn/wzpmc/api/user/IBot.java | 7 ++ .../api/user/permission/Permissions.java | 20 ++++ .../java/cn/wzpmc/commands/StopCommand.java | 20 +++- .../java/cn/wzpmc/console/MyBotConsole.java | 56 ++++++++++ .../cn/wzpmc/entities/user/bot/MyBot.java | 25 ++++- .../network/WebSocketConnectionHandler.java | 2 + .../java/cn/wzpmc/plugins/CommandManager.java | 100 ++++++++++++++++-- .../java/cn/wzpmc/plugins/JavaPlugin.java | 23 ++++ .../cn/wzpmc/plugins/PluginClassLoader.java | 30 ++++++ src/main/resources/log4j2.xml | 10 +- 19 files changed, 436 insertions(+), 29 deletions(-) create mode 100644 src/main/java/cn/wzpmc/api/plugins/BasePlugin.java create mode 100644 src/main/java/cn/wzpmc/api/plugins/IPluginClassLoader.java create mode 100644 src/main/java/cn/wzpmc/api/user/permission/Permissions.java create mode 100644 src/main/java/cn/wzpmc/console/MyBotConsole.java create mode 100644 src/main/java/cn/wzpmc/plugins/JavaPlugin.java create mode 100644 src/main/java/cn/wzpmc/plugins/PluginClassLoader.java diff --git a/.idea/compiler.xml b/.idea/compiler.xml index ef4060e..53eb9c3 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -10,6 +10,6 @@ - + \ No newline at end of file diff --git a/.idea/fileTemplates/includes/Version.txt b/.idea/fileTemplates/includes/Version.txt index 772a55e..e59eb1a 100644 --- a/.idea/fileTemplates/includes/Version.txt +++ b/.idea/fileTemplates/includes/Version.txt @@ -1 +1 @@ -0.0.1-dev \ No newline at end of file +0.0.2-dev \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 5d98256..f6589e3 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -4,7 +4,7 @@ - + \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 0d5d7ec..4c896d4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,39 +1,62 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer + val projectName = rootProject.name val groupName by extra("cn.wzpmc") val projectArtifactId by extra("my-bot") -val projectVersion by extra("0.0.1-dev") +val projectVersion by extra("0.0.2-dev") plugins { id("java") id("maven-publish") + id("com.github.johnrengelman.shadow") version "8.1.1" } group = groupName version = projectVersion - repositories { mavenCentral() maven("https://libraries.minecraft.net") } dependencies { + implementation("net.minecrell:terminalconsoleappender:1.3.0") { + exclude(group = "org.apache.logging.log4j", module = "log4j-core") + exclude(group = "org.apache.logging.log4j", module = "log4j-api") + } implementation("com.mojang:brigadier:1.0.18") // https://mvnrepository.com/artifact/io.netty/netty-all implementation("io.netty:netty-all:4.1.112.Final") // https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core implementation("org.apache.logging.log4j:log4j-core:2.23.1") + // https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api + implementation("org.apache.logging.log4j:log4j-api:2.23.1") + // https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-jul + implementation("org.apache.logging.log4j:log4j-jul:2.23.1") // https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2 implementation("com.alibaba.fastjson2:fastjson2:2.0.52") // https://mvnrepository.com/artifact/org.yaml/snakeyaml implementation("org.yaml:snakeyaml:2.2") // https://mvnrepository.com/artifact/org.jline/jline - implementation("org.jline:jline:3.26.3") + implementation("org.jline:jline-terminal:3.26.3") + implementation("org.jline:jline-reader:3.26.3") + /*implementation("org.jline:jline-terminal-jni:3.26.3") + implementation("org.jline:jline-terminal-ffm:3.26.3")*/ + // https://mvnrepository.com/artifact/org.jline/jline-terminal-jansi + implementation("org.jline:jline-terminal-jansi:3.26.3") + // https://mvnrepository.com/artifact/org.fusesource.jansi/jansi + implementation("org.fusesource.jansi:jansi:2.4.1") + /*// https://mvnrepository.com/artifact/org.jline/jline-terminal-jna + implementation("org.jline:jline-terminal-jna:3.26.3") + // https://mvnrepository.com/artifact/net.java.dev.jna/jna + implementation("net.java.dev.jna:jna:5.14.0")*/ // https://mvnrepository.com/artifact/org.projectlombok/lombok compileOnly("org.projectlombok:lombok:1.18.34") annotationProcessor("org.projectlombok:lombok:1.18.34") testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation("org.junit.jupiter:junit-jupiter") } + tasks.compileJava { options.encoding = "UTF-8" } @@ -48,6 +71,21 @@ tasks.register("sourcesJar") { archiveClassifier.set("sources") from(sourceSets.main.get().allSource) } +tasks.withType { + manifest { + attributes( + "Main-Class" to "cn.wzpmc.Main" + ) + } + + archiveBaseName.set("MyBot") + archiveVersion.set(projectVersion) + archiveClassifier.set("") + transform(Log4j2PluginsCacheFileTransformer::class.java) +} +tasks.named("build") { + dependsOn(tasks.named("shadowJar")) +} publishing { publications { create("mavenJava") { @@ -88,15 +126,14 @@ publishing { repositories { maven { - val releasesRepoUrl = uri("http://server.wzpmc.cn:8081/repository/maven-releases") - val snapshotsRepoUrl = uri("http://server.wzpmc.cn:8081/repository/maven-snapshots") + val releasesRepoUrl = uri("https://wzpmc.cn:90/repository/maven-releases") + val snapshotsRepoUrl = uri("https://wzpmc.cn:90/repository/maven-snapshots") url = if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl credentials { username = project.findProperty("repo.user") as String? ?: "" password = project.findProperty("repo.password") as String? ?: "" } - isAllowInsecureProtocol = true } } } diff --git a/src/main/java/cn/wzpmc/Main.java b/src/main/java/cn/wzpmc/Main.java index e1ebe9f..28d8bff 100644 --- a/src/main/java/cn/wzpmc/Main.java +++ b/src/main/java/cn/wzpmc/Main.java @@ -1,10 +1,13 @@ package cn.wzpmc; +import cn.wzpmc.commands.StopCommand; import cn.wzpmc.configuration.Configuration; +import cn.wzpmc.console.MyBotConsole; +import cn.wzpmc.entities.user.bot.MyBot; import cn.wzpmc.network.WebSocketConnectionHandler; +import cn.wzpmc.plugins.CommandManager; import cn.wzpmc.utils.TemplateFileUtils; import cn.wzpmc.utils.YamlUtils; -import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import lombok.SneakyThrows; import lombok.extern.log4j.Log4j2; @@ -18,6 +21,8 @@ public class Main { private static final String DEFAULT_CONFIGURATION_FILE_PATH = "templates/config.yaml"; @SneakyThrows public static void main(String[] args) { + System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager"); + System.setProperty("terminal.jline", "true"); log.info("启动MyBot..."); File configurationFile = new File("config.yaml"); if (TemplateFileUtils.saveDefaultConfig(Main.class.getClassLoader(), DEFAULT_CONFIGURATION_FILE_PATH, configurationFile)) { @@ -36,7 +41,10 @@ public class Main { } WebSocketConnectionHandler webSocketConnectionHandler = new WebSocketConnectionHandler(); ChannelFuture future = webSocketConnectionHandler.connect(uri); - Channel channel = future.sync().channel(); - + MyBot myBot = new MyBot(configuration); + CommandManager commandManager = myBot.getCommandManager(); + commandManager.registerCommand(new StopCommand(myBot)); + MyBotConsole myBotConsole = new MyBotConsole(myBot, webSocketConnectionHandler); + myBotConsole.start(); } } \ No newline at end of file diff --git a/src/main/java/cn/wzpmc/api/message/json/JsonMessagePart.java b/src/main/java/cn/wzpmc/api/message/json/JsonMessagePart.java index 8e545e6..27bbdef 100644 --- a/src/main/java/cn/wzpmc/api/message/json/JsonMessagePart.java +++ b/src/main/java/cn/wzpmc/api/message/json/JsonMessagePart.java @@ -37,7 +37,6 @@ public interface JsonMessagePart { .append('(') .append('\n'); Map data = this.getData(); - int i = 0; data.forEach((key, value) -> stringBuilder.append('\t').append(key).append('=').append(value).append(',').append('\n')); int length = stringBuilder.length(); stringBuilder.delete(length - 2, length - 1); diff --git a/src/main/java/cn/wzpmc/api/plugins/BasePlugin.java b/src/main/java/cn/wzpmc/api/plugins/BasePlugin.java new file mode 100644 index 0000000..7d572ce --- /dev/null +++ b/src/main/java/cn/wzpmc/api/plugins/BasePlugin.java @@ -0,0 +1,48 @@ +package cn.wzpmc.api.plugins; + +import cn.wzpmc.api.user.IBot; + +/** + * 插件基类 + * @author wzp + * @version 0.0.2-dev + * @since 2024/7/31 下午6:02 + */ +public interface BasePlugin { + /** + * 获取插件主类 + * @author wzp + * @since 2024/7/31 下午7:07 v0.0.2-dev + * @param pluginClass 插件主类类名 + * @return 插件主类 + * @param 插件主类类型 + */ + static T getPlugin(Class pluginClass){ + ClassLoader loader = pluginClass.getClassLoader(); + if (loader instanceof IPluginClassLoader){ + BasePlugin plugin = ((IPluginClassLoader) loader).getPlugin(); + if (pluginClass.isInstance(plugin)) { + return pluginClass.cast(plugin); + } + } + throw new RuntimeException(new IllegalAccessException("You shouldn't load plugin class without PluginClassLoader!!!")); + } + /** + * 获取Bot + * @author wzp + * @since 2024/7/31 下午7:06 v0.0.2-dev + * @return Bot对象 + */ + default IBot getBot() { + IPluginClassLoader classLoader = this.getClassLoader(); + return classLoader.getBot(); + } + + /** + * 获取插件所使用的类加载器 + * @author wzp + * @since 2024/7/31 下午7:11 v0.0.2-dev + * @return 类加载器 + */ + IPluginClassLoader getClassLoader(); +} diff --git a/src/main/java/cn/wzpmc/api/plugins/IPluginClassLoader.java b/src/main/java/cn/wzpmc/api/plugins/IPluginClassLoader.java new file mode 100644 index 0000000..1a72633 --- /dev/null +++ b/src/main/java/cn/wzpmc/api/plugins/IPluginClassLoader.java @@ -0,0 +1,35 @@ +package cn.wzpmc.api.plugins; + +import cn.wzpmc.api.user.IBot; + +import java.net.URL; +import java.net.URLClassLoader; + +/** + * 插件类加载器 + * @author wzp + * @version 0.0.2-dev + * @since 2024/7/31 下午6:59 + */ +public abstract class IPluginClassLoader extends URLClassLoader { + + public IPluginClassLoader(URL[] urls) { + super(urls); + } + + /** + * 获取当前插件 + * @author wzp + * @since 2024/7/31 下午7:15 v0.0.2-dev + * @return 插件 + */ + abstract public BasePlugin getPlugin(); + + /** + * 获取Bot + * @author wzp + * @since 2024/7/31 下午7:15 v0.0.2-dev + * @return Bot对象 + */ + abstract public IBot getBot(); +} diff --git a/src/main/java/cn/wzpmc/api/user/CommandSender.java b/src/main/java/cn/wzpmc/api/user/CommandSender.java index c8ea646..e0fb377 100644 --- a/src/main/java/cn/wzpmc/api/user/CommandSender.java +++ b/src/main/java/cn/wzpmc/api/user/CommandSender.java @@ -1,6 +1,7 @@ package cn.wzpmc.api.user; import cn.wzpmc.api.message.MessageComponent; +import cn.wzpmc.api.user.permission.Permissions; /** * 消息发送者 @@ -31,4 +32,22 @@ public interface CommandSender { * @param messageComponent 消息组件 */ void sendMessage(MessageComponent messageComponent); + + /** + * 获取指令发送者的权限 + * @author wzp + * @since 2024/8/1 下午4:50 v0.0.2-dev + * @return 权限 + */ + Permissions getPermission(); + + /** + * 指令发送者是否为管理员 + * @author wzp + * @since 2024/8/1 下午4:50 v0.0.2-dev + * @return 是否为管理员 + */ + default boolean isAdmin(){ + return Permissions.ADMIN.equals(this.getPermission()); + } } diff --git a/src/main/java/cn/wzpmc/api/user/IBot.java b/src/main/java/cn/wzpmc/api/user/IBot.java index 06a78ab..8406e3e 100644 --- a/src/main/java/cn/wzpmc/api/user/IBot.java +++ b/src/main/java/cn/wzpmc/api/user/IBot.java @@ -25,4 +25,11 @@ public interface IBot extends CommandSender { * @return 指令管理器 */ ICommandManager getCommandManager(); + + /** + * 停止Bot运行 + * @author wzp + * @since 2024/8/1 下午4:57 v0.0.2-dev + */ + void stop(); } diff --git a/src/main/java/cn/wzpmc/api/user/permission/Permissions.java b/src/main/java/cn/wzpmc/api/user/permission/Permissions.java new file mode 100644 index 0000000..189b867 --- /dev/null +++ b/src/main/java/cn/wzpmc/api/user/permission/Permissions.java @@ -0,0 +1,20 @@ +package cn.wzpmc.api.user.permission; + +/** + * 权限 + * @author wzp + * @version 0.0.2-dev + * @since 2024/8/1 下午4:48 + */ +public enum Permissions { + /** + * 普通用户 + * @since 2024/8/1 下午4:49 v0.0.2-dev + */ + USER, + /** + * 管理员 + * @since 2024/8/1 下午4:49 v0.0.2-dev + */ + ADMIN +} diff --git a/src/main/java/cn/wzpmc/commands/StopCommand.java b/src/main/java/cn/wzpmc/commands/StopCommand.java index 75460b9..1ea2f84 100644 --- a/src/main/java/cn/wzpmc/commands/StopCommand.java +++ b/src/main/java/cn/wzpmc/commands/StopCommand.java @@ -1,9 +1,27 @@ package cn.wzpmc.commands; +import cn.wzpmc.api.commands.BrigadierCommand; +import cn.wzpmc.api.user.CommandSender; +import cn.wzpmc.api.user.IBot; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import lombok.RequiredArgsConstructor; + /** + * /stop指令 * @author wzp * @version 0.0.1-dev * @since 2024/7/31 上午2:26 */ -public class StopCommand { +@RequiredArgsConstructor +public class StopCommand implements BrigadierCommand { + private final IBot bot; + @Override + public LiteralArgumentBuilder getCommandNode() { + return LiteralArgumentBuilder.literal("stop") + .requires(CommandSender::isAdmin) + .executes((e) -> { + this.bot.stop(); + return 0; + }); + } } diff --git a/src/main/java/cn/wzpmc/console/MyBotConsole.java b/src/main/java/cn/wzpmc/console/MyBotConsole.java new file mode 100644 index 0000000..fe5bf19 --- /dev/null +++ b/src/main/java/cn/wzpmc/console/MyBotConsole.java @@ -0,0 +1,56 @@ +package cn.wzpmc.console; + +import cn.wzpmc.entities.user.bot.MyBot; +import cn.wzpmc.network.WebSocketConnectionHandler; +import cn.wzpmc.plugins.CommandManager; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import net.minecrell.terminalconsole.SimpleTerminalConsole; +import org.jline.reader.LineReader; +import org.jline.reader.LineReaderBuilder; + +/** + * 主控制台 + * @author wzp + * @version 0.0.2-dev + * @since 2024/7/31 下午9:47 + */ +@Log4j2 +@RequiredArgsConstructor +public class MyBotConsole extends SimpleTerminalConsole { + @Getter + private boolean running = true; + private final MyBot bot; + private final CommandManager commandManager; + private final WebSocketConnectionHandler webSocketConnectionHandler; + public MyBotConsole(MyBot bot, WebSocketConnectionHandler webSocketConnectionHandler){ + this.bot = bot; + this.commandManager = bot.getCommandManager(); + this.webSocketConnectionHandler = webSocketConnectionHandler; + } + + @Override + protected LineReader buildReader(LineReaderBuilder builder) { + return super.buildReader(builder.appName("MyBot").completer(commandManager).highlighter(commandManager)); + } + + @Override + protected void runCommand(String command) { + if (!commandManager.execute(this.bot, command)) { + log.warn("执行指令:`{}`失败!", command); + } + } + + @Override + public void shutdown() { + running = false; + this.webSocketConnectionHandler.kill(); + } + + @Override + public void start() { + this.bot.setConsole(this); + super.start(); + } +} \ No newline at end of file diff --git a/src/main/java/cn/wzpmc/entities/user/bot/MyBot.java b/src/main/java/cn/wzpmc/entities/user/bot/MyBot.java index be1ff85..fb6610b 100644 --- a/src/main/java/cn/wzpmc/entities/user/bot/MyBot.java +++ b/src/main/java/cn/wzpmc/entities/user/bot/MyBot.java @@ -4,10 +4,13 @@ import cn.wzpmc.api.message.MessageComponent; import cn.wzpmc.api.message.StringMessage; import cn.wzpmc.api.message.json.JsonMessage; import cn.wzpmc.api.user.IBot; +import cn.wzpmc.api.user.permission.Permissions; import cn.wzpmc.configuration.Configuration; +import cn.wzpmc.console.MyBotConsole; import cn.wzpmc.plugins.CommandManager; import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.Setter; import lombok.extern.log4j.Log4j2; /** @@ -21,9 +24,13 @@ import lombok.extern.log4j.Log4j2; @Getter public class MyBot implements IBot { private final Configuration configuration; - private final Long id; - private final Long name; - private final CommandManager commandManager = new CommandManager(); + @Setter + private Long id; + @Setter + private Long name; + private final CommandManager commandManager = new CommandManager(this); + @Setter + private MyBotConsole console = null; @Override public void sendMessage(MessageComponent messageComponent) { @@ -34,4 +41,16 @@ public class MyBot implements IBot { log.info(((JsonMessage) messageComponent).toTextDisplay()); } } + + @Override + public Permissions getPermission() { + return Permissions.ADMIN; + } + + @Override + public void stop() { + if (this.console != null) { + this.console.shutdown(); + } + } } diff --git a/src/main/java/cn/wzpmc/network/WebSocketConnectionHandler.java b/src/main/java/cn/wzpmc/network/WebSocketConnectionHandler.java index 094b30e..fa80657 100644 --- a/src/main/java/cn/wzpmc/network/WebSocketConnectionHandler.java +++ b/src/main/java/cn/wzpmc/network/WebSocketConnectionHandler.java @@ -27,6 +27,7 @@ public class WebSocketConnectionHandler { * @author wzp * @since 2024/7/30 下午11:55 v0.0.1-dev * @param websocket websocket连接地址 + * @return 一个ChannelFuture对象 */ public ChannelFuture connect(URI websocket){ log.info("正在连接websocket"); @@ -44,6 +45,7 @@ public class WebSocketConnectionHandler { * @since 2024/7/31 上午2:04 v0.0.1-dev */ public void kill(){ + log.info("结束连接..."); this.eventLoopGroup.shutdownGracefully(); } } diff --git a/src/main/java/cn/wzpmc/plugins/CommandManager.java b/src/main/java/cn/wzpmc/plugins/CommandManager.java index a27f0f8..ced14cc 100644 --- a/src/main/java/cn/wzpmc/plugins/CommandManager.java +++ b/src/main/java/cn/wzpmc/plugins/CommandManager.java @@ -2,21 +2,31 @@ package cn.wzpmc.plugins; import cn.wzpmc.api.commands.BrigadierCommand; import cn.wzpmc.api.commands.RawCommand; +import cn.wzpmc.api.message.StringMessage; import cn.wzpmc.api.plugins.ICommandManager; import cn.wzpmc.api.user.CommandSender; +import cn.wzpmc.api.user.IBot; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.ParseResults; +import com.mojang.brigadier.context.ParsedCommandNode; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.suggestion.Suggestion; import com.mojang.brigadier.suggestion.Suggestions; -import lombok.NoArgsConstructor; +import com.mojang.brigadier.tree.LiteralCommandNode; import lombok.SneakyThrows; +import lombok.ToString; import lombok.extern.log4j.Log4j2; +import org.jline.reader.*; +import org.jline.utils.AttributedString; +import org.jline.utils.AttributedStringBuilder; +import org.jline.utils.AttributedStyle; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; +import java.util.regex.Pattern; /** * 指令管理器实现类 @@ -25,10 +35,14 @@ import java.util.stream.Collectors; * @since 2024/7/31 上午3:13 */ @Log4j2 -@NoArgsConstructor -public class CommandManager implements ICommandManager { +public class CommandManager implements ICommandManager, Completer, Highlighter { private final CommandDispatcher dispatcher = new CommandDispatcher<>(); private final ConcurrentHashMap rawCommands = new ConcurrentHashMap<>(); + private static final int[] COLORS = {AttributedStyle.CYAN, AttributedStyle.YELLOW, AttributedStyle.GREEN, AttributedStyle.MAGENTA, AttributedStyle.BLUE}; + private final IBot bot; + public CommandManager(IBot bot) { + this.bot = bot; + } @Override public void registerCommand(RawCommand rawCommand, String name) { if (rawCommands.containsKey(name)){ @@ -41,6 +55,7 @@ public class CommandManager implements ICommandManager { public void registerCommand(BrigadierCommand brigadierCommand){ dispatcher.register(brigadierCommand.getCommandNode()); } + @ToString private static final class CommandPart { private final String name; private final List args; @@ -67,6 +82,11 @@ public class CommandManager implements ICommandManager { try { dispatcher.execute(rawCommandLine, sender); } catch (CommandSyntaxException e) { + if (e.getType().equals(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand())) { + sender.sendMessage(StringMessage.text(this.bot.getConfiguration().getFallback().getCommand())); + return false; + } + sender.sendMessage(StringMessage.text(e.getLocalizedMessage())); return false; } } @@ -79,17 +99,83 @@ public class CommandManager implements ICommandManager { * @since 2024/7/31 上午3:36 v0.0.1-dev * @param sender 消息发送者 * @param rawCommandLine 完整命令行 + * @param cursor 当前光标位置 * @return 所有被补全的指令 */ @SneakyThrows - public List tabComplete(CommandSender sender, String rawCommandLine){ + public List tabComplete(CommandSender sender, String rawCommandLine, int cursor){ CommandPart commandPart = new CommandPart(rawCommandLine); List result = new ArrayList<>(); if (rawCommands.containsKey(commandPart.name)) { result.addAll(rawCommands.get(commandPart.name).onTabComplete(sender, commandPart.args)); } - Suggestions suggestions = dispatcher.getCompletionSuggestions(dispatcher.parse(rawCommandLine, sender)).get(); - result.addAll(suggestions.getList().stream().map(Suggestion::getText).collect(Collectors.toList())); + for (Map.Entry stringRawCommandEntry : rawCommands.entrySet()) { + String key = stringRawCommandEntry.getKey(); + if (key.contains(commandPart.name)){ + result.add(key); + } + } + Suggestions suggestions = dispatcher.getCompletionSuggestions(dispatcher.parse(rawCommandLine, sender), cursor).get(); + result.addAll(suggestions.getList().stream().map(Suggestion::getText).toList()); return result; } + + @Override + public void complete(LineReader lineReader, ParsedLine parsedLine, List list) { + String line = parsedLine.line(); + int cursor = parsedLine.cursor(); + list.addAll(this.tabComplete(this.bot, line, cursor).stream().map(Candidate::new).toList()); + } + + @Override + public AttributedString highlight(LineReader lineReader, String s) { + final AttributedStringBuilder builder = new AttributedStringBuilder(); + String[] strings = s.split(" "); + String commandName = strings[0]; + if (rawCommands.containsKey(commandName)){ + builder.append(commandName, AttributedStyle.DEFAULT.foreground(AttributedStyle.GREEN)).append(' '); + for (int i = 1; i < strings.length; i++) { + builder.append(strings[i]).append(' '); + } + }else { + final ParseResults results = this.dispatcher.parse(s, this.bot); + int pos = 0; + if (s.startsWith("/")) { + builder.append("/", AttributedStyle.DEFAULT); + pos = 1; + } + int component = -1; + for (final ParsedCommandNode node : results.getContext().getLastChild().getNodes()) { + if (node.getRange().getStart() >= s.length()) { + break; + } + final int start = node.getRange().getStart(); + final int end = Math.min(node.getRange().getEnd(), s.length()); + builder.append(s.substring(pos, start), AttributedStyle.DEFAULT); + if (node.getNode() instanceof LiteralCommandNode) { + builder.append(s.substring(start, end), AttributedStyle.DEFAULT); + } else { + if (++component >= COLORS.length) { + component = 0; + } + builder.append(s.substring(start, end), AttributedStyle.DEFAULT.foreground(COLORS[component])); + } + pos = end; + } + if (pos < s.length()) { + builder.append((s.substring(pos)), AttributedStyle.DEFAULT.foreground(AttributedStyle.RED)); + } + } + return builder.toAttributedString(); + } + + @Override + public void setErrorPattern(Pattern pattern) { + + } + + @Override + public void setErrorIndex(int i) { + + } } diff --git a/src/main/java/cn/wzpmc/plugins/JavaPlugin.java b/src/main/java/cn/wzpmc/plugins/JavaPlugin.java new file mode 100644 index 0000000..b4eb1c6 --- /dev/null +++ b/src/main/java/cn/wzpmc/plugins/JavaPlugin.java @@ -0,0 +1,23 @@ +package cn.wzpmc.plugins; + +import cn.wzpmc.api.plugins.BasePlugin; +import cn.wzpmc.api.plugins.IPluginClassLoader; + +/** + * Java插件基类 + * @author wzp + * @version 0.0.2-dev + * @since 2024/7/31 下午7:01 + */ +public class JavaPlugin implements BasePlugin { + + @Override + public IPluginClassLoader getClassLoader() { + Class aClass = this.getClass(); + ClassLoader loader = aClass.getClassLoader(); + if (loader instanceof IPluginClassLoader){ + return (IPluginClassLoader) loader; + } + throw new RuntimeException(new IllegalAccessException("You shouldn't load plugin class without PluginClassLoader!!!")); + } +} diff --git a/src/main/java/cn/wzpmc/plugins/PluginClassLoader.java b/src/main/java/cn/wzpmc/plugins/PluginClassLoader.java new file mode 100644 index 0000000..528faec --- /dev/null +++ b/src/main/java/cn/wzpmc/plugins/PluginClassLoader.java @@ -0,0 +1,30 @@ +package cn.wzpmc.plugins; + +import cn.wzpmc.api.plugins.BasePlugin; +import cn.wzpmc.api.plugins.IPluginClassLoader; +import cn.wzpmc.api.user.IBot; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.net.URL; + +/** + * 插件类加载器实现 + * @author wzp + * @version 0.0.2-dev + * @since 2024/7/31 下午7:12 + */ +@Getter +@EqualsAndHashCode(callSuper = true) +@ToString +public class PluginClassLoader extends IPluginClassLoader { + private final IBot bot; + @Setter + private BasePlugin plugin; + public PluginClassLoader(URL[] urls, IBot bot) { + super(urls); + this.bot = bot; + } +} diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml index 6b18dc3..8bbd7e0 100644 --- a/src/main/resources/log4j2.xml +++ b/src/main/resources/log4j2.xml @@ -2,10 +2,10 @@ - - + + - + - + @@ -27,4 +27,4 @@ - + \ No newline at end of file