diff --git a/imprex-testsuite-bungeecord/pom.xml b/imprex-testsuite-bungeecord/pom.xml index 57463b4..3edd60c 100644 --- a/imprex-testsuite-bungeecord/pom.xml +++ b/imprex-testsuite-bungeecord/pom.xml @@ -53,10 +53,12 @@ okio dev.imprex.shaded.okio + com.google.gson dev.imprex.shaded.com.google.gson diff --git a/imprex-testsuite-bungeecord/src/main/java/dev/imprex/testsuite/bungeecord/BungeecordPacketInjector.java b/imprex-testsuite-bungeecord/src/main/java/dev/imprex/testsuite/bungeecord/BungeecordPacketInjector.java index ac52cfc..e6cf885 100644 --- a/imprex-testsuite-bungeecord/src/main/java/dev/imprex/testsuite/bungeecord/BungeecordPacketInjector.java +++ b/imprex-testsuite-bungeecord/src/main/java/dev/imprex/testsuite/bungeecord/BungeecordPacketInjector.java @@ -1,6 +1,8 @@ package dev.imprex.testsuite.bungeecord; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.List; import com.mojang.brigadier.CommandDispatcher; @@ -20,26 +22,32 @@ public class BungeecordPacketInjector extends MessageToMessageDecoder { private static Field ProxiedPlayerChannelField; - private static Field ChannelWrapperChannelField; + private static Method ChannelWrapperChannelMethod; static { try { ProxiedPlayerChannelField = Class.forName("net.md_5.bungee.UserConnection").getDeclaredField("ch"); - ChannelWrapperChannelField = Class.forName("net.md_5.bungee.netty.ChannelWrapper").getDeclaredField("ch"); - } catch (NoSuchFieldException | SecurityException | ClassNotFoundException e) { + ProxiedPlayerChannelField.setAccessible(true); + + ChannelWrapperChannelMethod = Class.forName("net.md_5.bungee.netty.ChannelWrapper").getDeclaredMethod("getHandle"); + ChannelWrapperChannelMethod.setAccessible(true); + } catch (NoSuchFieldException | SecurityException | ClassNotFoundException | NoSuchMethodException e) { e.printStackTrace(); } } + private final BungeecordPlugin plugin; + private final BungeecordPlayer player; + private final CommandDispatcher dispatcher; private final List commandPrefixList; - - private final BungeecordPlayer player; public BungeecordPacketInjector(BungeecordPlugin plugin, BungeecordPlayer player) { + this.plugin = plugin; + this.player = player; + this.dispatcher = plugin.getTestsuite().getCommandRegistry().getDispatcher(); this.commandPrefixList = plugin.getCommandPrefixList(); - this.player = player; } @Override @@ -52,7 +60,12 @@ protected void decode(ChannelHandlerContext ctx, PacketWrapper msg, List if (packet instanceof TabCompleteRequest tabCompletePacket) { StringReader cursor = new StringReader(tabCompletePacket.getCursor()); - if (cursor.canRead() && cursor.peek() == '/') { + if (!cursor.canRead()) { + out.add(msg); + return; + } + + if (cursor.peek() == '/') { cursor.skip(); } @@ -61,6 +74,11 @@ protected void decode(ChannelHandlerContext ctx, PacketWrapper msg, List return; } + cursor.setCursor(1); + +// System.out.println(tabCompletePacket.getCursor()); +// System.out.println(cursor.getRemaining()); + ParseResults result = this.dispatcher.parse(cursor, this.player); this.dispatcher.getCompletionSuggestions(result).whenComplete((suggestions, error) -> { if (error != null) { @@ -68,6 +86,7 @@ protected void decode(ChannelHandlerContext ctx, PacketWrapper msg, List return; } +// System.out.println("Suggestions: " + suggestions.getList().size()); ProxiedPlayer proxiedPlayer = this.player.getProxiedPlayer(); if (proxiedPlayer.isConnected() && !suggestions.isEmpty()) { proxiedPlayer.unsafe().sendPacket(new TabCompleteResponse(tabCompletePacket.getTransactionId(), suggestions)); @@ -80,10 +99,10 @@ protected void decode(ChannelHandlerContext ctx, PacketWrapper msg, List public void inject() { try { - Object channelWrapper = ProxiedPlayerChannelField.get(this.player); - Channel channel = (Channel) ChannelWrapperChannelField.get(channelWrapper); + Object channelWrapper = ProxiedPlayerChannelField.get(this.player.getProxiedPlayer()); + Channel channel = (Channel) ChannelWrapperChannelMethod.invoke(channelWrapper); channel.pipeline().addAfter("packet-decoder", "imprex-testsuite-decoder", this); - } catch (IllegalArgumentException | IllegalAccessException e) { + } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } diff --git a/imprex-testsuite-bungeecord/src/main/java/dev/imprex/testsuite/bungeecord/BungeecordPlugin.java b/imprex-testsuite-bungeecord/src/main/java/dev/imprex/testsuite/bungeecord/BungeecordPlugin.java index a01d272..7fa49c0 100644 --- a/imprex-testsuite-bungeecord/src/main/java/dev/imprex/testsuite/bungeecord/BungeecordPlugin.java +++ b/imprex-testsuite-bungeecord/src/main/java/dev/imprex/testsuite/bungeecord/BungeecordPlugin.java @@ -1,11 +1,14 @@ package dev.imprex.testsuite.bungeecord; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.tree.CommandNode; + import dev.imprex.testsuite.TestsuiteLogger; import dev.imprex.testsuite.TestsuitePlugin; +import dev.imprex.testsuite.api.TestsuiteSender; import dev.imprex.testsuite.server.ServerInstance; import net.kyori.adventure.platform.bungeecord.BungeeAudiences; import net.md_5.bungee.api.connection.ProxiedPlayer; @@ -43,21 +46,28 @@ public void onEnable() { // Register commands PluginManager pluginManager = this.getProxy().getPluginManager(); - pluginManager.registerCommand(this, new BungeecordCommand(this.testsuite, (args) -> args, "testsuite", "ts", "tests", "tsuite")); - this.commandPrefixList.addAll(Arrays.asList("testsuite", "ts", "tests", "tsuite")); - - this.testsuite.getCommandRegistry().getCommands().values().stream() - .filter(command -> command.isRoot()) - .forEach(command -> { - String literal = command.literal().getLiteral(); - this.commandPrefixList.add(literal); - - pluginManager.registerCommand(this, new BungeecordCommand( - this.testsuite, - (args) -> literal + " " + args, - literal, - command.aliases().toArray(String[]::new))); - }); + CommandDispatcher parentDispatcher = this.testsuite.getCommandRegistry().getDispatcher(); + for (CommandNode node : parentDispatcher.getRoot().getChildren()) { + String literal = node.getName(); + this.commandPrefixList.add(literal); + + pluginManager.registerCommand(this, new BungeecordCommand( + this.testsuite, + (args) -> literal + " " + args, + literal)); + } + +// this.testsuite.getCommandRegistry().getCommands().values().stream() +// .filter(command -> command.isRoot()) +// .forEach(command -> { +// String literal = command.literal().getLiteral(); +// this.commandPrefixList.add(literal); +// +// pluginManager.registerCommand(this, new BungeecordCommand( +// this.testsuite, +// (args) -> literal + " " + args, +// literal)); +// }); pluginManager.registerListener(this, this); } diff --git a/imprex-testsuite-core/src/main/java/dev/imprex/testsuite/command/CommandRegistry.java b/imprex-testsuite-core/src/main/java/dev/imprex/testsuite/command/CommandRegistry.java index 2c4832b..82eaecb 100644 --- a/imprex-testsuite-core/src/main/java/dev/imprex/testsuite/command/CommandRegistry.java +++ b/imprex-testsuite-core/src/main/java/dev/imprex/testsuite/command/CommandRegistry.java @@ -1,13 +1,16 @@ package dev.imprex.testsuite.command; +import static dev.imprex.testsuite.command.ArgumentBuilder.literal; import static dev.imprex.testsuite.command.CommandBuilder.command; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.tree.LiteralCommandNode; import dev.imprex.testsuite.TestsuitePlugin; import dev.imprex.testsuite.api.TestsuiteSender; @@ -28,7 +31,10 @@ public class CommandRegistry { private final Map commands = new HashMap<>(); - private final CommandDispatcher dispatcher = new CommandDispatcher<>(); + + private final CommandDispatcher parentDispatcher = new CommandDispatcher<>(); + + private final LiteralArgumentBuilder literal = literal("testsuite"); public CommandRegistry(TestsuitePlugin plugin) { this.register(command(new CommandConnect(plugin).create()) @@ -51,6 +57,12 @@ public CommandRegistry(TestsuitePlugin plugin) { .asRoot()); this.register(command(new CommandStop(plugin).create()) .asRoot()); + + LiteralCommandNode node = this.parentDispatcher.register(literal); + for (String alias : Arrays.asList("ts", "tsuite")) { + this.parentDispatcher.register(literal(alias) + .redirect(node)); + } } public void register(CommandBuilder builder) { @@ -58,11 +70,17 @@ public void register(CommandBuilder builder) { if (this.commands.containsKey(command)) { throw new IllegalArgumentException("Duplicate command: " + command); } - + CommandMeta registration = builder.build(); LiteralArgumentBuilder literal = registration.literal(); this.commands.put(command, registration); - this.dispatcher.register(literal); + + if (builder.isRoot) { + this.parentDispatcher.register(literal); + } + + LiteralCommandNode node = literal.build(); + this.literal.then(node); for (String alias : builder.aliases) { if (this.commands.containsKey(alias)) { @@ -71,10 +89,10 @@ public void register(CommandBuilder builder) { this.commands.put(alias, registration); - // Handling in implementation -// this.dispatcher.register(literal(alias) -// .requires(literal.getRequirement()) -// .redirect(literal.build())); + if (builder.isRoot) { + this.parentDispatcher.register(literal(alias) + .redirect(node)); + } } } @@ -83,6 +101,6 @@ public Map getCommands() { } public CommandDispatcher getDispatcher() { - return this.dispatcher; + return this.parentDispatcher; } } \ No newline at end of file diff --git a/imprex-testsuite-core/src/main/java/dev/imprex/testsuite/command/command/CommandList.java b/imprex-testsuite-core/src/main/java/dev/imprex/testsuite/command/command/CommandList.java index 11f56c5..40b527a 100644 --- a/imprex-testsuite-core/src/main/java/dev/imprex/testsuite/command/command/CommandList.java +++ b/imprex-testsuite-core/src/main/java/dev/imprex/testsuite/command/command/CommandList.java @@ -92,14 +92,19 @@ public int listServer(CommandContext context) { .append(Component.text(server.getPlayers().size()) .color(Chat.Color.LIGHT_GREEN) .hoverEvent(HoverEvent.showText(Component.text("Player count") - .color(Chat.Color.LIGHT_GREEN)))) + .color(Chat.Color.LIGHT_GREEN)))); + + if (server.isIdleTimeout()) { + playerCount = playerCount .append(Component.text(" | ")) .append(Component.text(milliToSeconds(server.getInactiveTime()) + "s") .color(Chat.Color.DARK_GREEN) .hoverEvent(HoverEvent.showText(Component.text("Inactive time") - .color(Chat.Color.DARK_GREEN)))) - .append(Component.text(")") - .color(Chat.Color.GRAY)); + .color(Chat.Color.DARK_GREEN)))); + } + + playerCount = playerCount.append(Component.text(")") + .color(Chat.Color.GRAY)); Component connectServer = Component.text("Connect") .color(Chat.Color.DARK_GREEN) diff --git a/imprex-testsuite-core/src/main/java/dev/imprex/testsuite/command/suggestion/ServerSuggestionBuilder.java b/imprex-testsuite-core/src/main/java/dev/imprex/testsuite/command/suggestion/ServerSuggestionBuilder.java index 522cc29..2bf6244 100644 --- a/imprex-testsuite-core/src/main/java/dev/imprex/testsuite/command/suggestion/ServerSuggestionBuilder.java +++ b/imprex-testsuite-core/src/main/java/dev/imprex/testsuite/command/suggestion/ServerSuggestionBuilder.java @@ -46,6 +46,8 @@ public SuggestionProvider buildSuggest(String fieldName) { String input = ArgumentBuilder.getSafeStringArgument(context, fieldName, ""); String[] keywords = input.toLowerCase().split("[-_. ]"); + System.out.println("INPUT: " + context.getInput()); + System.out.println("INPUT: " + input); transformation.apply(this.supplier.get()) .map(Objects::toString) .filter(name -> { diff --git a/imprex-testsuite-core/src/main/java/dev/imprex/testsuite/server/ServerInstance.java b/imprex-testsuite-core/src/main/java/dev/imprex/testsuite/server/ServerInstance.java index 32c20a6..e42b217 100644 --- a/imprex-testsuite-core/src/main/java/dev/imprex/testsuite/server/ServerInstance.java +++ b/imprex-testsuite-core/src/main/java/dev/imprex/testsuite/server/ServerInstance.java @@ -1,7 +1,9 @@ package dev.imprex.testsuite.server; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; @@ -49,7 +51,7 @@ public class ServerInstance implements TestsuiteServer, Runnable { private AtomicReference serverStatus = new AtomicReference<>(PteroServerStatus.UNKNOWN); private AtomicLong inactiveTime = new AtomicLong(System.currentTimeMillis()); - private AtomicBoolean idleTimeout = new AtomicBoolean(true); + private AtomicBoolean idleTimeout = new AtomicBoolean(false); public ServerInstance(ServerManager manager, ClientServer server) { this.manager = manager; @@ -185,27 +187,32 @@ public CompletableFuture setupServer() { } return this.deletePluginJars() - .thenCompose(__ -> this.uploadServerFiles()); + .thenAccept(__ -> this.uploadServerFiles()); } private CompletableFuture deletePluginJars() { return PteroUtil.execute(this.server.retrieveDirectory()) - .thenApply(directory -> directory.getDirectoryByName("plugins")) - .thenCompose(optional -> { + .thenCompose(directory -> { + Optional optional = directory.getDirectoryByName("plugins"); if (optional.isEmpty()) { return CompletableFuture.completedFuture(null); } - Directory directory = optional.get(); - CompletableFuture future = new CompletableFuture(); + return PteroUtil.execute(directory.into(optional.get())); + }) + .thenCompose(directory -> { + if (directory == null) { + return CompletableFuture.completedFuture(null); + } + List> deleteRequest = new ArrayList>(); for (GenericFile file : directory.getFiles()) { if (file.getName().endsWith(".jar")) { - future = future.thenAccept(__ -> PteroUtil.execute(file.delete())); + deleteRequest.add(PteroUtil.execute(file.delete())); } } - return future; + return CompletableFuture.allOf(deleteRequest.toArray(CompletableFuture[]::new)); }); } @@ -223,6 +230,8 @@ public CompletableFuture executeCommand(String command) { } public CompletableFuture start() { + this.setIdleTimeout(true); + // this.subscribe(); // is already called in override return this.override(false).whenComplete((changes, error) -> { if (error != null) { @@ -239,6 +248,8 @@ public CompletableFuture start() { } public CompletableFuture restart() { + this.setIdleTimeout(true); + this.subscribe(); return PteroUtil.execute(this.server.restart()); } @@ -283,6 +294,10 @@ public boolean toggleIdleTimeout() { boolean state = this.idleTimeout.get(); return this.idleTimeout.compareAndSet(state, !state) ? !state : state; } + + public boolean isIdleTimeout() { + return this.idleTimeout.get(); + } public void notifyMessage(String message, Object... arguments) { Chat.builder(this).append(message, arguments).broadcast(); diff --git a/imprex-testsuite-core/src/main/java/dev/imprex/testsuite/server/ServerManager.java b/imprex-testsuite-core/src/main/java/dev/imprex/testsuite/server/ServerManager.java index 0d039e2..a0256b0 100644 --- a/imprex-testsuite-core/src/main/java/dev/imprex/testsuite/server/ServerManager.java +++ b/imprex-testsuite-core/src/main/java/dev/imprex/testsuite/server/ServerManager.java @@ -82,7 +82,6 @@ public void run() { if (instance == null) { instance = new ServerInstance(this, server); - instance.setIdleTimeout(false); // only enable when started with command this.serverInstances.put(identifier, instance); diff --git a/pom.xml b/pom.xml index 85e6b9b..b5bbc9a 100644 --- a/pom.xml +++ b/pom.xml @@ -82,7 +82,7 @@ com.mojang brigadier - 1.0.18 + 1.1.8 provided