diff --git a/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/Printer.java b/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/Printer.java index aa6c59bc7..a060c4dd3 100644 --- a/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/Printer.java +++ b/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/Printer.java @@ -104,8 +104,6 @@ public boolean onGameTick() { } List positions = getReachablePositions(); - boolean didPlace = false; - boolean acceptsMoreActions = false; if (PrinterConfig.BLOCK_TIMEOUT.getIntegerValue() != 0) { positions = positions.stream().filter((pos) -> blockPosTimeout.stream().noneMatch((entry) -> entry.pos.equals(pos))).toList(); // From block timeout. Don't place already placed blocks. @@ -118,20 +116,10 @@ public boolean onGameTick() { Guide[] guides = interactionGuides.getInteractionGuides(state); - BlockHitResult result = RayTraceUtils.traceToSchematicWorld(player, 10, true, true); - boolean isCurrentlyLookingSchematic = result != null && result.getBlockPos().equals(position); - for (Guide guide : guides) { if (guide.canExecute(player)) { - // System.out.println("Executing Guide:" + guide); List actions = guide.execute(player); actionHandler.addActions(actions.toArray(Action[]::new)); -// actions.forEach((action) -> { -// if (action instanceof InteractActionImpl a1) { -// // InfoUtils.sendVanillaMessage(Text.literal("InteractActionImpl: " + a1)); -// mc.inGameHud.getChatHud().addMessage(Text.literal("InteractActionImpl: " + a1.context.getBlockPos())); -// } -// }); return true; } if (guide.skipOtherGuides()) continue findBlock; diff --git a/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/config/PrinterConfig.java b/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/config/PrinterConfig.java index 8e45fec23..a2c315e70 100644 --- a/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/config/PrinterConfig.java +++ b/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/config/PrinterConfig.java @@ -58,6 +58,8 @@ public static PrinterConfig getInstance() { public static final ConfigBoolean PRINTER_ALLOW_NONE_EXACT_STATES = new ConfigBoolean("printerAllowNoneExactStates", false, "Allow none exact block states to be placed.\nThis includes things like lichen, muchroom stems, etc."); public static final ConfigBoolean PRINTER_DISABLE_IN_GUIS = new ConfigBoolean("printerDisableInGuis", true, "Disable the printer in GUIs."); public static final ConfigBoolean PRINTER_AIRPLACE = new ConfigBoolean("printerAirPlace", false, "Place blocks in the air."); + public static final ConfigBoolean PRINTER_AIRPLACE_ONLY = new ConfigBoolean("printerAirPlaceOnly", false, "Attempt to air place only when air place is enabled."); + public static final ConfigBoolean PRINTER_AIRPLACE_OFFHAND_SLOT_SUPPRESS = new ConfigBoolean("printerAirPlaceOffhandSlotSuppress", true, "Suppress off-hand slot updates when air placing. Turn off when there are de-sync issues in the off-hand slot."); public static final ConfigDouble PRINTER_AIRPLACE_RANGE = new ConfigDouble("printerAirPlaceRange", 5, 0, 10, "Range at which the printer can air place at"); public static final ConfigBoolean PRINTER_AIRPLACE_FLOATING_ONLY = new ConfigBoolean("printerAirPlaceFloatingOnly", false, "Only attempt to air place if the block position is surrounded by air."); public static final ConfigInteger PRINTER_MIN_INACTIVE_TIME_AIR_PLACE = new ConfigInteger("printerMinInactiveTimeAirPlace", 5, "Minimum time in ticks to wait before placing a block in the air."); @@ -89,6 +91,8 @@ public ImmutableList getOptions() { list.add(PRINTER_ALLOW_NONE_EXACT_STATES); list.add(PRINTER_DISABLE_IN_GUIS); list.add(PRINTER_AIRPLACE); + list.add(PRINTER_AIRPLACE_ONLY); + list.add(PRINTER_AIRPLACE_OFFHAND_SLOT_SUPPRESS); list.add(PRINTER_AIRPLACE_RANGE); list.add(PRINTER_AIRPLACE_FLOATING_ONLY); list.add(PRINTER_MIN_INACTIVE_TIME_AIR_PLACE); diff --git a/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/guides/placement/GeneralPlacementGuide.java b/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/guides/placement/GeneralPlacementGuide.java index 0b19c9325..68e8321c0 100644 --- a/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/guides/placement/GeneralPlacementGuide.java +++ b/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/guides/placement/GeneralPlacementGuide.java @@ -273,18 +273,18 @@ public PrinterPlacementContext getPlacementContext(ClientPlayerEntity player) { } } continue; - } - - BlockHitResult hitResult = new BlockHitResult(blockHit, side.getOpposite(), neighborPos, false); - PrinterPlacementContext context = new PrinterPlacementContext(player, hitResult, requiredItem, slot, relativeDirection, requiresShift); - context.canStealth = true; - BlockState result = getRequiredItemAsBlock(player) - .orElse(targetState.getBlock()) - .getPlacementState(context); // FIXME torch shift clicks another torch and getPlacementState is the clicked block, which is true - - if (result != null && (statesEqual(result, targetState) || correctChestPlacement(targetState, result))) { - contextCache = context; - return context; + } else { + BlockHitResult hitResult = new BlockHitResult(blockHit, side.getOpposite(), neighborPos, false); + PrinterPlacementContext context = new PrinterPlacementContext(player, hitResult, requiredItem, slot, relativeDirection, requiresShift); + context.canStealth = true; + BlockState result = getRequiredItemAsBlock(player) + .orElse(targetState.getBlock()) + .getPlacementState(context); // FIXME torch shift clicks another torch and getPlacementState is the clicked block, which is true + + if (result != null && (statesEqual(result, targetState) || correctChestPlacement(targetState, result))) { + contextCache = context; + return context; + } } } } diff --git a/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/guides/placement/PlacementGuide.java b/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/guides/placement/PlacementGuide.java index 3380dae35..d3d2a3b2b 100644 --- a/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/guides/placement/PlacementGuide.java +++ b/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/guides/placement/PlacementGuide.java @@ -2,9 +2,11 @@ import me.aleksilassila.litematica.printer.v1_21.LitematicaMixinMod; import me.aleksilassila.litematica.printer.v1_21.actions.*; +import me.aleksilassila.litematica.printer.v1_21.config.PrinterConfig; import me.aleksilassila.litematica.printer.v1_21.implementation.PrinterPlacementContext; import me.aleksilassila.litematica.printer.v1_21.SchematicBlockState; import me.aleksilassila.litematica.printer.v1_21.guides.Guide; +import me.aleksilassila.litematica.printer.v1_21.implementation.actions.AirPlaceAction; import me.aleksilassila.litematica.printer.v1_21.implementation.actions.InteractActionImpl; import net.minecraft.block.*; import net.minecraft.client.MinecraftClient; @@ -109,9 +111,13 @@ public boolean isInAir(BlockPos pos){ if (ctx.isAirPlace) { actionChain.addAction(new PrepareAction(ctx)); - actionChain.addAction(new InteractActionImpl(ctx)); + actionChain.addAction(new AirPlaceAction(ctx)); actions.add(actionChain); return actions; + } else { + if (PrinterConfig.PRINTER_AIRPLACE.getBooleanValue() && PrinterConfig.PRINTER_AIRPLACE_ONLY.getBooleanValue()) { + return actions; + } } actionChain.addAction(new PrepareLook(ctx)); diff --git a/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/implementation/actions/AirPlaceAction.java b/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/implementation/actions/AirPlaceAction.java new file mode 100644 index 000000000..7a866e3f8 --- /dev/null +++ b/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/implementation/actions/AirPlaceAction.java @@ -0,0 +1,50 @@ +package me.aleksilassila.litematica.printer.v1_21.implementation.actions; + +import me.aleksilassila.litematica.printer.v1_21.actions.InteractAction; +import me.aleksilassila.litematica.printer.v1_21.config.PrinterConfig; +import me.aleksilassila.litematica.printer.v1_21.implementation.PrinterPlacementContext; +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.network.packet.c2s.play.HandSwingC2SPacket; +import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; + +public class AirPlaceAction extends InteractAction { + private final MinecraftClient mc = MinecraftClient.getInstance(); + public AirPlaceAction(PrinterPlacementContext context) { + super(context); + } + + @Override + protected ActionResult interact(MinecraftClient client, ClientPlayerEntity player, Hand hand, BlockHitResult hitResult) { + if (!mc.world.getBlockState(hitResult.getBlockPos().offset(hitResult.getSide())).isAir()) { + if (PrinterConfig.PRINTER_DEBUG_LOG.getBooleanValue()) System.out.println("InteractActionImpl.interact: block is not air"); + return ActionResult.FAIL; + } + if (PrinterConfig.PRINTER_DEBUG_LOG.getBooleanValue()) System.out.println("InteractActionImpl.interact: attempting to air place block"); + airPlace(hitResult.getBlockPos().offset(hitResult.getSide())); + return ActionResult.PASS; + } + + private void airPlace(BlockPos pos) { + ClientPlayNetworkHandler connection = mc.getNetworkHandler(); + ClientPlayerInteractionManager interactionManager = mc.interactionManager; + if (mc.player == null || connection == null || interactionManager == null) return; + connection.sendPacket(new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.SWAP_ITEM_WITH_OFFHAND, BlockPos.ORIGIN, Direction.UP)); + + Hand hand = Hand.OFF_HAND; + + BlockHitResult hit = new BlockHitResult(Vec3d.ofCenter(pos), Direction.UP, pos, true); + interactionManager.interactBlock(mc.player, hand, hit); + mc.player.swingHand(Hand.MAIN_HAND, false); + connection.sendPacket(new HandSwingC2SPacket(hand)); + connection.sendPacket(new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.SWAP_ITEM_WITH_OFFHAND, BlockPos.ORIGIN, Direction.UP)); + } +} diff --git a/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/implementation/actions/InteractActionImpl.java b/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/implementation/actions/InteractActionImpl.java index 21d0eb3b2..b48f2cb6e 100644 --- a/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/implementation/actions/InteractActionImpl.java +++ b/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/implementation/actions/InteractActionImpl.java @@ -6,17 +6,9 @@ import me.aleksilassila.litematica.printer.v1_21.implementation.PrinterPlacementContext; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.network.packet.c2s.play.ClickSlotC2SPacket; -import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket; -import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket; -import net.minecraft.screen.slot.SlotActionType; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.hit.BlockHitResult; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; -import net.minecraft.util.math.Vec3d; public class InteractActionImpl extends InteractAction { public InteractActionImpl(PrinterPlacementContext context) { @@ -25,55 +17,11 @@ public InteractActionImpl(PrinterPlacementContext context) { private final MinecraftClient mc = MinecraftClient.getInstance(); @Override protected ActionResult interact(MinecraftClient client, ClientPlayerEntity player, Hand hand, BlockHitResult hitResult) { - if(context.isAirPlace) { - if (PrinterConfig.PRINTER_DEBUG_LOG.getBooleanValue()) System.out.println("InteractActionImpl.interact: attempting to air place block"); - airPlace(hitResult.getBlockPos().offset(hitResult.getSide())); - return ActionResult.PASS; - } - ActionResult result = client.interactionManager.interactBlock(player, hand, hitResult); if (!result.isAccepted()) { if (PrinterConfig.PRINTER_DEBUG_LOG.getBooleanValue()) System.out.println("Failed to interact with block got " + result); } - // client.interactionManager.interactItem(player, hand); - // client.getNetworkHandler().sendPacket(new HandSwingC2SPacket(Hand.MAIN_HAND)); + mc.player.swingHand(Hand.MAIN_HAND, false); return result; } - - public boolean isInAir(BlockPos pos){ - if(mc.world == null ) return false; - for(Direction dir : Direction.values()){ - if(!mc.world.getBlockState(pos.offset(dir)).isAir()){ - return false; - } - } - return true; - } - private void airPlace(BlockPos pos){ - swap(); - place(pos); - swapBack(); - } - - private void swap(){ - int ogSlot = mc.player.getInventory().getSlotWithStack(mc.player.getMainHandStack()); - mc.getNetworkHandler().sendPacket(new ClickSlotC2SPacket(0,0,ogSlot + 36,0, SlotActionType.SWAP,mc.player.getMainHandStack(),new Int2ObjectArrayMap<>())); - mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(0)); - } - - private void swapBack(){ - int ogSlot = mc.player.getInventory().getSlotWithStack(mc.player.getMainHandStack()); - mc.getNetworkHandler().sendPacket(new ClickSlotC2SPacket(0,0,ogSlot + 36,0,SlotActionType.SWAP,mc.player.getMainHandStack(),new Int2ObjectArrayMap<>())); - mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(ogSlot)); - } - - private void place(BlockPos pos){ - int slot = 36; - ItemStack stack = mc.player.getMainHandStack(); - mc.getNetworkHandler().sendPacket(new ClickSlotC2SPacket(0,0,slot,0,SlotActionType.PICKUP,stack,new Int2ObjectArrayMap<>())); - mc.getNetworkHandler().sendPacket(new PlayerInteractBlockC2SPacket(Hand.MAIN_HAND, new BlockHitResult(Vec3d.ofCenter(pos), Direction.DOWN, new BlockPos(pos), false), 0)); - mc.getNetworkHandler().sendPacket(new ClickSlotC2SPacket(0,0,slot,0,SlotActionType.PICKUP,stack,new Int2ObjectArrayMap<>())); - mc.getNetworkHandler().sendPacket(new ClickSlotC2SPacket(0,0,slot,0,SlotActionType.PICKUP,stack,new Int2ObjectArrayMap<>())); - } - } diff --git a/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/mixin/MixinClientConnection.java b/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/mixin/MixinClientConnection.java new file mode 100644 index 000000000..839e11afa --- /dev/null +++ b/v1_21/src/main/java/me/aleksilassila/litematica/printer/v1_21/mixin/MixinClientConnection.java @@ -0,0 +1,40 @@ +package me.aleksilassila.litematica.printer.v1_21.mixin; + +import io.netty.channel.ChannelHandlerContext; +import me.aleksilassila.litematica.printer.v1_21.LitematicaMixinMod; +import me.aleksilassila.litematica.printer.v1_21.Printer; +import me.aleksilassila.litematica.printer.v1_21.config.PrinterConfig; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.network.ClientConnection; +import net.minecraft.network.packet.Packet; +import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket; +import net.minecraft.screen.PlayerScreenHandler; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ClientConnection.class) +public class MixinClientConnection { + @Inject(method = "channelRead0*", at = @At("HEAD"), cancellable = true) + private void channelReadPre(ChannelHandlerContext channelHandlerContext, Packet packet, CallbackInfo callback) { + if (!LitematicaMixinMod.PRINT_MODE.getBooleanValue() && !LitematicaMixinMod.PRINT.getKeybind().isPressed()) { + return; + } + if (!PrinterConfig.PRINTER_AIRPLACE.getBooleanValue() || !PrinterConfig.PRINTER_AIRPLACE_OFFHAND_SLOT_SUPPRESS.getBooleanValue()) { + return; + } + if (Printer.inactivityCounter > 20) { + return; + } + if (packet instanceof ScreenHandlerSlotUpdateS2CPacket packet1) { + if (packet1.getSyncId() == -2 && packet1.getSlot() == PlayerInventory.OFF_HAND_SLOT) { + callback.cancel(); + } else if (packet1.getSyncId() == 0 && PlayerScreenHandler.isInHotbar(packet1.getSyncId())) { + if (packet1.getSlot() == PlayerScreenHandler.OFFHAND_ID) { + callback.cancel(); + } + } + } + } +} diff --git a/v1_21/src/main/resources/litematica-printer.mixins.json b/v1_21/src/main/resources/litematica-printer.mixins.json index a9847b02d..6e6b122d4 100644 --- a/v1_21/src/main/resources/litematica-printer.mixins.json +++ b/v1_21/src/main/resources/litematica-printer.mixins.json @@ -4,6 +4,7 @@ "package": "me.aleksilassila.litematica.printer.v1_21.mixin", "compatibilityLevel": "JAVA_16", "mixins": [ + "MixinClientConnection" ], "client": [ "AxeItemAccessor",