diff --git a/src/main/java/land/chipmunk/chipmunkbot/ChipmunkBot.java b/src/main/java/land/chipmunk/chipmunkbot/ChipmunkBot.java index 77d5e84..98e5c77 100644 --- a/src/main/java/land/chipmunk/chipmunkbot/ChipmunkBot.java +++ b/src/main/java/land/chipmunk/chipmunkbot/ChipmunkBot.java @@ -15,6 +15,7 @@ import land.chipmunk.chipmunkbot.plugins.*; public class ChipmunkBot extends Client { @Getter public final ChatPlugin chat = new ChatPlugin(this); + @Getter public final QueryPlugin query = new QueryPlugin(this); @Getter public final PlayerListPlugin playerList = new PlayerListPlugin(this); @Getter public final CommandManager commandManager = new CommandManager(this); @Getter public final ChatCommandHandler chatCommandHandler = new ChatCommandHandler(this); diff --git a/src/main/java/land/chipmunk/chipmunkbot/commands/RunCommand.java b/src/main/java/land/chipmunk/chipmunkbot/commands/RunCommand.java index c5605fc..9410f34 100644 --- a/src/main/java/land/chipmunk/chipmunkbot/commands/RunCommand.java +++ b/src/main/java/land/chipmunk/chipmunkbot/commands/RunCommand.java @@ -8,6 +8,11 @@ import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; import static com.mojang.brigadier.arguments.StringArgumentType.getString; import com.mojang.brigadier.context.CommandContext; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import com.github.steveice10.opennbt.tag.builtin.Tag; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.StringTag; +import java.util.concurrent.CompletableFuture; public class RunCommand extends Command { public RunCommand () { @@ -26,7 +31,16 @@ public class RunCommand extends Command { final CommandSource source = context.getSource(); final ChipmunkBot client = source.client(); - client.core().run(getString(context, "command")); + final CompletableFuture future = client.core().runTracked(getString(context, "command")); + + future.thenApply(tag -> { + if (!tag.contains("LastOutput") || !(tag.get("LastOutput") instanceof StringTag)) return tag; + final String outputJson = ((StringTag) tag.get("LastOutput")).getValue(); + final Component output = GsonComponentSerializer.gson().deserialize(outputJson); + source.sendOutput(output); + + return tag; + }); return 1; } diff --git a/src/main/java/land/chipmunk/chipmunkbot/commands/TestCommand.java b/src/main/java/land/chipmunk/chipmunkbot/commands/TestCommand.java index 02210b0..53723e8 100644 --- a/src/main/java/land/chipmunk/chipmunkbot/commands/TestCommand.java +++ b/src/main/java/land/chipmunk/chipmunkbot/commands/TestCommand.java @@ -16,7 +16,7 @@ public class TestCommand extends Command { } public int helloWorld (CommandContext context) { - CommandSource source = context.getSource(); + final CommandSource source = context.getSource(); source.sendOutput(Component.text("Hello, world!")); return 1; diff --git a/src/main/java/land/chipmunk/chipmunkbot/plugins/CommandCore.java b/src/main/java/land/chipmunk/chipmunkbot/plugins/CommandCore.java index 4fafbc9..997ed33 100644 --- a/src/main/java/land/chipmunk/chipmunkbot/plugins/CommandCore.java +++ b/src/main/java/land/chipmunk/chipmunkbot/plugins/CommandCore.java @@ -25,6 +25,9 @@ import com.github.steveice10.opennbt.tag.builtin.ByteTag; import com.google.gson.JsonObject; import lombok.Getter; import lombok.Setter; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.CompletableFuture; public class CommandCore extends SessionAdapter { private ChipmunkBot client; @@ -130,4 +133,28 @@ public class CommandCore extends SessionAdapter { incrementCurrentBlock(); } + + public CompletableFuture runTracked (String command) { + final Session session = client.session(); + final Vector3i currentBlock = currentBlockAbsolute(); + + // TODO: Support using repeating command blocks (on kaboom-like servers) (because less packets) + session.send(new ServerboundSetCommandBlockPacket(currentBlock, "", CommandBlockMode.SEQUENCE, false, false, false)); + session.send(new ServerboundSetCommandBlockPacket(currentBlock, command, CommandBlockMode.REDSTONE, true, false, true)); + + incrementCurrentBlock(); + + CompletableFuture future = new CompletableFuture(); + + final TimerTask queryTask = new TimerTask() { + public void run () { + client.query().block(currentBlock) + .thenApply(tag -> { future.complete(tag); return tag; }); + } + }; + + new Timer().schedule(queryTask, 50); + + return future; + } } diff --git a/src/main/java/land/chipmunk/chipmunkbot/plugins/QueryPlugin.java b/src/main/java/land/chipmunk/chipmunkbot/plugins/QueryPlugin.java new file mode 100644 index 0000000..c489f41 --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkbot/plugins/QueryPlugin.java @@ -0,0 +1,55 @@ +package land.chipmunk.chipmunkbot.plugins; + +import land.chipmunk.chipmunkbot.Client; +import com.nukkitx.math.vector.Vector3i; +import com.github.steveice10.packetlib.packet.Packet; +import com.github.steveice10.packetlib.Session; +import com.github.steveice10.packetlib.event.session.SessionListener; +import com.github.steveice10.packetlib.event.session.SessionAdapter; +import com.github.steveice10.mc.protocol.packet.ingame.serverbound.level.ServerboundBlockEntityTagQuery; +import com.github.steveice10.mc.protocol.packet.ingame.serverbound.level.ServerboundEntityTagQuery; +import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.ClientboundTagQueryPacket; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import java.util.concurrent.CompletableFuture; +import java.util.Map; +import java.util.HashMap; + +public class QueryPlugin extends SessionAdapter { + private Client client; + private int nextTransactionId = 0; + private Map> transactions = new HashMap<>(); + + public QueryPlugin (Client client) { + this.client = client; + client.session().addListener((SessionListener) this); + } + + public CompletableFuture block (Vector3i position) { + final int transactionId = nextTransactionId++; + if (nextTransactionId > Integer.MAX_VALUE) nextTransactionId = 0; // ? Can and should I use negative numbers too? + client.session().send(new ServerboundBlockEntityTagQuery(transactionId, position)); + + final CompletableFuture future = new CompletableFuture(); + transactions.put(transactionId, future); + return future; + } + + public CompletableFuture entity (int entityId) { + final int transactionId = nextTransactionId++; + if (nextTransactionId > Integer.MAX_VALUE) nextTransactionId = 0; // ? Can and should I use negative numbers too? + client.session().send(new ServerboundEntityTagQuery(transactionId, entityId)); + + final CompletableFuture future = new CompletableFuture(); + transactions.put(transactionId, future); + return future; + } + + @Override + public void packetReceived (Session session, Packet packet) { + if (packet instanceof ClientboundTagQueryPacket) packetReceived(session, (ClientboundTagQueryPacket) packet); + } + + public void packetReceived (Session session, ClientboundTagQueryPacket packet) { + transactions.get(packet.getTransactionId()).complete(packet.getNbt()); + } +}