From 26fd290b929c638de5ed445ce1eae577916a254c Mon Sep 17 00:00:00 2001 From: Chip <65827213+ChipmunkMC@users.noreply.github.com> Date: Tue, 31 Jan 2023 22:49:50 -0500 Subject: [PATCH] Initial core implementation --- build.gradle | 8 + lombok.config | 2 + .../chipmunk/chipmunkmod/ChipmunkMod.java | 2 +- .../chipmunkmod/command/CommandManager.java | 1 + .../chipmunkmod/commands/CoreCommand.java | 79 ++++++++++ .../chipmunk/chipmunkmod/data/BlockArea.java | 13 ++ .../mixin/ClientPlayerEntityMixin.java | 34 ++++ .../chipmunkmod/modules/CommandCore.java | 148 ++++++++++++++++++ src/main/resources/chipmunkmod.mixins.json | 3 +- 9 files changed, 288 insertions(+), 2 deletions(-) create mode 100644 lombok.config create mode 100644 src/main/java/land/chipmunk/chipmunkmod/commands/CoreCommand.java create mode 100644 src/main/java/land/chipmunk/chipmunkmod/data/BlockArea.java create mode 100644 src/main/java/land/chipmunk/chipmunkmod/mixin/ClientPlayerEntityMixin.java create mode 100644 src/main/java/land/chipmunk/chipmunkmod/modules/CommandCore.java diff --git a/build.gradle b/build.gradle index 6d30805..68c9007 100644 --- a/build.gradle +++ b/build.gradle @@ -16,6 +16,8 @@ repositories { // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. // See https://docs.gradle.org/current/userguide/declaring_repositories.html // for more information about repositories. + + mavenCentral() } dependencies { @@ -31,6 +33,12 @@ dependencies { // These are included in the Fabric API production distribution and allow you to update your mod to the latest modules at a later more convenient time. // modImplementation "net.fabricmc.fabric-api:fabric-api-deprecated:${project.fabric_version}" + + compileOnly 'org.projectlombok:lombok:1.18.24' + annotationProcessor 'org.projectlombok:lombok:1.18.24' + + testCompileOnly 'org.projectlombok:lombok:1.18.24' + testAnnotationProcessor 'org.projectlombok:lombok:1.18.24' } processResources { diff --git a/lombok.config b/lombok.config new file mode 100644 index 0000000..347c825 --- /dev/null +++ b/lombok.config @@ -0,0 +1,2 @@ +config.stopBubbling = true +lombok.accessors.fluent = true \ No newline at end of file diff --git a/src/main/java/land/chipmunk/chipmunkmod/ChipmunkMod.java b/src/main/java/land/chipmunk/chipmunkmod/ChipmunkMod.java index 805889a..4f165da 100644 --- a/src/main/java/land/chipmunk/chipmunkmod/ChipmunkMod.java +++ b/src/main/java/land/chipmunk/chipmunkmod/ChipmunkMod.java @@ -8,7 +8,7 @@ public class ChipmunkMod implements ModInitializer { // This logger is used to write text to the console and the log file. // It is considered best practice to use your mod id as the logger's name. // That way, it's clear which mod wrote info, warnings, and errors. - public static final Logger LOGGER = LoggerFactory.getLogger("modid"); + public static final Logger LOGGER = LoggerFactory.getLogger("chipmunkmod"); @Override public void onInitialize () { diff --git a/src/main/java/land/chipmunk/chipmunkmod/command/CommandManager.java b/src/main/java/land/chipmunk/chipmunkmod/command/CommandManager.java index 448a7e4..1505c75 100644 --- a/src/main/java/land/chipmunk/chipmunkmod/command/CommandManager.java +++ b/src/main/java/land/chipmunk/chipmunkmod/command/CommandManager.java @@ -66,5 +66,6 @@ public class CommandManager { static { TestCommand.register(dispatcher); + CoreCommand.register(dispatcher); } } \ No newline at end of file diff --git a/src/main/java/land/chipmunk/chipmunkmod/commands/CoreCommand.java b/src/main/java/land/chipmunk/chipmunkmod/commands/CoreCommand.java new file mode 100644 index 0000000..c8e6984 --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkmod/commands/CoreCommand.java @@ -0,0 +1,79 @@ +package land.chipmunk.chipmunkmod.commands; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import static land.chipmunk.chipmunkmod.command.CommandManager.literal; +import static land.chipmunk.chipmunkmod.command.CommandManager.argument; +import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; +import static com.mojang.brigadier.arguments.StringArgumentType.getString; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.text.Text; +import net.minecraft.nbt.NbtCompound; +import java.util.concurrent.CompletableFuture; +import land.chipmunk.chipmunkmod.modules.CommandCore; + +public class CoreCommand { + public static void register (CommandDispatcher dispatcher) { + dispatcher.register( + literal("core") + .then( + literal("run") + .then( + argument("command", greedyString()) + .executes(c -> run(c)) + ) + ) + + .then( + literal("runTracked") + .then( + argument("command", greedyString()) + .executes(c -> runTracked(c)) + ) + ) + + .then(literal("refill").executes(c -> refill(c))) + .then(literal("move").executes(c -> move(c))) + ); + } + + public static int run (CommandContext context) { + CommandCore.INSTANCE.run(getString(context, "command")); + + return Command.SINGLE_SUCCESS; + } + + public static int runTracked (CommandContext context) { + final FabricClientCommandSource source = context.getSource(); + + final String command = getString(context, "command"); + + final CompletableFuture future = CommandCore.INSTANCE.runTracked(command); + future.thenApply(tag -> { + try { + final String output = tag.getString("LastOutput"); + if (output != null) source.sendFeedback(Text.Serializer.fromJson(output)); + } catch (Exception ignored) { + } + + return tag; + }); + + return Command.SINGLE_SUCCESS; + } + + public static int refill (CommandContext context) { + CommandCore.INSTANCE.refill(); + + return Command.SINGLE_SUCCESS; + } + + public static int move (CommandContext context) { + final FabricClientCommandSource source = context.getSource(); + + CommandCore.INSTANCE.move(source.getClient().player.getPos()); + + return Command.SINGLE_SUCCESS; + } +} diff --git a/src/main/java/land/chipmunk/chipmunkmod/data/BlockArea.java b/src/main/java/land/chipmunk/chipmunkmod/data/BlockArea.java new file mode 100644 index 0000000..631d6c8 --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkmod/data/BlockArea.java @@ -0,0 +1,13 @@ +package land.chipmunk.chipmunkmod.data; + +import net.minecraft.util.math.BlockPos; +import lombok.AllArgsConstructor; +import lombok.Data; + +// ? Am I reinventing the wheel here? +@AllArgsConstructor +@Data +public class BlockArea { + private BlockPos start; + private BlockPos end; +} diff --git a/src/main/java/land/chipmunk/chipmunkmod/mixin/ClientPlayerEntityMixin.java b/src/main/java/land/chipmunk/chipmunkmod/mixin/ClientPlayerEntityMixin.java new file mode 100644 index 0000000..429b714 --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkmod/mixin/ClientPlayerEntityMixin.java @@ -0,0 +1,34 @@ +package land.chipmunk.chipmunkmod.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.entity.MovementType; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec2f; +import land.chipmunk.chipmunkmod.modules.CommandCore; + +@Mixin(ClientPlayerEntity.class) +public class ClientPlayerEntityMixin { + private static MinecraftClient CLIENT = MinecraftClient.getInstance(); + + @Inject(at = @At("HEAD"), method = "move") + public void move (MovementType type, Vec3d relPos, CallbackInfo ci) { + if ((ClientPlayerEntity) (Object) this != CLIENT.player) return; + + final Vec3d position = ((ClientPlayerEntity) (Object) this).getPos().add(relPos); + + final ClientWorld world = CLIENT.getNetworkHandler().getWorld(); + + final BlockPos origin = CommandCore.INSTANCE.origin(); + if (origin == null) { CommandCore.INSTANCE.move(position); return; } + final int distance = (int) Math.sqrt(new Vec2f(origin.getX() / 16, origin.getZ() / 16).distanceSquared(new Vec2f((int) position.getX() / 16, (int) position.getZ() / 16))); + if (distance > world.getSimulationDistance()) CommandCore.INSTANCE.move(position); + } +} diff --git a/src/main/java/land/chipmunk/chipmunkmod/modules/CommandCore.java b/src/main/java/land/chipmunk/chipmunkmod/modules/CommandCore.java new file mode 100644 index 0000000..9610596 --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkmod/modules/CommandCore.java @@ -0,0 +1,148 @@ +package land.chipmunk.chipmunkmod.modules; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.network.ClientConnection; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.network.packet.c2s.play.UpdateCommandBlockC2SPacket; +import net.minecraft.block.entity.CommandBlockBlockEntity; +import net.minecraft.nbt.NbtCompound; +import lombok.Getter; +import lombok.Setter; +import java.util.List; +import java.util.ArrayList; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import land.chipmunk.chipmunkmod.data.BlockArea; + +public class CommandCore { + private MinecraftClient client; + @Getter @Setter private boolean ready = false; + @Getter @Setter private BlockPos origin; + // TODO: Make it configurable + @Getter private final BlockArea relativeArea = new BlockArea(new BlockPos(0, 0, 0), new BlockPos(15, 0, 15)); + @Getter @Setter private BlockPos currentBlockRelative; + + public static CommandCore INSTANCE = new CommandCore(MinecraftClient.getInstance()); + + public CommandCore (MinecraftClient client) { + this.client = client; + } + + public void move (Vec3d position) { + if (!ready) { + ready = true; + // for (Listener listener : listeners) listener.ready(); + } + + origin = new BlockPos( + ((int) position.getX() / 16) * 16, + 0, // TODO: Use the actual bottom of the world instead of hardcoding to 0 + ((int) position.getZ() / 16) * 16 + ); + + if (currentBlockRelative == null) currentBlockRelative = new BlockPos(relativeArea.start()); + refill(); + } + + public void refill () { + // final PositionManager position = client.position(); + final BlockPos relStart = relativeArea.start(); + final BlockPos relEnd = relativeArea.end(); + + final String command = String.format( + "fill %s %s %s %s %s %s minecraft:chain_command_block", + relStart.getX() + origin.getX(), + relStart.getY() + origin.getY(), + relStart.getZ() + origin.getZ(), + + relEnd.getX() + origin.getX(), + relEnd.getY() + origin.getY(), + relEnd.getZ() + origin.getZ() + ); + + client.getNetworkHandler().sendChatCommand(command); + } + + public void incrementCurrentBlock () { + final BlockPos start = relativeArea.start(); + final BlockPos end = relativeArea.end(); + + int x = currentBlockRelative.getX(); + int y = currentBlockRelative.getY(); + int z = currentBlockRelative.getZ(); + + x++; + + if (x > end.getX()) { + x = start.getX(); + z++; + } + + if (z > end.getZ()) { + z = start.getZ(); + y++; + } + + if (y > end.getY()) { + x = start.getX(); + y = start.getY(); + z = start.getZ(); + } + + currentBlockRelative = new BlockPos(x, y, z); + } + + public BlockPos currentBlockAbsolute () { + return currentBlockRelative.add(origin); + } + + public void run (String command) { + final ClientConnection connection = client.getNetworkHandler().getConnection(); + final BlockPos currentBlock = currentBlockAbsolute(); + + // TODO: Support using repeating command blocks (on kaboom-like servers) (because less packets) + connection.send(new UpdateCommandBlockC2SPacket(currentBlock, "", CommandBlockBlockEntity.Type.SEQUENCE, false, false, false)); + connection.send(new UpdateCommandBlockC2SPacket(currentBlock, command, CommandBlockBlockEntity.Type.REDSTONE, false, false, true)); + + incrementCurrentBlock(); + } + + public CompletableFuture runTracked (String command) { + final ClientConnection connection = client.getNetworkHandler().getConnection(); + final BlockPos currentBlock = currentBlockAbsolute(); + + // TODO: Support using repeating command blocks (on kaboom-like servers) (because less packets) + connection.send(new UpdateCommandBlockC2SPacket(currentBlock, "", CommandBlockBlockEntity.Type.SEQUENCE, false, false, false)); + connection.send(new UpdateCommandBlockC2SPacket(currentBlock, command, CommandBlockBlockEntity.Type.REDSTONE, true, false, true)); + + incrementCurrentBlock(); + + CompletableFuture future = new CompletableFuture(); + + final Timer timer = new Timer(); + + final TimerTask queryTask = new TimerTask() { + public void run () { + client.getNetworkHandler().getDataQueryHandler().queryBlockNbt(currentBlock, + tag -> { future.complete(tag); }); + + timer.cancel(); // ? Is this necesary? + timer.purge(); + } + }; + + timer.schedule(queryTask, 50); + + return future; + } + + /* @Override + public void disconnected (DisconnectedEvent event) { + origin = null; + currentBlockRelative = null; + ready = false; + } */ +} diff --git a/src/main/resources/chipmunkmod.mixins.json b/src/main/resources/chipmunkmod.mixins.json index e36dc97..4972de4 100644 --- a/src/main/resources/chipmunkmod.mixins.json +++ b/src/main/resources/chipmunkmod.mixins.json @@ -9,7 +9,8 @@ "ChatScreenMixin", "ChatInputSuggestorMixin", "ClientPlayNetworkHandlerAccessor", - "ClientConnectionMixin" + "ClientConnectionMixin", + "ClientPlayerEntityMixin" ], "injectors": { "defaultRequire": 1