Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Commandsystem using packet injection #2

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions imprex-testsuite-bungeecord/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@
<pattern>okio</pattern>
<shadedPattern>dev.imprex.shaded.okio</shadedPattern>
</relocation>
<!-- bungeecord packet injection is unable to find method when relocating
<relocation>
<pattern>com.mojang</pattern>
<shadedPattern>dev.imprex.shaded.com.mojang</shadedPattern>
</relocation>
-->
<relocation>
<pattern>com.google.gson</pattern>
<shadedPattern>dev.imprex.shaded.com.google.gson</shadedPattern>
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -20,26 +22,32 @@
public class BungeecordPacketInjector extends MessageToMessageDecoder<PacketWrapper> {

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<TestsuiteSender> dispatcher;
private final List<String> 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
Expand All @@ -52,7 +60,12 @@ protected void decode(ChannelHandlerContext ctx, PacketWrapper msg, List<Object>

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();
}

Expand All @@ -61,13 +74,19 @@ protected void decode(ChannelHandlerContext ctx, PacketWrapper msg, List<Object>
return;
}

cursor.setCursor(1);

// System.out.println(tabCompletePacket.getCursor());
// System.out.println(cursor.getRemaining());

ParseResults<TestsuiteSender> result = this.dispatcher.parse(cursor, this.player);
this.dispatcher.getCompletionSuggestions(result).whenComplete((suggestions, error) -> {
if (error != null) {
error.printStackTrace();
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));
Expand All @@ -80,10 +99,10 @@ protected void decode(ChannelHandlerContext ctx, PacketWrapper msg, List<Object>

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();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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<TestsuiteSender> parentDispatcher = this.testsuite.getCommandRegistry().getDispatcher();
for (CommandNode<TestsuiteSender> 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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -28,7 +31,10 @@
public class CommandRegistry {

private final Map<String, CommandMeta> commands = new HashMap<>();
private final CommandDispatcher<TestsuiteSender> dispatcher = new CommandDispatcher<>();

private final CommandDispatcher<TestsuiteSender> parentDispatcher = new CommandDispatcher<>();

private final LiteralArgumentBuilder<TestsuiteSender> literal = literal("testsuite");

public CommandRegistry(TestsuitePlugin plugin) {
this.register(command(new CommandConnect(plugin).create())
Expand All @@ -51,18 +57,30 @@ public CommandRegistry(TestsuitePlugin plugin) {
.asRoot());
this.register(command(new CommandStop(plugin).create())
.asRoot());

LiteralCommandNode<TestsuiteSender> node = this.parentDispatcher.register(literal);
for (String alias : Arrays.asList("ts", "tsuite")) {
this.parentDispatcher.register(literal(alias)
.redirect(node));
}
}

public void register(CommandBuilder builder) {
String command = builder.literal.getLiteral();
if (this.commands.containsKey(command)) {
throw new IllegalArgumentException("Duplicate command: " + command);
}

CommandMeta registration = builder.build();
LiteralArgumentBuilder<TestsuiteSender> literal = registration.literal();
this.commands.put(command, registration);
this.dispatcher.register(literal);

if (builder.isRoot) {
this.parentDispatcher.register(literal);
}

LiteralCommandNode<TestsuiteSender> node = literal.build();
this.literal.then(node);

for (String alias : builder.aliases) {
if (this.commands.containsKey(alias)) {
Expand All @@ -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));
}
}
}

Expand All @@ -83,6 +101,6 @@ public Map<String, CommandMeta> getCommands() {
}

public CommandDispatcher<TestsuiteSender> getDispatcher() {
return this.dispatcher;
return this.parentDispatcher;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,19 @@ public int listServer(CommandContext<TestsuiteSender> 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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public SuggestionProvider<TestsuiteSender> 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 -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -49,7 +51,7 @@ public class ServerInstance implements TestsuiteServer, Runnable {
private AtomicReference<PteroServerStatus> 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;
Expand Down Expand Up @@ -185,27 +187,32 @@ public CompletableFuture<Void> setupServer() {
}

return this.deletePluginJars()
.thenCompose(__ -> this.uploadServerFiles());
.thenAccept(__ -> this.uploadServerFiles());
}

private CompletableFuture<Void> deletePluginJars() {
return PteroUtil.execute(this.server.retrieveDirectory())
.thenApply(directory -> directory.getDirectoryByName("plugins"))
.thenCompose(optional -> {
.thenCompose(directory -> {
Optional<Directory> optional = directory.getDirectoryByName("plugins");
if (optional.isEmpty()) {
return CompletableFuture.completedFuture(null);
}

Directory directory = optional.get();
CompletableFuture<Void> future = new CompletableFuture<Void>();
return PteroUtil.execute(directory.into(optional.get()));
})
.thenCompose(directory -> {
if (directory == null) {
return CompletableFuture.completedFuture(null);
}

List<CompletableFuture<Void>> deleteRequest = new ArrayList<CompletableFuture<Void>>();
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));
});
}

Expand All @@ -223,6 +230,8 @@ public CompletableFuture<Void> executeCommand(String command) {
}

public CompletableFuture<Void> start() {
this.setIdleTimeout(true);

// this.subscribe(); // is already called in override
return this.override(false).whenComplete((changes, error) -> {
if (error != null) {
Expand All @@ -239,6 +248,8 @@ public CompletableFuture<Void> start() {
}

public CompletableFuture<Void> restart() {
this.setIdleTimeout(true);

this.subscribe();
return PteroUtil.execute(this.server.restart());
}
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
<dependency>
<groupId>com.mojang</groupId>
<artifactId>brigadier</artifactId>
<version>1.0.18</version>
<version>1.1.8</version>
<scope>provided</scope>
</dependency>
<dependency>
Expand Down