diff --git a/src/main/java/land/chipmunk/chipmunkmod/modules/CommandCore.java b/src/main/java/land/chipmunk/chipmunkmod/modules/CommandCore.java index 1cb0749..91b5dc6 100644 --- a/src/main/java/land/chipmunk/chipmunkmod/modules/CommandCore.java +++ b/src/main/java/land/chipmunk/chipmunkmod/modules/CommandCore.java @@ -4,18 +4,30 @@ import land.chipmunk.chipmunkmod.ChipmunkMod; import land.chipmunk.chipmunkmod.listeners.Listener; import land.chipmunk.chipmunkmod.listeners.ListenerManager; import net.minecraft.block.Block; +import net.minecraft.block.BlockState; import net.minecraft.block.CommandBlock; import net.minecraft.block.entity.CommandBlockBlockEntity; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.network.ClientPlayerInteractionManager; import net.minecraft.client.world.ClientWorld; +import net.minecraft.component.ComponentMap; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.NbtComponent; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; import net.minecraft.nbt.NbtCompound; import net.minecraft.network.ClientConnection; +import net.minecraft.network.packet.c2s.play.CreativeInventoryActionC2SPacket; import net.minecraft.network.packet.c2s.play.UpdateCommandBlockC2SPacket; -import net.minecraft.util.math.BlockBox; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; +import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket; +import net.minecraft.registry.Registries; +import net.minecraft.state.property.Property; +import net.minecraft.util.Hand; +import net.minecraft.util.Util; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.*; import net.minecraft.world.chunk.Chunk; import net.minecraft.world.dimension.DimensionType; @@ -25,14 +37,17 @@ import java.util.concurrent.CompletableFuture; public class CommandCore { public static CommandCore INSTANCE = new CommandCore(MinecraftClient.getInstance()); + private final MinecraftClient client; + public boolean ready = false; public BlockPos origin; public BlockBox noPos; - public BlockPos block; public BlockBox withPos; + public BlockPos block; public boolean runFillCommand = true; public boolean alreadyFilled = false; + private Timer timer; private boolean shouldRefill = false; private DimensionType oldDimension; @@ -85,7 +100,6 @@ public class CommandCore { if (networkHandler == null) { cleanup(); - return; } @@ -127,7 +141,7 @@ public class CommandCore { } } } catch (final Exception e) { - e.printStackTrace(); + ChipmunkMod.LOGGER.error("Error while checking if core needs refilling", e); } } @@ -156,12 +170,21 @@ public class CommandCore { block = new BlockPos(withPos.getMinX(), withPos.getMinY(), withPos.getMinZ()); refill(); - for (final Listener listener : ListenerManager.listeners) listener.coreMoved(); - if (!ready) { - ready = true; + final Timer timer = new Timer(); - for (final Listener listener : ListenerManager.listeners) listener.coreReady(); - } + final TimerTask task = new TimerTask() { + @Override + public void run () { + for (final Listener listener : ListenerManager.listeners) listener.coreMoved(); + if (!ready) { + ready = true; + + for (final Listener listener : ListenerManager.listeners) listener.coreReady(); + } + } + }; + + timer.schedule(task, 75); } public void refill () { @@ -185,7 +208,7 @@ public class CommandCore { withPos.getMaxZ() ); - client.getNetworkHandler().sendChatCommand(command); + runPlaceBlock(command); } public void incrementCurrentBlock () { @@ -305,26 +328,137 @@ public class CommandCore { ); } - incrementCurrentBlock(); - final CompletableFuture future = new CompletableFuture<>(); final Timer timer = new Timer(); + final BlockPos oldBlock = block; + final TimerTask queryTask = new TimerTask() { public void run () { - client.getNetworkHandler().getDataQueryHandler().queryBlockNbt(block, future::complete); - - timer.cancel(); // ? Is this necessary? - timer.purge(); + client.getNetworkHandler().getDataQueryHandler().queryBlockNbt(oldBlock, future::complete); } }; timer.schedule(queryTask, 50); + incrementCurrentBlock(); + return future; } + public void runPlaceBlock (final String command) { + final ClientPlayNetworkHandler networkHandler = client.getNetworkHandler(); + if (networkHandler == null) return; + + final ClientConnection connection = networkHandler.getConnection(); + + final ClientPlayerEntity player = client.player; + final ClientWorld world = client.world; + + if (player == null || world == null || !player.isInCreativeMode()) return; + + final ClientPlayerInteractionManager interactionManager = client.interactionManager; + if (interactionManager == null) return; + + final float yaw = player.getYaw(); + final float pitch = player.getPitch(); + + // Anti Block when flying around + final BlockPos position = player.getBlockPos().add( + (int) Math.round(3 * Math.cos(Math.toRadians(yaw))), + 3 * (pitch < 0 ? -1 : 1), + (int) Math.round(3 * Math.sin(Math.toRadians(yaw))) + ); + + // stolen from two five hundred million dollars + final BlockState oldBlockState = world.getBlockState(position); + + final int freeHotBarSlot = player.getInventory().getEmptySlot(); + final int slot = 36 + freeHotBarSlot; + + final int oldSelectedSlot = player.getInventory().selectedSlot; + final ItemStack oldStack = player.getInventory().getStack(slot).copy(); + + final ItemStack commandBlock = new ItemStack(Items.COMMAND_BLOCK); + + final NbtCompound data = new NbtCompound(); + data.putString("id", "minecraft:command_block"); + data.putString("Command", command); + data.putBoolean("auto", true); + + final NbtCompound blockEntityTag = new NbtCompound(); + blockEntityTag.put("BlockEntityTag", data); + + commandBlock.applyComponentsFrom( + ComponentMap.builder() + .add(DataComponentTypes.CUSTOM_DATA, NbtComponent.of(blockEntityTag)) + .add(DataComponentTypes.BLOCK_ENTITY_DATA, NbtComponent.of(data)) + .build() + ); + + connection.send(new CreativeInventoryActionC2SPacket(slot, commandBlock)); + + if (oldSelectedSlot != freeHotBarSlot) { + connection.send(new UpdateSelectedSlotC2SPacket(freeHotBarSlot)); + } + + interactionManager.breakBlock(position); + interactionManager.interactBlock( + player, + Hand.MAIN_HAND, + new BlockHitResult( + new Vec3d(position), + Direction.UP, + position, + false + ) + ); + + if (oldSelectedSlot != freeHotBarSlot) { + connection.send(new UpdateSelectedSlotC2SPacket(oldSelectedSlot)); + } + connection.send(new CreativeInventoryActionC2SPacket(slot, oldStack)); + + final Timer timer = new Timer(); + + final TimerTask task = new TimerTask() { + @Override + public void run () { + final StringBuilder command = new StringBuilder( + String.format( + "setblock %d %d %d %s", + + position.getX(), + position.getY(), + position.getZ(), + + Registries.BLOCK.getId(oldBlockState.getBlock()) + ) + ); + + if (!oldBlockState.getProperties().isEmpty()) { + command.append('['); + + for (final Property property : oldBlockState.getProperties()) { + command.append(property.getName()) + .append('=') + .append(Util.getValueAsString(property, property.getType())) + .append(','); + } + + command.deleteCharAt(command.length() - 1); + + command.append(']'); + } + + CommandCore.this.run(command.toString()); + } + }; + + timer.schedule(task, 100); // assuming the core has already been filled at this point + } + public void cleanup () { if (timer == null) return;