Initial core implementation

This commit is contained in:
chipmunk 2023-01-31 22:49:50 -05:00
parent 92612c6b32
commit 26fd290b92
9 changed files with 288 additions and 2 deletions

View file

@ -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 {

2
lombok.config Normal file
View file

@ -0,0 +1,2 @@
config.stopBubbling = true
lombok.accessors.fluent = true

View file

@ -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 () {

View file

@ -66,5 +66,6 @@ public class CommandManager {
static {
TestCommand.register(dispatcher);
CoreCommand.register(dispatcher);
}
}

View file

@ -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<FabricClientCommandSource> 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<FabricClientCommandSource> context) {
CommandCore.INSTANCE.run(getString(context, "command"));
return Command.SINGLE_SUCCESS;
}
public static int runTracked (CommandContext<FabricClientCommandSource> context) {
final FabricClientCommandSource source = context.getSource();
final String command = getString(context, "command");
final CompletableFuture<NbtCompound> 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<FabricClientCommandSource> context) {
CommandCore.INSTANCE.refill();
return Command.SINGLE_SUCCESS;
}
public static int move (CommandContext<FabricClientCommandSource> context) {
final FabricClientCommandSource source = context.getSource();
CommandCore.INSTANCE.move(source.getClient().player.getPos());
return Command.SINGLE_SUCCESS;
}
}

View file

@ -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;
}

View file

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

View file

@ -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<NbtCompound> 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<NbtCompound> future = new CompletableFuture<NbtCompound>();
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;
} */
}

View file

@ -9,7 +9,8 @@
"ChatScreenMixin",
"ChatInputSuggestorMixin",
"ClientPlayNetworkHandlerAccessor",
"ClientConnectionMixin"
"ClientConnectionMixin",
"ClientPlayerEntityMixin"
],
"injectors": {
"defaultRequire": 1