refactor: improvements to place block core (so-called "deploy core")

- after 5 tries of failed deploy core attempts, it falls back to using chat instead. this happens a lot when you `/tp` to somewhere underground where the player's surroundings are covered with blocks and command blocks don't get executed for some reason
- i'm using the obd sex mod way of determining block positions now
- the deploy core block now gets set to the previous block only when the core is CONFIRMED to be filled, and not just a fixed 100ms waiting time after placed

i hope this is enough to fix troubles, if there are more issues i'll try to fix them
This commit is contained in:
Chayapak Supasakul 2025-10-13 18:49:05 +07:00
parent 03eb3c84f6
commit aae472cb27
Signed by: ChomeNS
SSH key fingerprint: SHA256:0YoxhdyXsgbc0nfeB2N6FYE60mxMU7DS4uCUMaw2mvA

View file

@ -1,11 +1,13 @@
package land.chipmunk.chipmunkmod.modules;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
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.FallingBlock;
import net.minecraft.block.entity.CommandBlockBlockEntity;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayNetworkHandler;
@ -23,17 +25,18 @@ import net.minecraft.network.packet.c2s.play.CreativeInventoryActionC2SPacket;
import net.minecraft.network.packet.c2s.play.UpdateCommandBlockC2SPacket;
import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket;
import net.minecraft.registry.Registries;
import net.minecraft.registry.tag.BlockTags;
import net.minecraft.state.property.Property;
import net.minecraft.util.Hand;
import net.minecraft.util.Pair;
import net.minecraft.util.Util;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockBox;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.*;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.dimension.DimensionType;
import java.util.Collections;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CompletableFuture;
@ -51,8 +54,10 @@ public class CommandCore implements Listener {
public boolean runFillCommand = true;
public boolean alreadyFilled = false;
private final Map<BlockPos, String> pendingSetBlockCommands = Collections.synchronizedMap(new Object2ObjectArrayMap<>());
private Timer timer;
private boolean shouldRefill = false;
private int refillTriesUsingPlaceBlock = 0;
private DimensionType oldDimension;
public CommandCore (final MinecraftClient client) {
@ -103,7 +108,30 @@ public class CommandCore implements Listener {
check();
if (!shouldRefill) return;
if (!shouldRefill) {
refillTriesUsingPlaceBlock = 0;
synchronized (pendingSetBlockCommands) {
if (pendingSetBlockCommands.isEmpty()) return;
for (final Map.Entry<BlockPos, String> entry : pendingSetBlockCommands.entrySet()) {
final BlockPos blockPos = entry.getKey();
final String block = entry.getValue();
run(
String.format(
"setblock %d %d %d %s",
blockPos.getX(),
blockPos.getY(),
blockPos.getZ(),
block
)
);
}
pendingSetBlockCommands.clear();
}
return;
}
refill();
@ -189,8 +217,17 @@ public class CommandCore implements Listener {
public void refill () {
final ClientPlayNetworkHandler networkHandler = client.getNetworkHandler();
final ClientPlayerEntity player = client.player;
if (!runFillCommand || client.world == null || networkHandler == null || withPos == null) return;
if (
!runFillCommand
|| player == null
|| !player.isInCreativeMode()
|| !player.hasPermissionLevel(2)
|| client.world == null
|| networkHandler == null
|| withPos == null
) return;
final Chunk chunk = client.world.getChunk(withPos.getCenter());
@ -208,7 +245,13 @@ public class CommandCore implements Listener {
withPos.getMaxZ()
);
runPlaceBlock(command);
if (refillTriesUsingPlaceBlock > 5) {
networkHandler.sendChatCommand(command);
refillTriesUsingPlaceBlock = 0;
} else {
runPlaceBlock(command);
refillTriesUsingPlaceBlock++;
}
}
public void incrementCurrentBlock () {
@ -360,29 +403,24 @@ public class CommandCore implements Listener {
final DimensionType dimensionType = world.getDimension();
final ClientPlayerInteractionManager interactionManager = client.interactionManager;
if (interactionManager == null) return;
// stolen from two five hundred million dollars
final Pair<BlockPos, BlockState> pair = findBlockLocation();
if (
player.getPos().getY() < dimensionType.minY()
|| player.getPos().getY() > dimensionType.height() + dimensionType.minY()
pair == null
|| pair.getLeft().getY() < dimensionType.minY()
|| pair.getLeft().getY() > dimensionType.height() + dimensionType.minY()
) {
networkHandler.sendChatCommand(command);
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 BlockPos position = pair.getLeft();
final BlockState oldBlockState = pair.getRight();
final int freeHotBarSlot = player.getInventory().getEmptySlot();
final int slot = 36 + freeHotBarSlot;
@ -430,43 +468,57 @@ public class CommandCore implements Listener {
}
connection.send(new CreativeInventoryActionC2SPacket(slot, oldStack));
final Timer timer = new Timer();
final StringBuilder oldBlockString = new StringBuilder(Registries.BLOCK.getId(oldBlockState.getBlock()).toString());
if (!oldBlockState.getProperties().isEmpty()) {
oldBlockString.append('[');
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.getValues().getLast())) // water[level=15] instead of water[level=0,level=1,....,level=15]
.append(',');
}
command.deleteCharAt(command.length() - 1);
command.append(']');
}
CommandCore.this.run(command.toString());
for (final Property<?> property : oldBlockState.getProperties()) {
oldBlockString.append(property.getName())
.append('=')
.append(Util.getValueAsString(property, property.getValues().getLast())) // water[level=15] instead of water[level=0,level=1,....,level=15]
.append(',');
}
};
timer.schedule(task, 100); // assuming the core has already been filled at this point
oldBlockString.deleteCharAt(oldBlockString.length() - 1);
oldBlockString.append(']');
}
pendingSetBlockCommands.put(position, oldBlockString.toString());
}
// also from two five hundred million dollars
private Pair<BlockPos, BlockState> findBlockLocation () {
final ClientPlayerEntity player = client.player;
if (player == null) return null;
final BlockPos position = player.getBlockPos();
final Box boundingBox = player.getBoundingBox();
boundingBox.expand(1.0);
for (int x = position.getX() - 2; x <= position.getX() + 2; x++) {
for (int y = position.getY() - 2; y <= position.getY() + 2; y++) {
for (int z = position.getZ() - 2; z <= position.getZ() + 2; z++) {
final BlockPos blockPos = new BlockPos(x, y, z);
if (!player.canInteractWithBlockAt(blockPos, 1.0)) continue;
// we don't want to place a block inside ourselves
if (boundingBox.intersects(new Vec3d(blockPos), new Vec3d(blockPos))) continue;
final BlockState blockState = player.getWorld().getBlockState(blockPos);
if (blockState.getBlock() instanceof FallingBlock) continue;
final boolean replaceable = blockState.isIn(BlockTags.REPLACEABLE);
if (
!replaceable
&& !blockState.isIn(BlockTags.AXE_MINEABLE)
&& !blockState.isIn(BlockTags.HOE_MINEABLE)
&& !blockState.isIn(BlockTags.SHOVEL_MINEABLE)
&& !blockState.isIn(BlockTags.PICKAXE_MINEABLE)
) {
continue;
}
return new Pair<>(blockPos, blockState);
}
}
}
return null;
}
public void cleanup () {