From e2dd59a77711ccf06b7631a48eeaa1e73cf99c09 Mon Sep 17 00:00:00 2001 From: Raycoms Date: Sun, 1 Mar 2026 13:12:53 +0100 Subject: [PATCH 1/8] rework world equals checks (#818) --- .../structurize/client/gui/WindowScan.java | 1 + .../placement/AbstractBlueprintIterator.java | 6 +- .../placement/IPlacementContext.java | 45 ++ .../placement/SimplePlacementContext.java | 62 +++ .../placement/StructurePlacer.java | 40 +- .../placement/DoBlockPlacementHandler.java | 71 ++- .../DoDoorBlockPlacementHandler.java | 131 +++++ .../handlers/placement/IPlacementHandler.java | 118 ++-- .../handlers/placement/PlacementHandlers.java | 523 +++++++++++++----- .../structure/AbstractStructureHandler.java | 2 +- .../structure/IStructureHandler.java | 20 +- .../ldtteam/structurize/util/BlockUtils.java | 3 +- 12 files changed, 755 insertions(+), 267 deletions(-) create mode 100644 src/main/java/com/ldtteam/structurize/placement/IPlacementContext.java create mode 100644 src/main/java/com/ldtteam/structurize/placement/SimplePlacementContext.java create mode 100644 src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoDoorBlockPlacementHandler.java diff --git a/src/main/java/com/ldtteam/structurize/client/gui/WindowScan.java b/src/main/java/com/ldtteam/structurize/client/gui/WindowScan.java index 9a47e4b206..3805e12eb1 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/WindowScan.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/WindowScan.java @@ -12,6 +12,7 @@ import com.ldtteam.structurize.client.gui.util.InputFilters; import com.ldtteam.structurize.client.gui.util.ItemPositionsStorage; import com.ldtteam.structurize.network.messages.*; +import com.ldtteam.structurize.placement.SimplePlacementContext; import com.ldtteam.structurize.placement.handlers.placement.IPlacementHandler; import com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers; import com.ldtteam.structurize.storage.rendering.RenderingCache; diff --git a/src/main/java/com/ldtteam/structurize/placement/AbstractBlueprintIterator.java b/src/main/java/com/ldtteam/structurize/placement/AbstractBlueprintIterator.java index 22089ba705..81b07872f1 100644 --- a/src/main/java/com/ldtteam/structurize/placement/AbstractBlueprintIterator.java +++ b/src/main/java/com/ldtteam/structurize/placement/AbstractBlueprintIterator.java @@ -1,7 +1,7 @@ package com.ldtteam.structurize.placement; +import com.ldtteam.structurize.placement.handlers.placement.IPlacementHandler; import com.ldtteam.structurize.placement.structure.IStructureHandler; -import com.ldtteam.structurize.util.BlockUtils; import com.ldtteam.structurize.util.BlueprintPositionInfo; import net.minecraft.core.BlockPos; import net.minecraftforge.common.util.TriPredicate; @@ -106,9 +106,7 @@ private Result iterateWithCondition(final TriPredicate virtualBlocks); + + /** + * Get the world position this is placed at. + * @return the position. + */ + BlockPos getCenterPos(); + + /** + * Get the bluerint from the handler. + * @return the blueprint + */ + Blueprint getBluePrint(); +} diff --git a/src/main/java/com/ldtteam/structurize/placement/SimplePlacementContext.java b/src/main/java/com/ldtteam/structurize/placement/SimplePlacementContext.java new file mode 100644 index 0000000000..4e30d96254 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/placement/SimplePlacementContext.java @@ -0,0 +1,62 @@ +package com.ldtteam.structurize.placement; + +import com.ldtteam.structurize.api.RotationMirror; +import com.ldtteam.structurize.blueprints.v1.Blueprint; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Function; + +/** + * Simple placement context for non blueprint handling. + */ +public class SimplePlacementContext implements IPlacementContext +{ + /** + * If placement should be fancy or complete. + */ + private final boolean fancyPlacement; + + /** + * Rotation mirror. + */ + private final RotationMirror rotationMirror; + + public SimplePlacementContext(final boolean fancyPlacement, final RotationMirror rotationMirror) + { + this.fancyPlacement = fancyPlacement; + this.rotationMirror = rotationMirror; + } + + @Override + public RotationMirror getRotationMirror() + { + return rotationMirror; + } + + @Override + public boolean fancyPlacement() + { + return fancyPlacement; + } + + @Override + public BlockState getSolidBlockForPos(final BlockPos worldPos, final Function virtualBlocks) + { + return Blocks.DIRT.defaultBlockState(); + } + + @Override + public BlockPos getCenterPos() + { + return BlockPos.ZERO; + } + + @Override + public Blueprint getBluePrint() + { + return null; + } +} diff --git a/src/main/java/com/ldtteam/structurize/placement/StructurePlacer.java b/src/main/java/com/ldtteam/structurize/placement/StructurePlacer.java index 74f6780736..dad5833d93 100644 --- a/src/main/java/com/ldtteam/structurize/placement/StructurePlacer.java +++ b/src/main/java/com/ldtteam/structurize/placement/StructurePlacer.java @@ -8,6 +8,7 @@ import com.ldtteam.structurize.placement.handlers.placement.IPlacementHandler; import com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers; import com.ldtteam.structurize.placement.structure.IStructureHandler; +import com.ldtteam.structurize.util.BlockInfo; import com.ldtteam.structurize.util.BlockUtils; import com.ldtteam.structurize.util.ChangeStorage; import net.minecraft.core.BlockPos; @@ -169,7 +170,7 @@ public StructurePhasePlacementResult executeStructureStep( result = handleEntitySpawn(world, worldPos, localPos, storage); break; default: - result = handleBlockPlacement(world, worldPos, localPos, storage, localState, handler.getBluePrint().getTileEntityData(worldPos, localPos)); + result = handleBlockPlacement(world, worldPos, storage, new BlockInfo(localPos, localState, handler.getBluePrint().getTileEntityData(worldPos, localPos))); } count++; @@ -208,19 +209,19 @@ public StructurePhasePlacementResult executeStructureStep( * When we extract this into another mod, we have to override the method. * @param world the world. * @param worldPos the world position. - * @param localPos the local pos * @param storage the change storage. - * @param localState the local state. - * @param tileEntityData the tileEntity. + * @param blockInfo the tileEntity. */ public BlockPlacementResult handleBlockPlacement( final Level world, final BlockPos worldPos, - final BlockPos localPos, final ChangeStorage storage, - BlockState localState, - CompoundTag tileEntityData) + final BlockInfo blockInfo) { + BlockState localState = blockInfo.getState(); + CompoundTag tileEntityData = blockInfo.getTileEntityData(); + final BlockPos localPos = blockInfo.getPos(); + final BlockState worldState = world.getBlockState(worldPos); boolean sameBlockInWorld = false; if (worldState.getBlock() == localState.getBlock() && tileEntityData == null) @@ -314,16 +315,6 @@ else if (requiredItems == null) } } - BlockEntity worldEntity = null; - if (tileEntityData != null) - { - worldEntity = world.getBlockEntity(worldPos); - } - - if (localState.getBlock() == ModBlocks.blockSolidSubstitution.get() && handler.fancyPlacement()) - { - localState = this.handler.getSolidBlockForPos(worldPos, handler.getBluePrint().getRawBlockStateFunction().compose(handler::getStructurePosFromWorld)); - } if (localState.getBlock() == ModBlocks.blockTagSubstitution.get() && handler.fancyPlacement()) { if (tileEntityData != null && BlockEntity.loadStatic(localPos, localState, tileEntityData) instanceof BlockEntityTagSubstitution tagEntity) @@ -337,7 +328,7 @@ else if (requiredItems == null) } } - if (BlockUtils.areBlockStatesEqual(localState, worldState, handler::replaceWithSolidBlock, handler.fancyPlacement(), handler::shouldBlocksBeConsideredEqual, tileEntityData, worldEntity)) + if (IPlacementHandler.doesWorldStateMatchBlueprintState(blockInfo, worldPos, this.handler)) { return new BlockPlacementResult(worldPos, BlockPlacementResult.Result.SUCCESS); } @@ -349,7 +340,7 @@ else if (requiredItems == null) if (!sameBlockInWorld && !this.handler.isCreative()) { - for (final ItemStack stack : placementHandler.getRequiredItems(world, worldPos, localState, tileEntityData, false)) + for (final ItemStack stack : placementHandler.getRequiredItems(world, worldPos, localState, tileEntityData, handler)) { if (!stack.isEmpty() && !this.handler.isStackFree(stack)) { @@ -375,7 +366,7 @@ else if (requiredItems == null) this.handler.prePlacementLogic(worldPos, localState, requiredItems); - final IPlacementHandler.ActionProcessingResult result = placementHandler.handle(getHandler().getBluePrint(), world, worldPos, localState, tileEntityData, !this.handler.fancyPlacement(), this.handler.getWorldPos(), this.handler.getSettings()); + final IPlacementHandler.ActionProcessingResult result = placementHandler.handle(world, worldPos, localState, tileEntityData, this.handler); if (result == IPlacementHandler.ActionProcessingResult.DENY) { placementHandler.handleRemoval(handler, world, worldPos, tileEntityData); @@ -417,7 +408,7 @@ public BlockPlacementResult handleEntitySpawn( { try { - final BlockPos pos = this.handler.getWorldPos().subtract(handler.getBluePrint().getPrimaryBlockOffset()); + final BlockPos pos = this.handler.getCenterPos().subtract(handler.getBluePrint().getPrimaryBlockOffset()); final Optional> type = EntityType.by(compound); if (type.isPresent()) @@ -627,10 +618,7 @@ public BlockPlacementResult getResourceRequirements( { worldEntity = world.getBlockEntity(worldPos); } - if (localState.getBlock() == ModBlocks.blockSolidSubstitution.get() && handler.fancyPlacement()) - { - localState = this.handler.getSolidBlockForPos(worldPos, handler.getBluePrint().getRawBlockStateFunction().compose(handler::getStructurePosFromWorld)); - } + if (localState.getBlock() == ModBlocks.blockTagSubstitution.get() && handler.fancyPlacement()) { if (tileEntityData != null && BlockEntity.loadStatic(localPos, localState, tileEntityData) instanceof BlockEntityTagSubstitution tagEntity) @@ -652,7 +640,7 @@ public BlockPlacementResult getResourceRequirements( final IPlacementHandler placementHandler = PlacementHandlers.getHandler(world, worldPos, localState); if (!sameBlockInWorld) { - for (final ItemStack stack : placementHandler.getRequiredItems(world, worldPos, localState, tileEntityData, false)) + for (final ItemStack stack : placementHandler.getRequiredItems(world, worldPos, localState, tileEntityData, handler)) { if (!stack.isEmpty() && !this.handler.isStackFree(stack)) { diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoBlockPlacementHandler.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoBlockPlacementHandler.java index 75f47bd666..fe35b21990 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoBlockPlacementHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoBlockPlacementHandler.java @@ -2,6 +2,8 @@ import com.ldtteam.domumornamentum.block.IMateriallyTexturedBlock; import com.ldtteam.domumornamentum.block.decorative.PillarBlock; +import com.ldtteam.domumornamentum.client.model.data.MaterialTextureData; +import com.ldtteam.domumornamentum.entity.block.MateriallyTexturedBlockEntity; import com.ldtteam.domumornamentum.util.BlockUtils; import com.ldtteam.structurize.api.util.ItemStackUtils; import com.ldtteam.structurize.api.util.Log; @@ -11,11 +13,14 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtOps; +import net.minecraft.util.Tuple; import net.minecraft.world.InteractionHand; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.FenceBlock; +import net.minecraft.world.level.block.FenceGateBlock; import net.minecraft.world.level.block.IronBarsBlock; import net.minecraft.world.level.block.WallBlock; import net.minecraft.world.level.block.entity.BlockEntity; @@ -46,13 +51,13 @@ public ActionProcessingResult handle( @NotNull final BlockPos pos, @NotNull final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete, - final BlockPos centerPos, - final PlacementSettings settings) + @NotNull final IPlacementContext placementContext) { BlockState placementState = blockState; - if (blockState.getBlock() instanceof WallBlock || blockState.getBlock() instanceof FenceBlock || blockState.getBlock() instanceof PillarBlock - || blockState.getBlock() instanceof IronBarsBlock) + if (blockState.getBlock() instanceof WallBlock + || blockState.getBlock() instanceof FenceBlock + || blockState.getBlock() instanceof IronBarsBlock + || blockState.getBlock() instanceof PillarBlock) { try { @@ -72,22 +77,8 @@ public ActionProcessingResult handle( if (world.getBlockState(pos).equals(placementState)) { + // Need to remove first to be able to place over. world.removeBlock(pos, false); - world.setBlock(pos, placementState, UPDATE_FLAG); - if (tileEntityData != null) - { - try - { - handleTileEntityPlacement(tileEntityData, world, pos, settings); - placementState.getBlock().setPlacedBy(world, pos, placementState, null, placementState.getBlock().getCloneItemStack(placementState, - new BlockHitResult(new Vec3(0, 0, 0), Direction.NORTH, pos, false), world, pos, null)); - } - catch (final Exception ex) - { - Log.getLogger().warn("Unable to place TileEntity"); - } - } - return ActionProcessingResult.PASS; } if (!world.setBlock(pos, placementState, UPDATE_FLAG)) @@ -99,8 +90,8 @@ public ActionProcessingResult handle( { try { - handleTileEntityPlacement(tileEntityData, world, pos, settings); - blockState.getBlock().setPlacedBy(world, pos, placementState, null, placementState.getBlock().getCloneItemStack(placementState, + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); + placementState.getBlock().setPlacedBy(world, pos, placementState, null, placementState.getBlock().getCloneItemStack(placementState, new BlockHitResult(new Vec3(0, 0, 0), Direction.NORTH, pos, false), world, pos, null)); } catch (final Exception ex) @@ -112,13 +103,46 @@ public ActionProcessingResult handle( return ActionProcessingResult.SUCCESS; } + @Override + public boolean doesWorldStateMatchBlueprintState(final BlockState worldState, final BlockState blueprintState, final Tuple blockEntityData, final @NotNull IPlacementContext structureHandler) + { + if (blueprintState.getBlock() == worldState.getBlock() + && (blueprintState.getBlock() instanceof WallBlock + || blueprintState.getBlock() instanceof FenceBlock + || blueprintState.getBlock() instanceof IronBarsBlock + || blueprintState.getBlock() instanceof FenceGateBlock) + && compareBEData(blockEntityData)) + { + return true; + } + + return worldState.equals(blueprintState) && compareBEData(blockEntityData); + } + + /** + * Check if the Block Entity data matches for DO blocks. + * @param blockEntityData check on the block entity textures. + * @return true if so. + */ + public static boolean compareBEData(final Tuple blockEntityData) + { + if (blockEntityData != null) + { + if (blockEntityData.getA() instanceof final MateriallyTexturedBlockEntity mtbe && blockEntityData.getB().contains(BLOCK_ENTITY_TEXTURE_DATA)) + { + return mtbe.getTextureData().equals(MaterialTextureData.CODEC.decode(NbtOps.INSTANCE, blockEntityData.getB().get(BLOCK_ENTITY_TEXTURE_DATA)).getOrThrow().getFirst()); + } + } + return false; + } + @Override public List getRequiredItems( @NotNull final Level world, @NotNull final BlockPos pos, @NotNull final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete) + @NotNull final IPlacementContext placementContext) { final List itemList = new ArrayList<>(); if (tileEntityData != null) @@ -152,3 +176,4 @@ public void handleRemoval( world.removeBlock(pos, false); } } + diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoDoorBlockPlacementHandler.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoDoorBlockPlacementHandler.java new file mode 100644 index 0000000000..55f3890ab0 --- /dev/null +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoDoorBlockPlacementHandler.java @@ -0,0 +1,131 @@ +package com.ldtteam.structurize.placement.handlers.placement; + +import com.ldtteam.domumornamentum.block.AbstractBlockDoor; +import com.ldtteam.domumornamentum.block.IMateriallyTexturedBlock; +import com.ldtteam.domumornamentum.util.BlockUtils; +import com.ldtteam.structurize.api.ItemStackUtils; +import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.placement.IPlacementContext; +import com.ldtteam.structurize.placement.structure.IStructureHandler; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.Tuple; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; +import net.minecraft.world.level.block.state.properties.Property; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static com.ldtteam.structurize.api.constants.Constants.UPDATE_FLAG; +import static com.ldtteam.structurize.placement.handlers.placement.DoBlockPlacementHandler.compareBEData; +import static com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers.handleTileEntityPlacement; + +public class DoDoorBlockPlacementHandler implements IPlacementHandler +{ + @Override + public boolean canHandle(@NotNull final Level world, @NotNull final BlockPos pos, @NotNull final BlockState blockState) + { + return blockState.getBlock() instanceof IMateriallyTexturedBlock && blockState.getBlock() instanceof AbstractBlockDoor; + } + + @Override + public ActionProcessingResult handle( + @NotNull final Level world, + @NotNull final BlockPos pos, + @NotNull final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + @NotNull final IPlacementContext placementContext) + { + if (blockState.getValue(net.minecraft.world.level.block.DoorBlock.HALF).equals(DoubleBlockHalf.LOWER)) + { + if (world.getBlockState(pos).equals(blockState)) + { + world.removeBlock(pos, false); + world.removeBlock(pos.above(), false); + } + + world.setBlock(pos, blockState.setValue(net.minecraft.world.level.block.DoorBlock.HALF, DoubleBlockHalf.LOWER), UPDATE_FLAG); + world.setBlock(pos.above(), blockState.setValue(net.minecraft.world.level.block.DoorBlock.HALF, DoubleBlockHalf.UPPER), UPDATE_FLAG); + + if (tileEntityData != null) + { + try + { + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); + handleTileEntityPlacement(tileEntityData, world, pos.above(), placementContext.getRotationMirror()); + } + catch (final Exception ex) + { + Log.getLogger().warn("Unable to place TileEntity"); + } + } + } + return ActionProcessingResult.SUCCESS; + } + + @Override + public List getRequiredItems( + @NotNull final Level world, + @NotNull final BlockPos pos, + @NotNull final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + @NotNull final IPlacementContext placementContext) + { + final List itemList = new ArrayList<>(); + if (tileEntityData != null && blockState.getValue(net.minecraft.world.level.block.DoorBlock.HALF).equals(DoubleBlockHalf.LOWER)) + { + BlockPos blockpos = new BlockPos(tileEntityData.getInt("x"), tileEntityData.getInt("y"), tileEntityData.getInt("z")); + final BlockEntity tileEntity = BlockEntity.loadStatic(blockpos, blockState, tileEntityData, world.registryAccess()); + if (tileEntity == null) + { + return Collections.emptyList(); + } + + itemList.add(BlockUtils.getMaterializedItemStack(tileEntity, world.registryAccess())); + } + itemList.removeIf(ItemStackUtils::isEmpty); + return itemList; + } + + @Override + public boolean doesWorldStateMatchBlueprintState( + final BlockState worldState, + final BlockState blueprintState, + final Tuple blockEntityData, + final @NotNull IPlacementContext structureHandler) + { + if (worldState.getBlock() == blueprintState.getBlock()) + { + if (structureHandler.fancyPlacement()) + { + for (Property property : worldState.getProperties()) + { + // Compare properties, but if just open or powered don't match, ignore. + if (!blueprintState.hasProperty(property) || + (blueprintState.getValue(property) != worldState.getValue(property) + && property != net.minecraft.world.level.block.DoorBlock.OPEN) + && property != net.minecraft.world.level.block.DoorBlock.POWERED) + { + return false; + } + } + } + else + { + if (!worldState.equals(blueprintState)) + { + return false; + } + } + } + return compareBEData(blockEntityData); + } +} + diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/IPlacementHandler.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/IPlacementHandler.java index f974b78710..198ff567e9 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/IPlacementHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/IPlacementHandler.java @@ -1,15 +1,19 @@ package com.ldtteam.structurize.placement.handlers.placement; -import com.ldtteam.structurize.blueprints.v1.Blueprint; +import com.ldtteam.structurize.placement.IPlacementContext; import com.ldtteam.structurize.placement.structure.IStructureHandler; +import com.ldtteam.structurize.util.BlockInfo; import com.ldtteam.structurize.util.BlockUtils; import com.ldtteam.structurize.util.InventoryUtils; import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.Tuple; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; @@ -19,6 +23,38 @@ */ public interface IPlacementHandler { + static boolean doesWorldStateMatchBlueprintState(@NotNull BlockInfo blockInfo, @NotNull BlockPos worldPos, @NotNull IStructureHandler structureHandler) + { + // If we're doing full paste, do a more simple comparison. That saves us a lot of logic in the placement handlers. + if (!structureHandler.fancyPlacement()) + { + if (blockInfo.hasTileEntityData()) + { + // Always update tile entities in here. + return false; + } + return blockInfo.getState().equals(structureHandler.getWorld().getBlockState(blockInfo.getPos())); + } + + final IPlacementHandler placementHandler = PlacementHandlers.getHandler(structureHandler.getWorld(), worldPos, blockInfo.getState()); + Tuple blockEntityData = null; + if (blockInfo.hasTileEntityData()) + { + final BlockEntity blockEntity = structureHandler.getWorld().getBlockEntity(worldPos); + if (blockEntity == null) + { + return false; + } + blockEntityData = new Tuple<>(blockEntity, blockInfo.getTileEntityData()); + } + + //todo domum blocks on minecol side + //todo minecolonies racks on minecol side + //todo minecolonies allow conversion of dirt like to dirt + + return placementHandler.doesWorldStateMatchBlueprintState(structureHandler.getWorld().getBlockState(worldPos), blockInfo.getState(), blockEntityData, structureHandler); + } + /** * Check if a placement handler can handle a certain block. * @@ -33,70 +69,18 @@ public interface IPlacementHandler * Method used to handle the processing of a Placement of a block. * * @param world receives the world. - * @param pos the position. + * @param worldPos the position. * @param blockState the blockState. * @param tileEntityData the placer of the block. - * @param complete place it complete (with or without substitution blocks etc). - * @param centerPos centerPos the central position of it. + * @param placementContext extended placement context. * @return ACCEPT, DENY or IGNORE. */ - default ActionProcessingResult handle( + ActionProcessingResult handle( final Level world, - final BlockPos pos, + final BlockPos worldPos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete, final BlockPos centerPos) - { - /* - * Do nothing... - */ - return ActionProcessingResult.PASS; - } - - /** - * Method used to handle the processing of a Placement of a block. - * - * @param world receives the world. - * @param pos the position. - * @param blockState the blockState. - * @param tileEntityData the placer of the block. - * @param complete place it complete (with or without substitution blocks etc). - * @param centerPos the central position of it. - * @param settings the settings to use to rotate or mirror it. - * @return ACCEPT, DENY or IGNORE. - */ - default ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final boolean complete, final BlockPos centerPos, final PlacementSettings settings) - { - return handle(world, pos, blockState, tileEntityData, complete, centerPos); - } - - /** - * Method used to handle the processing of a Placement of a block. - * - * @param blueprint the blueprint. - * @param world receives the world. - * @param pos the position. - * @param blockState the blockState. - * @param tileEntityData the placer of the block. - * @param complete place it complete (with or without substitution blocks etc). - * @param centerPos the central position of it. - * @param settings the settings to use to rotate or mirror it. - * @return ACCEPT, DENY or IGNORE. - */ - default ActionProcessingResult handle(final Blueprint blueprint, - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final boolean complete, final BlockPos centerPos, final PlacementSettings settings) - { - return handle(world, pos, blockState, tileEntityData, complete, centerPos,settings); - } + @NotNull final IPlacementContext placementContext); /** * Handles the removal of the existing block in the world @@ -145,15 +129,21 @@ default void handleRemoval( * @param pos the position. * @param blockState the blockState. * @param tileEntityData the placer of the block. - * @param complete place it complete (with or without substitution blocks etc). + * @param placementContext the placement context. * @return the list of items. */ List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final boolean complete); + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + @NotNull final IPlacementContext placementContext); + + /** + * Method used to compare blueprint state with world state to detect if changes are necessary. + * @return true if world state matches blueprint state. + */ + boolean doesWorldStateMatchBlueprintState(final BlockState worldState, final BlockState blueprintState, @Nullable final Tuple blockEntityData, @NotNull final IPlacementContext placementContext); /** * Possible result of an IPlacementHandler call. diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java index 0ad3c999b5..77ab3b2d9c 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java @@ -4,8 +4,8 @@ import com.ldtteam.structurize.api.util.ItemStackUtils; import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.blocks.ModBlocks; -import com.ldtteam.structurize.blocks.schematic.BlockFluidSubstitution; import com.ldtteam.structurize.blueprints.v1.Blueprint; +import com.ldtteam.structurize.placement.IPlacementContext; import com.ldtteam.structurize.placement.structure.IStructureHandler; import com.ldtteam.structurize.tag.ModTags; import com.ldtteam.structurize.util.BlockUtils; @@ -14,6 +14,7 @@ import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.tags.BlockTags; +import net.minecraft.util.Tuple; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.item.ItemEntity; @@ -23,11 +24,9 @@ import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.BedPart; -import net.minecraft.world.level.block.state.properties.BlockStateProperties; -import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; -import net.minecraft.world.level.block.state.properties.DripstoneThickness; +import net.minecraft.world.level.block.state.properties.*; import net.minecraft.world.phys.AABB; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -59,13 +58,15 @@ public final class PlacementHandlers handlers.add(new DoublePlantPlacementHandler()); handlers.add(new SpecialBlockPlacementAttemptHandler()); handlers.add(new FlowerPotPlacementHandler()); - handlers.add(new StairBlockPlacementHandler()); handlers.add(new HopperClientLagPlacementHandler()); handlers.add(new ContainerPlacementHandler()); handlers.add(new DripStoneBlockPlacementHandler()); handlers.add(new FallingBlockPlacementHandler()); handlers.add(new BannerPlacementHandler()); handlers.add(new DoBlockPlacementHandler()); + handlers.add(new DoDoorBlockPlacementHandler()); + handlers.add(new SolidSubstitutionPlacementHandler()); + handlers.add(new SubstitutionPlacementHandler()); handlers.add(new GeneralBlockPlacementHandler()); } @@ -97,8 +98,14 @@ public static void add(IPlacementHandler handler, Class override) public static void add(IPlacementHandler handler) { handlers.add(1, handler); + handlerCache.clear(); } + /** + * Simple block based handler cache to avoid too many iterations + */ + private static Map handlerCache = new IdentityHashMap<>(128); + /** * Finds the appropriate {@link IPlacementHandler} for the given location. * @param world the world. @@ -110,10 +117,18 @@ public static IPlacementHandler getHandler(final Level world, final BlockPos worldPos, final BlockState newState) { + final Block block = newState.getBlock(); + final IPlacementHandler cached = handlerCache.get(block); + if (cached != null) + { + return cached; + } + for (final IPlacementHandler placementHandler : handlers) { if (placementHandler.canHandle(world, worldPos, newState)) { + handlerCache.put(block, placementHandler); return placementHandler; } } @@ -137,7 +152,7 @@ public static class FluidSubstitutionPlacementHandler implements IPlacementHandl @Override public boolean canHandle(Level world, BlockPos pos, BlockState blockState) { - return blockState.getBlock() instanceof BlockFluidSubstitution; + return blockState.getBlock() == ModBlocks.blockFluidSubstitution.get(); } @Override @@ -146,11 +161,11 @@ public List getRequiredItems( BlockPos pos, BlockState blockState, @Nullable CompoundTag tileEntityData, - boolean complete) + IPlacementContext placementContext) { List items = new ArrayList<>(); - if (complete) + if (!placementContext.fancyPlacement()) { // for scan tool, show the actual placeholder block items.add(new ItemStack(blockState.getBlock())); @@ -190,10 +205,9 @@ public ActionProcessingResult handle( BlockPos pos, BlockState blockState, @Nullable CompoundTag tileEntityData, - boolean complete, - BlockPos centerPos) + IPlacementContext placementContext) { - if (complete) + if (!placementContext.fancyPlacement()) { world.setBlock(pos, ModBlocks.blockFluidSubstitution.get().defaultBlockState(), UPDATE_FLAG); return ActionProcessingResult.PASS; @@ -210,6 +224,19 @@ public ActionProcessingResult handle( return ActionProcessingResult.PASS; } + + @Override + public boolean doesWorldStateMatchBlueprintState( + final BlockState worldState, + final BlockState blueprintState, + final Tuple blockEntityData, + final @NotNull IPlacementContext structureHandler) + { + // If is source keep, if water logged, keep, if full solid, keep. (Replace primarily if air, plant, etc). + return worldState.getFluidState().isSource() + || worldState.hasProperty(BlockStateProperties.WATERLOGGED) + || BlockUtils.isAnySolid(worldState); + } } public static class FirePlacementHandler implements IPlacementHandler @@ -226,7 +253,7 @@ public List getRequiredItems( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete) + final IPlacementContext placementContext) { final List itemList = new ArrayList<>(); itemList.add(new ItemStack(Items.FLINT_AND_STEEL, 1)); @@ -239,12 +266,21 @@ public ActionProcessingResult handle( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete, - final BlockPos centerPos) + final IPlacementContext placementContext) { world.setBlock(pos, blockState, UPDATE_FLAG); return ActionProcessingResult.PASS; } + + @Override + public boolean doesWorldStateMatchBlueprintState( + final BlockState worldState, + final BlockState blueprintState, + final Tuple blockEntityData, + final @NotNull IPlacementContext structureHandler) + { + return worldState.equals(blueprintState); + } } public static class FallingBlockPlacementHandler implements IPlacementHandler @@ -261,7 +297,7 @@ public List getRequiredItems( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete) + final IPlacementContext placementContext) { final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState)); itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); @@ -291,7 +327,7 @@ public List getRequiredItems( } else { - itemList.addAll(getRequiredItemsForState(world, pos, supportBlockState, tileEntityData, complete)); + itemList.addAll(getRequiredItemsForState(world, pos, supportBlockState, tileEntityData, placementContext)); } } return itemList; @@ -303,9 +339,7 @@ public ActionProcessingResult handle( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete, - final BlockPos centerPos, - final PlacementSettings settings) + final IPlacementContext placementContext) { if (world.getBlockState(pos).equals(blockState)) { @@ -336,11 +370,21 @@ public ActionProcessingResult handle( if (tileEntityData != null) { - handleTileEntityPlacement(tileEntityData, world, pos, settings); + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); } return ActionProcessingResult.SUCCESS; } + + @Override + public boolean doesWorldStateMatchBlueprintState( + final BlockState worldState, + final BlockState blueprintState, + final Tuple blockEntityData, + final @NotNull IPlacementContext structureHandler) + { + return worldState.equals(blueprintState); + } } public static class GrassPlacementHandler implements IPlacementHandler @@ -348,7 +392,7 @@ public static class GrassPlacementHandler implements IPlacementHandler @Override public boolean canHandle(final Level world, final BlockPos pos, final BlockState blockState) { - return blockState.getBlock() == Blocks.GRASS_BLOCK || (blockState.getBlock() != Blocks.DIRT && blockState.is(BlockTags.DIRT) && Blocks.DIRT == blockState.getBlock()); + return blockState.getBlock() == Blocks.GRASS_BLOCK; } @Override @@ -357,8 +401,7 @@ public ActionProcessingResult handle( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete, - final BlockPos centerPos) + final IPlacementContext placementContext) { if (!world.setBlock(pos, blockState, UPDATE_FLAG)) { @@ -373,14 +416,24 @@ public List getRequiredItems( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete) + final IPlacementContext placementContext) { - if (complete) + if (!placementContext.fancyPlacement()) { return Collections.singletonList(new ItemStack(blockState.getBlock())); } return Collections.singletonList(new ItemStack(Blocks.DIRT)); } + + @Override + public boolean doesWorldStateMatchBlueprintState( + final BlockState worldState, + final BlockState blueprintState, + final Tuple blockEntityData, + final @NotNull IPlacementContext placementContext) + { + return worldState.is(BlockTags.DIRT); + } } public static class DoorPlacementHandler implements IPlacementHandler @@ -397,8 +450,7 @@ public ActionProcessingResult handle( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete, - final BlockPos centerPos) + final IPlacementContext placementContext) { if (blockState.getValue(DoorBlock.HALF).equals(DoubleBlockHalf.LOWER)) { @@ -415,7 +467,7 @@ public List getRequiredItems( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete) + final IPlacementContext placementContext) { final List itemList = new ArrayList<>(); if (blockState.getValue(DoorBlock.HALF).equals(DoubleBlockHalf.LOWER)) @@ -424,6 +476,38 @@ public List getRequiredItems( } return itemList; } + + @Override + public boolean doesWorldStateMatchBlueprintState( + final BlockState worldState, + final BlockState blueprintState, + final Tuple blockEntityData, + final @NotNull IPlacementContext structureHandler) + { + if (worldState.getBlock() == blueprintState.getBlock()) + { + if (structureHandler.fancyPlacement()) + { + for (Property property : worldState.getProperties()) + { + // Compare properties, but if just open or powered don't match, ignore. + if (!blueprintState.hasProperty(property) || + (blueprintState.getValue(property) != worldState.getValue(property) + && property != net.minecraft.world.level.block.DoorBlock.OPEN) + && property != net.minecraft.world.level.block.DoorBlock.POWERED) + { + return false; + } + } + } + else + { + return worldState.equals(blueprintState); + } + return true; + } + return false; + } } public static class BedPlacementHandler implements IPlacementHandler @@ -440,9 +524,7 @@ public ActionProcessingResult handle( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete, - final BlockPos centerPos, - final PlacementSettings settings) + final IPlacementContext placementContext) { if (blockState.getValue(BedBlock.PART) == BedPart.HEAD) { @@ -454,8 +536,8 @@ public ActionProcessingResult handle( if (tileEntityData != null) { - handleTileEntityPlacement(tileEntityData, world, pos, settings); - handleTileEntityPlacement(tileEntityData, world, pos.relative(facing.getOpposite()), settings); + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); + handleTileEntityPlacement(tileEntityData, world, pos.relative(facing.getOpposite()), placementContext.getRotationMirror()); } return ActionProcessingResult.SUCCESS; } @@ -469,7 +551,7 @@ public List getRequiredItems( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete) + final IPlacementContext placementContext) { if (blockState.getValue(BedBlock.PART) == BedPart.HEAD) { @@ -479,6 +561,16 @@ public List getRequiredItems( } return Collections.emptyList(); } + + @Override + public boolean doesWorldStateMatchBlueprintState( + final BlockState worldState, + final BlockState blueprintState, + final Tuple blockEntityData, + final @NotNull IPlacementContext structureHandler) + { + return worldState.equals(blueprintState); + } } public static class DoublePlantPlacementHandler implements IPlacementHandler @@ -495,8 +587,7 @@ public ActionProcessingResult handle( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete, - final BlockPos centerPos) + final IPlacementContext placementContext) { if (blockState.getValue(DoublePlantBlock.HALF).equals(DoubleBlockHalf.LOWER)) { @@ -513,12 +604,22 @@ public List getRequiredItems( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete) + final IPlacementContext placementContext) { final List itemList = new ArrayList<>(); itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); return itemList; } + + @Override + public boolean doesWorldStateMatchBlueprintState( + final BlockState worldState, + final BlockState blueprintState, + final Tuple blockEntityData, + final @NotNull IPlacementContext structureHandler) + { + return worldState.equals(blueprintState); + } } public static class SpecialBlockPlacementAttemptHandler implements IPlacementHandler @@ -526,8 +627,9 @@ public static class SpecialBlockPlacementAttemptHandler implements IPlacementHan @Override public boolean canHandle(final Level world, final BlockPos pos, final BlockState blockState) { - return blockState.getBlock() instanceof EndPortalBlock || blockState.getBlock() instanceof SpawnerBlock || - blockState.getBlock() instanceof DragonEggBlock; + return blockState.getBlock() instanceof EndPortalBlock + || blockState.getBlock() instanceof SpawnerBlock + || blockState.getBlock() instanceof DragonEggBlock; } @Override @@ -536,8 +638,7 @@ public ActionProcessingResult handle( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete, - final BlockPos centerPos) + final IPlacementContext placementContext) { return ActionProcessingResult.PASS; } @@ -548,10 +649,20 @@ public List getRequiredItems( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete) + final IPlacementContext placementContext) { return new ArrayList<>(); } + + @Override + public boolean doesWorldStateMatchBlueprintState( + final BlockState worldState, + final BlockState blueprintState, + final Tuple blockEntityData, + final @NotNull IPlacementContext structureHandler) + { + return true; + } } public static class FlowerPotPlacementHandler implements IPlacementHandler @@ -568,14 +679,13 @@ public ActionProcessingResult handle( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete, - final BlockPos centerPos, - final PlacementSettings settings) + final IPlacementContext placementContext) { - if (world.getBlockState(pos).getBlock() == blockState.getBlock()) + if (world.getBlockState(pos).getBlock() == Blocks.FLOWER_POT) { - return ActionProcessingResult.PASS; + world.removeBlock(pos, false); } + if (!world.setBlock(pos, blockState, UPDATE_FLAG)) { return ActionProcessingResult.DENY; @@ -583,7 +693,7 @@ public ActionProcessingResult handle( if (tileEntityData != null) { - handleTileEntityPlacement(tileEntityData, world, pos, settings); + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); } return ActionProcessingResult.SUCCESS; } @@ -594,14 +704,30 @@ public List getRequiredItems( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete) + final IPlacementContext placementContext) { final List itemList = new ArrayList<>(); - itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); - itemList.add(new ItemStack(((FlowerPotBlock) blockState.getBlock()).getContent())); + if (world.getBlockState(pos).getBlock() != Blocks.FLOWER_POT) + { + itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); + } + itemList.add(new ItemStack(((FlowerPotBlock) blockState.getBlock()).getPotted())); itemList.removeIf(ItemStackUtils::isEmpty); return itemList; } + + @Override + public boolean doesWorldStateMatchBlueprintState( + final BlockState worldState, + final BlockState blueprintState, + final Tuple blockEntityData, + final @NotNull IPlacementContext structureHandler) + { + return worldState.equals(blueprintState) + && blueprintState.getBlock() instanceof FlowerPotBlock blueprintPot + && worldState.getBlock() instanceof FlowerPotBlock worldPot + && blueprintPot.getPotted() == worldPot.getPotted(); + } } public static class AirPlacementHandler implements IPlacementHandler @@ -609,7 +735,7 @@ public static class AirPlacementHandler implements IPlacementHandler @Override public boolean canHandle(final Level world, final BlockPos pos, final BlockState blockState) { - return blockState.getBlock() instanceof AirBlock; + return blockState.is(Blocks.AIR); } @Override @@ -618,8 +744,7 @@ public ActionProcessingResult handle( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete, - final BlockPos centerPos) + final IPlacementContext placementContext) { if (!world.isEmptyBlock(pos)) { @@ -644,10 +769,20 @@ public List getRequiredItems( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete) + final IPlacementContext placementContext) { return new ArrayList<>(); } + + @Override + public boolean doesWorldStateMatchBlueprintState( + final BlockState worldState, + final BlockState blueprintState, + final Tuple blockEntityData, + final @NotNull IPlacementContext structureHandler) + { + return worldState.is(Blocks.AIR); + } } public static class BlockGrassPathPlacementHandler implements IPlacementHandler @@ -664,8 +799,7 @@ public ActionProcessingResult handle( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete, - final BlockPos centerPos) + final IPlacementContext placementContext) { if (!world.setBlock(pos, Blocks.DIRT_PATH.defaultBlockState(), UPDATE_FLAG)) { @@ -681,54 +815,29 @@ public List getRequiredItems( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete) + final IPlacementContext placementContext) { - if (complete) + if (!placementContext.fancyPlacement()) { return Collections.singletonList(new ItemStack(blockState.getBlock())); } - return Collections.singletonList(new ItemStack(Blocks.DIRT)); - } - } - - public static class StairBlockPlacementHandler implements IPlacementHandler - { - @Override - public boolean canHandle(final Level world, final BlockPos pos, final BlockState blockState) - { - return blockState.getBlock() instanceof StairBlock - && !(blockState.getBlock() instanceof EntityBlock) - && world.getBlockState(pos).getBlock() instanceof StairBlock - && world.getBlockState(pos).getValue(StairBlock.FACING) == blockState.getValue(StairBlock.FACING) - && world.getBlockState(pos).getValue(StairBlock.HALF) == blockState.getValue(StairBlock.HALF) - && blockState.getBlock() == world.getBlockState(pos).getBlock(); - } - @Override - public ActionProcessingResult handle( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final boolean complete, - final BlockPos centerPos) - { - return ActionProcessingResult.PASS; + // It's free, if the world block is already dirt. + if (world.getBlockState(pos).is(Blocks.DIRT)) + { + return Collections.emptyList(); + } + return Collections.singletonList(new ItemStack(Blocks.DIRT)); } @Override - public List getRequiredItems( - final Level world, - final BlockPos pos, - final BlockState blockState, - @Nullable final CompoundTag tileEntityData, - final boolean complete) + public boolean doesWorldStateMatchBlueprintState( + final BlockState worldState, + final BlockState blueprintState, + final Tuple blockEntityData, + final @NotNull IPlacementContext structureHandler) { - if (complete) - { - return Collections.singletonList(new ItemStack(blockState.getBlock())); - } - return new ArrayList<>(); + return worldState.equals(blueprintState); } } @@ -746,9 +855,7 @@ public ActionProcessingResult handle( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete, - final BlockPos centerPos, - final PlacementSettings settings) + final IPlacementContext placementContext) { if (world.getBlockState(pos).equals(blockState)) { @@ -756,7 +863,7 @@ public ActionProcessingResult handle( world.setBlock(pos, blockState, UPDATE_FLAG); if (tileEntityData != null) { - handleTileEntityPlacement(tileEntityData, world, pos, settings); + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); } return ActionProcessingResult.PASS; } @@ -768,7 +875,7 @@ public ActionProcessingResult handle( if (tileEntityData != null) { - handleTileEntityPlacement(tileEntityData, world, pos, settings); + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); } return ActionProcessingResult.SUCCESS; @@ -780,13 +887,23 @@ public List getRequiredItems( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete) + final IPlacementContext placementContext) { final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState)); itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); itemList.removeIf(ItemStackUtils::isEmpty); return itemList; } + + @Override + public boolean doesWorldStateMatchBlueprintState( + final BlockState worldState, + final BlockState blueprintState, + final Tuple blockEntityData, + final @NotNull IPlacementContext structureHandler) + { + return worldState.equals(blueprintState); + } } public static class ContainerPlacementHandler implements IPlacementHandler @@ -803,9 +920,7 @@ public ActionProcessingResult handle( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete, - final BlockPos centerPos, - final PlacementSettings settings) + final IPlacementContext placementContext) { if (!world.setBlock(pos, blockState, UPDATE_FLAG)) { @@ -825,7 +940,7 @@ public ActionProcessingResult handle( if (tileEntityData != null) { - handleTileEntityPlacement(tileEntityData, world, pos, settings); + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); } return ActionProcessingResult.SUCCESS; @@ -837,7 +952,7 @@ public List getRequiredItems( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete) + final IPlacementContext placementContext) { final List itemList = new ArrayList<>(); itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); @@ -847,6 +962,16 @@ public List getRequiredItems( return itemList; } + + @Override + public boolean doesWorldStateMatchBlueprintState( + final BlockState worldState, + final BlockState blueprintState, + final Tuple blockEntityData, + final @NotNull IPlacementContext structureHandler) + { + return worldState.equals(blueprintState); + } } /** @@ -865,16 +990,14 @@ public ActionProcessingResult handle(final Level world, final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete, - final BlockPos centerPos) + final IPlacementContext placementContext) { final boolean flag = !world.hasNeighborSignal(pos); return super.handle(world, pos, flag != blockState.getValue(HopperBlock.ENABLED) ? blockState.setValue(HopperBlock.ENABLED, flag) : blockState, tileEntityData, - complete, - centerPos); + placementContext); } } @@ -892,15 +1015,13 @@ public ActionProcessingResult handle( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete, - final BlockPos centerPos, - final PlacementSettings settings) + final IPlacementContext placementContext) { if (world.getBlockState(pos).equals(blockState)) { if (tileEntityData != null) { - handleTileEntityPlacement(tileEntityData, world, pos, settings); + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); } return ActionProcessingResult.PASS; } @@ -912,7 +1033,7 @@ public ActionProcessingResult handle( if (tileEntityData != null) { - handleTileEntityPlacement(tileEntityData, world, pos, settings); + handleTileEntityPlacement(tileEntityData, world, pos, placementContext.getRotationMirror()); blockState.getBlock().setPlacedBy(world, pos, blockState, null, BlockUtils.getItemStackFromBlockState(blockState)); } @@ -925,13 +1046,23 @@ public List getRequiredItems( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete) + final IPlacementContext placementContext) { - final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState)); - itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); + { itemList.removeIf(ItemStackUtils::isEmpty); return itemList; } + + @Override + public boolean doesWorldStateMatchBlueprintState( + final BlockState worldState, + final BlockState blueprintState, + final Tuple blockEntityData, + final @NotNull IPlacementContext structureHandler) + { + // Always enter banner handling logic to make sure they're updated. + return false; + } } public static class DripStoneBlockPlacementHandler implements IPlacementHandler @@ -944,20 +1075,25 @@ public boolean canHandle(final Level world, final BlockPos pos, final BlockState @Override public ActionProcessingResult handle( - final Blueprint blueprint, final Level world, final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete, - final BlockPos centerPos, - final PlacementSettings settings) + final IPlacementContext placementContext) { + final BlockPos centerPos = placementContext.getCenterPos(); + final Blueprint blueprint = placementContext.getBluePrint(); if (world.getBlockState(pos).equals(blockState)) { return ActionProcessingResult.PASS; } + if (blueprint == null) + { + world.setBlock(pos, blockState, UPDATE_FLAG); + return ActionProcessingResult.SUCCESS; + } + if (blockState.getValue(PointedDripstoneBlock.THICKNESS) != DripstoneThickness.TIP && blockState.getValue(PointedDripstoneBlock.THICKNESS) != DripstoneThickness.TIP_MERGE) { return ActionProcessingResult.PASS; @@ -1007,13 +1143,23 @@ public List getRequiredItems( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete) + final IPlacementContext placementContext) { final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState)); itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); itemList.removeIf(ItemStackUtils::isEmpty); return itemList; } + + @Override + public boolean doesWorldStateMatchBlueprintState( + final BlockState worldState, + final BlockState blueprintState, + final Tuple blockEntityData, + final @NotNull IPlacementContext structureHandler) + { + return worldState.getBlock() == blueprintState.getBlock(); + } } public static class BlackListedBlockPlacementHandler implements IPlacementHandler @@ -1030,10 +1176,8 @@ public ActionProcessingResult handle( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete, - final BlockPos centerPos) + final IPlacementContext placementContext) { - return ActionProcessingResult.PASS; } @@ -1043,10 +1187,125 @@ public List getRequiredItems( final BlockPos pos, final BlockState blockState, @Nullable final CompoundTag tileEntityData, - final boolean complete) + final IPlacementContext placementContext) { return Collections.emptyList(); } + + @Override + public boolean doesWorldStateMatchBlueprintState( + final BlockState worldState, + final BlockState blueprintState, + final Tuple blockEntityData, + final @NotNull IPlacementContext structureHandler) + { + return true; + } + } + + public static class SolidSubstitutionPlacementHandler implements IPlacementHandler + { + @Override + public boolean canHandle(final Level world, final BlockPos pos, final BlockState blockState) + { + return blockState.getBlock() == ModBlocks.blockSolidSubstitution.get(); + } + + @Override + public ActionProcessingResult handle( + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) + { + BlockState stateToPlace = blockState; + if (placementContext.fancyPlacement()) + { + stateToPlace = placementContext.getSolidBlockForPos(pos, placementContext.getBluePrint().getRawBlockStateFunction()); + } + if (!world.setBlock(pos, stateToPlace, UPDATE_FLAG)) + { + return ActionProcessingResult.PASS; + } + return ActionProcessingResult.SUCCESS; + } + + @Override + public List getRequiredItems( + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) + { + if (placementContext.fancyPlacement()) + { + return Collections.singletonList(BlockUtils.getItemStackFromBlockState(placementContext.getSolidBlockForPos(pos, placementContext.getBluePrint().getRawBlockStateFunction()))); + } + else + { + return Collections.singletonList(new ItemStack(ModBlocks.blockSolidSubstitution.get())); + } + } + + @Override + public boolean doesWorldStateMatchBlueprintState( + final BlockState worldState, + final BlockState blueprintState, + final Tuple blockEntityData, + final IPlacementContext placementContext) + { + return worldState.equals(blueprintState) || BlockUtils.isGoodFloorBlock(worldState); + } + } + + public static class SubstitutionPlacementHandler implements IPlacementHandler + { + @Override + public boolean canHandle(final Level world, final BlockPos pos, final BlockState blockState) + { + return blockState.getBlock() == ModBlocks.blockSubstitution.get(); + } + + @Override + public ActionProcessingResult handle( + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) + { + return ActionProcessingResult.PASS; + } + + @Override + public List getRequiredItems( + final Level world, + final BlockPos pos, + final BlockState blockState, + @Nullable final CompoundTag tileEntityData, + final IPlacementContext placementContext) + { + if (placementContext.fancyPlacement()) + { + return Collections.emptyList(); + } + else + { + return Collections.singletonList(new ItemStack(ModBlocks.blockSubstitution.get())); + } + } + + @Override + public boolean doesWorldStateMatchBlueprintState( + final BlockState worldState, + final BlockState blueprintState, + final Tuple blockEntityData, + final IPlacementContext placementContext) + { + return placementContext.fancyPlacement() || worldState.equals(blueprintState); + } } /** @@ -1096,13 +1355,13 @@ public static void handleTileEntityPlacement( * @param pos the pos to place it at. * @param state the state to place. * @param data its TE data. - * @param complete if complete. + * @param placementContext the placement context. * @return the required items. */ - public static List getRequiredItemsForState(final Level world, final BlockPos pos, final BlockState state, final CompoundTag data, final boolean complete) + public static List getRequiredItemsForState(final Level world, final BlockPos pos, final BlockState state, final CompoundTag data, IPlacementContext placementContext) { final IPlacementHandler placementHandler = getHandler(world, pos, state); - return placementHandler.getRequiredItems(world, pos, state, data, complete); + return placementHandler.getRequiredItems(world, pos, state, data, placementContext); } /** diff --git a/src/main/java/com/ldtteam/structurize/placement/structure/AbstractStructureHandler.java b/src/main/java/com/ldtteam/structurize/placement/structure/AbstractStructureHandler.java index 8b709b8db4..977b47b0df 100644 --- a/src/main/java/com/ldtteam/structurize/placement/structure/AbstractStructureHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/structure/AbstractStructureHandler.java @@ -144,7 +144,7 @@ public String getMd5() } @Override - public BlockPos getWorldPos() + public BlockPos getCenterPos() { return this.worldPos; } diff --git a/src/main/java/com/ldtteam/structurize/placement/structure/IStructureHandler.java b/src/main/java/com/ldtteam/structurize/placement/structure/IStructureHandler.java index a275322a48..08a6693749 100644 --- a/src/main/java/com/ldtteam/structurize/placement/structure/IStructureHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/structure/IStructureHandler.java @@ -12,13 +12,12 @@ import net.minecraftforge.items.IItemHandler; import org.jetbrains.annotations.Nullable; import java.util.List; -import java.util.function.Function; /** * A handler for structures. * Handlers hold necessary and specific information about the entity/block/etc that is executing the placement. */ -public interface IStructureHandler +public interface IStructureHandler extends IPlacementContext { /** * Set the blueprint. @@ -54,12 +53,6 @@ default boolean isCorrectMD5(final String otherMD5) return this.getMd5().compareTo(otherMD5) == 0; } - /** - * Get the bluerint from the handler. - * @return the blueprint - */ - Blueprint getBluePrint(); - /** * Get the world from the handler. * @return the world. @@ -151,18 +144,13 @@ default boolean isCorrectMD5(final String otherMD5) */ boolean replaceWithSolidBlock(BlockState blockState); - /** - * If this is supposed to be fancy placement (player facing) or builder facing (complete). - * @return true if fancy placement. - */ - boolean fancyPlacement(); - /** * Special equal condition check. * @param state the first state. * @param state1 the second state. * @return true if considered equal and block should be skipped. */ + @Deprecated boolean shouldBlocksBeConsideredEqual(BlockState state, BlockState state1); /** @@ -197,7 +185,7 @@ default void consume(final List requiredItems) */ default BlockPos getProgressPosInWorld(final BlockPos localPos) { - return getWorldPos().subtract(getBluePrint().getPrimaryBlockOffset()).offset(localPos); + return getCenterPos().subtract(getBluePrint().getPrimaryBlockOffset()).offset(localPos); } /** @@ -207,7 +195,7 @@ default BlockPos getProgressPosInWorld(final BlockPos localPos) */ default BlockPos getStructurePosFromWorld(final BlockPos worldPos) { - return getBluePrint().getPrimaryBlockOffset().offset(worldPos.subtract(getWorldPos())); + return getBluePrint().getPrimaryBlockOffset().offset(worldPos.subtract(getCenterPos())); } /** diff --git a/src/main/java/com/ldtteam/structurize/util/BlockUtils.java b/src/main/java/com/ldtteam/structurize/util/BlockUtils.java index b95f69d23b..7ee788dda0 100644 --- a/src/main/java/com/ldtteam/structurize/util/BlockUtils.java +++ b/src/main/java/com/ldtteam/structurize/util/BlockUtils.java @@ -5,6 +5,7 @@ import com.ldtteam.structurize.api.util.Utils; import com.ldtteam.structurize.api.util.constant.Constants; import com.ldtteam.structurize.blocks.ModBlocks; +import com.ldtteam.structurize.placement.SimplePlacementContext; import com.ldtteam.structurize.placement.handlers.placement.IPlacementHandler; import com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers; import com.ldtteam.structurize.tag.ModTags; @@ -538,7 +539,7 @@ public static boolean doBlocksMatch(final ItemStack block, final ServerLevel wor { final IPlacementHandler handler = PlacementHandlers.getHandler(world, BlockPos.ZERO, blockState); final List itemList = - handler.getRequiredItems(world, position, blockState, tileEntity == null ? null : tileEntity.saveWithFullMetadata(), true); + handler.getRequiredItems(world, position, blockState, tileEntity == null ? null : tileEntity.saveWithFullMetadata(world.registryAccess()), new SimplePlacementContext(false, RotationMirror.NONE)); if (!itemList.isEmpty() && ItemStackUtils.compareItemStacksIgnoreStackSize(itemList.get(0), block)) { isMatch = true; From 8040bcdf0dac61e6a3742ad5451ec3343a03ba60 Mon Sep 17 00:00:00 2001 From: Raycoms Date: Tue, 17 Feb 2026 08:34:55 +0100 Subject: [PATCH 2/8] fix normal placeholders not placing --- .../placement/handlers/placement/PlacementHandlers.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java index 77ab3b2d9c..3d1e1c56c8 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java @@ -1276,6 +1276,10 @@ public ActionProcessingResult handle( @Nullable final CompoundTag tileEntityData, final IPlacementContext placementContext) { + if (!placementContext.fancyPlacement()) + { + world.setBlock(pos, blockState, UPDATE_FLAG); + } return ActionProcessingResult.PASS; } From b53fb3925c6d6b148d6e11d7c9fff1f7f8b08c22 Mon Sep 17 00:00:00 2001 From: Raycoms Date: Wed, 18 Feb 2026 12:03:16 +0100 Subject: [PATCH 3/8] Small fixes for rework --- .../structurize/placement/StructurePlacer.java | 12 +++++------- .../handlers/placement/PlacementHandlers.java | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/ldtteam/structurize/placement/StructurePlacer.java b/src/main/java/com/ldtteam/structurize/placement/StructurePlacer.java index dad5833d93..8d6cd7351e 100644 --- a/src/main/java/com/ldtteam/structurize/placement/StructurePlacer.java +++ b/src/main/java/com/ldtteam/structurize/placement/StructurePlacer.java @@ -613,12 +613,6 @@ public BlockPlacementResult getResourceRequirements( } } - BlockEntity worldEntity = null; - if (tileEntityData != null) - { - worldEntity = world.getBlockEntity(worldPos); - } - if (localState.getBlock() == ModBlocks.blockTagSubstitution.get() && handler.fancyPlacement()) { if (tileEntityData != null && BlockEntity.loadStatic(localPos, localState, tileEntityData) instanceof BlockEntityTagSubstitution tagEntity) @@ -632,8 +626,12 @@ public BlockPlacementResult getResourceRequirements( } } - if (BlockUtils.areBlockStatesEqual(localState, worldState, handler::replaceWithSolidBlock, handler.fancyPlacement(), handler::shouldBlocksBeConsideredEqual, tileEntityData, worldEntity)) + if (IPlacementHandler.doesWorldStateMatchBlueprintState(new BlockInfo(localPos, localState, handler.getBluePrint().getTileEntityData(worldPos, localPos)), worldPos, this.handler)) { + if (requiredItems.isEmpty()) + { + return new BlockPlacementResult(worldPos, BlockPlacementResult.Result.SUCCESS); + } return new BlockPlacementResult(worldPos, BlockPlacementResult.Result.MISSING_ITEMS, requiredItems); } diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java index 3d1e1c56c8..b73b20ce08 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java @@ -1256,7 +1256,7 @@ public boolean doesWorldStateMatchBlueprintState( final Tuple blockEntityData, final IPlacementContext placementContext) { - return worldState.equals(blueprintState) || BlockUtils.isGoodFloorBlock(worldState); + return worldState.equals(blueprintState) || (placementContext.fancyPlacement() && BlockUtils.isGoodFloorBlock(worldState)); } } From 2b3bfafd24e254ad1acc9336a03f47885a4bca22 Mon Sep 17 00:00:00 2001 From: Raycoms Date: Wed, 18 Feb 2026 14:19:36 +0100 Subject: [PATCH 4/8] move container placement handler right above general block handler --- .../placement/handlers/placement/PlacementHandlers.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java index b73b20ce08..e911445791 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java @@ -48,6 +48,8 @@ public final class PlacementHandlers static { handlers.add(new AirPlacementHandler()); + handlers.add(new SolidSubstitutionPlacementHandler()); + handlers.add(new SubstitutionPlacementHandler()); handlers.add(new BlackListedBlockPlacementHandler()); handlers.add(new FluidSubstitutionPlacementHandler()); handlers.add(new FirePlacementHandler()); @@ -59,14 +61,12 @@ public final class PlacementHandlers handlers.add(new SpecialBlockPlacementAttemptHandler()); handlers.add(new FlowerPotPlacementHandler()); handlers.add(new HopperClientLagPlacementHandler()); - handlers.add(new ContainerPlacementHandler()); handlers.add(new DripStoneBlockPlacementHandler()); handlers.add(new FallingBlockPlacementHandler()); handlers.add(new BannerPlacementHandler()); handlers.add(new DoBlockPlacementHandler()); handlers.add(new DoDoorBlockPlacementHandler()); - handlers.add(new SolidSubstitutionPlacementHandler()); - handlers.add(new SubstitutionPlacementHandler()); + handlers.add(new ContainerPlacementHandler()); handlers.add(new GeneralBlockPlacementHandler()); } @@ -121,6 +121,7 @@ public static IPlacementHandler getHandler(final Level world, final IPlacementHandler cached = handlerCache.get(block); if (cached != null) { + Log.getLogger().warn("Resolved: " + newState.toString() + " to " + cached.getClass().toString()); return cached; } @@ -128,6 +129,7 @@ public static IPlacementHandler getHandler(final Level world, { if (placementHandler.canHandle(world, worldPos, newState)) { + Log.getLogger().warn("Resolved: " + newState.toString() + " to " + placementHandler.getClass().toString()); handlerCache.put(block, placementHandler); return placementHandler; } From d9424f78dfae3f4356f6a691f9a5080682abff1e Mon Sep 17 00:00:00 2001 From: Raycoms Date: Wed, 18 Feb 2026 14:27:48 +0100 Subject: [PATCH 5/8] remove derp logging --- .../placement/handlers/placement/PlacementHandlers.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java index e911445791..0fa0a47cfc 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java @@ -121,7 +121,6 @@ public static IPlacementHandler getHandler(final Level world, final IPlacementHandler cached = handlerCache.get(block); if (cached != null) { - Log.getLogger().warn("Resolved: " + newState.toString() + " to " + cached.getClass().toString()); return cached; } @@ -129,7 +128,6 @@ public static IPlacementHandler getHandler(final Level world, { if (placementHandler.canHandle(world, worldPos, newState)) { - Log.getLogger().warn("Resolved: " + newState.toString() + " to " + placementHandler.getClass().toString()); handlerCache.put(block, placementHandler); return placementHandler; } From 086d72418b8921a9025b41d1318b9674fc718f4d Mon Sep 17 00:00:00 2001 From: Raycoms Date: Sun, 1 Mar 2026 13:13:28 +0100 Subject: [PATCH 6/8] Synchronize access (#820) Synchronize access to avoid concurrency bugs And do == class instead of instanceof --- .../handlers/placement/PlacementHandlers.java | 60 ++++++++++++++++--- 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java index 0fa0a47cfc..019600aa8a 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java @@ -45,6 +45,13 @@ public final class PlacementHandlers { public static final List handlers = new ArrayList<>(); + + public enum AddType { + BEFORE, + AFTER, + REPLACE + } + static { handlers.add(new AirPlacementHandler()); @@ -78,15 +85,51 @@ public final class PlacementHandlers */ public static void add(IPlacementHandler handler, Class override) { - for (int i = 0; i < handlers.size(); i++) + synchronized (handlers) { - if (override.isInstance(handlers.get(i))) + for (int i = 0; i < handlers.size(); i++) { - handlers.set(i, handler); - return; + if (override == handlers.get(i).getClass()) + { + handlers.set(i, handler); + return; + } } + add(handler); + } + } + + /** + * Allows for Adding a Handler before, after or instead of another one. + * @param handler the new handler to add + * @param override the class to match. + * @param addType if before/after/replace. + */ + public static void add(IPlacementHandler handler, Class override, final AddType addType) + { + synchronized (handlers) + { + for (int i = 0; i < handlers.size(); i++) + { + if (override == handlers.get(i).getClass()) + { + switch (addType) + { + case BEFORE: + handlers.add(i - 1, handler); + break; + case AFTER: + handlers.add(i, handler); + break; + case REPLACE: + handlers.set(i, handler); + break; + } + return; + } + } + add(handler); } - add(handler); } /** @@ -97,8 +140,11 @@ public static void add(IPlacementHandler handler, Class override) */ public static void add(IPlacementHandler handler) { - handlers.add(1, handler); - handlerCache.clear(); + synchronized (handlers) + { + handlers.add(1, handler); + handlerCache.clear(); + } } /** From 82872a199a4e9f09b5e6726f6d68c1ac5f807b74 Mon Sep 17 00:00:00 2001 From: Raycoms Date: Wed, 25 Feb 2026 10:27:20 +0100 Subject: [PATCH 7/8] Fix/order (#821) --- .../handlers/placement/PlacementHandlers.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java index 019600aa8a..a28e939e4c 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java @@ -116,10 +116,10 @@ public static void add(IPlacementHandler handler, Class override, final AddTy switch (addType) { case BEFORE: - handlers.add(i - 1, handler); + handlers.add(i, handler); break; case AFTER: - handlers.add(i, handler); + handlers.add(i+1, handler); break; case REPLACE: handlers.set(i, handler); @@ -133,16 +133,15 @@ public static void add(IPlacementHandler handler, Class override, final AddTy } /** - * Adds a handler to the start of the handlers list, - * effectively overriding existing ones with similar - * 'canHandle' functions because this one will evaluate before them + * Adds a handler to the start of the handlers list, right after the air, solid and light placeholder handlers. + * This may effectively override existing ones with similar 'canHandle' functions because this one will evaluate before them. * @param handler */ public static void add(IPlacementHandler handler) { synchronized (handlers) { - handlers.add(1, handler); + handlers.add(3, handler); handlerCache.clear(); } } From 3b8fed7a013d08fbd4aaf4d63c23f483a982a399 Mon Sep 17 00:00:00 2001 From: Raycoms Date: Sun, 1 Mar 2026 13:43:37 +0100 Subject: [PATCH 8/8] rework world equals checks (#818) finalize --- .../ldtteam/structurize/client/gui/WindowScan.java | 3 ++- .../structurize/placement/IPlacementContext.java | 4 ++-- .../structurize/placement/SimplePlacementContext.java | 8 ++++---- .../structurize/placement/StructurePlacer.java | 4 ++-- .../handlers/placement/DoBlockPlacementHandler.java | 6 +++--- .../placement/DoDoorBlockPlacementHandler.java | 11 +++++------ .../handlers/placement/PlacementHandlers.java | 11 +++++------ .../placement/structure/AbstractStructureHandler.java | 8 +++++++- .../placement/structure/CreativeStructureHandler.java | 2 -- .../placement/structure/IStructureHandler.java | 8 ++------ .../java/com/ldtteam/structurize/util/BlockUtils.java | 2 +- .../ldtteam/structurize/util/PlacementSettings.java | 1 - .../structurize/util/TickedWorldOperation.java | 3 ++- 13 files changed, 35 insertions(+), 36 deletions(-) diff --git a/src/main/java/com/ldtteam/structurize/client/gui/WindowScan.java b/src/main/java/com/ldtteam/structurize/client/gui/WindowScan.java index 3805e12eb1..9c7869c0cb 100644 --- a/src/main/java/com/ldtteam/structurize/client/gui/WindowScan.java +++ b/src/main/java/com/ldtteam/structurize/client/gui/WindowScan.java @@ -17,6 +17,7 @@ import com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers; import com.ldtteam.structurize.storage.rendering.RenderingCache; import com.ldtteam.structurize.storage.rendering.types.BoxPreviewData; +import com.ldtteam.structurize.util.PlacementSettings; import com.ldtteam.structurize.util.ScanToolData; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; @@ -515,7 +516,7 @@ private void updateResources() else { final IPlacementHandler handler = PlacementHandlers.getHandler(world, BlockPos.ZERO, blockState); - final List itemList = handler.getRequiredItems(world, here, blockState, tileEntity == null ? null : tileEntity.saveWithFullMetadata(), true); + final List itemList = handler.getRequiredItems(world, here, blockState, tileEntity == null ? null : tileEntity.saveWithFullMetadata(), new SimplePlacementContext(false, new PlacementSettings())); for (final ItemStack stack : itemList) { addNeededResource(stack, visible, here); diff --git a/src/main/java/com/ldtteam/structurize/placement/IPlacementContext.java b/src/main/java/com/ldtteam/structurize/placement/IPlacementContext.java index 97aa9cdf60..27c334d0a6 100644 --- a/src/main/java/com/ldtteam/structurize/placement/IPlacementContext.java +++ b/src/main/java/com/ldtteam/structurize/placement/IPlacementContext.java @@ -1,7 +1,7 @@ package com.ldtteam.structurize.placement; -import com.ldtteam.structurize.api.RotationMirror; import com.ldtteam.structurize.blueprints.v1.Blueprint; +import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; @@ -14,7 +14,7 @@ public interface IPlacementContext * Getter for the placement settings. * @return the settings object. */ - RotationMirror getRotationMirror(); + PlacementSettings getRotationMirror(); /** * If this is supposed to be fancy placement (player facing) or builder facing (complete). diff --git a/src/main/java/com/ldtteam/structurize/placement/SimplePlacementContext.java b/src/main/java/com/ldtteam/structurize/placement/SimplePlacementContext.java index 4e30d96254..9c3be75109 100644 --- a/src/main/java/com/ldtteam/structurize/placement/SimplePlacementContext.java +++ b/src/main/java/com/ldtteam/structurize/placement/SimplePlacementContext.java @@ -1,7 +1,7 @@ package com.ldtteam.structurize.placement; -import com.ldtteam.structurize.api.RotationMirror; import com.ldtteam.structurize.blueprints.v1.Blueprint; +import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; @@ -22,16 +22,16 @@ public class SimplePlacementContext implements IPlacementContext /** * Rotation mirror. */ - private final RotationMirror rotationMirror; + private final PlacementSettings rotationMirror; - public SimplePlacementContext(final boolean fancyPlacement, final RotationMirror rotationMirror) + public SimplePlacementContext(final boolean fancyPlacement, final PlacementSettings rotationMirror) { this.fancyPlacement = fancyPlacement; this.rotationMirror = rotationMirror; } @Override - public RotationMirror getRotationMirror() + public PlacementSettings getRotationMirror() { return rotationMirror; } diff --git a/src/main/java/com/ldtteam/structurize/placement/StructurePlacer.java b/src/main/java/com/ldtteam/structurize/placement/StructurePlacer.java index 8d6cd7351e..e1e89b5b0d 100644 --- a/src/main/java/com/ldtteam/structurize/placement/StructurePlacer.java +++ b/src/main/java/com/ldtteam/structurize/placement/StructurePlacer.java @@ -244,7 +244,7 @@ public BlockPlacementResult handleBlockPlacement( { try { - final BlockPos pos = this.handler.getWorldPos().subtract(handler.getBluePrint().getPrimaryBlockOffset()); + final BlockPos pos = this.handler.getCenterPos().subtract(handler.getBluePrint().getPrimaryBlockOffset()); final Optional> type = EntityType.by(compound); if (type.isPresent()) @@ -575,7 +575,7 @@ public BlockPlacementResult getResourceRequirements( { try { - final BlockPos pos = this.handler.getWorldPos().subtract(handler.getBluePrint().getPrimaryBlockOffset()); + final BlockPos pos = this.handler.getCenterPos().subtract(handler.getBluePrint().getPrimaryBlockOffset()); final Optional> type = EntityType.by(compound); if (type.isPresent()) diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoBlockPlacementHandler.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoBlockPlacementHandler.java index fe35b21990..10121fb0a3 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoBlockPlacementHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoBlockPlacementHandler.java @@ -7,9 +7,9 @@ import com.ldtteam.domumornamentum.util.BlockUtils; import com.ldtteam.structurize.api.util.ItemStackUtils; import com.ldtteam.structurize.api.util.Log; +import com.ldtteam.structurize.placement.IPlacementContext; import com.ldtteam.structurize.placement.structure.IStructureHandler; import com.ldtteam.structurize.util.InventoryUtils; -import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; @@ -128,9 +128,9 @@ public static boolean compareBEData(final Tuple blockE { if (blockEntityData != null) { - if (blockEntityData.getA() instanceof final MateriallyTexturedBlockEntity mtbe && blockEntityData.getB().contains(BLOCK_ENTITY_TEXTURE_DATA)) + if (blockEntityData.getA() instanceof final MateriallyTexturedBlockEntity mtbe && blockEntityData.getB().contains("textureData")) { - return mtbe.getTextureData().equals(MaterialTextureData.CODEC.decode(NbtOps.INSTANCE, blockEntityData.getB().get(BLOCK_ENTITY_TEXTURE_DATA)).getOrThrow().getFirst()); + return mtbe.getTextureData().equals(MaterialTextureData.deserializeFromNBT(blockEntityData.getB().getCompound("textureData"))); } } return false; diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoDoorBlockPlacementHandler.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoDoorBlockPlacementHandler.java index 55f3890ab0..5e531f0799 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoDoorBlockPlacementHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/DoDoorBlockPlacementHandler.java @@ -3,10 +3,9 @@ import com.ldtteam.domumornamentum.block.AbstractBlockDoor; import com.ldtteam.domumornamentum.block.IMateriallyTexturedBlock; import com.ldtteam.domumornamentum.util.BlockUtils; -import com.ldtteam.structurize.api.ItemStackUtils; -import com.ldtteam.structurize.api.Log; +import com.ldtteam.structurize.api.util.ItemStackUtils; +import com.ldtteam.structurize.api.util.Log; import com.ldtteam.structurize.placement.IPlacementContext; -import com.ldtteam.structurize.placement.structure.IStructureHandler; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.util.Tuple; @@ -23,7 +22,7 @@ import java.util.Collections; import java.util.List; -import static com.ldtteam.structurize.api.constants.Constants.UPDATE_FLAG; +import static com.ldtteam.structurize.api.util.constant.Constants.UPDATE_FLAG; import static com.ldtteam.structurize.placement.handlers.placement.DoBlockPlacementHandler.compareBEData; import static com.ldtteam.structurize.placement.handlers.placement.PlacementHandlers.handleTileEntityPlacement; @@ -82,13 +81,13 @@ public List getRequiredItems( if (tileEntityData != null && blockState.getValue(net.minecraft.world.level.block.DoorBlock.HALF).equals(DoubleBlockHalf.LOWER)) { BlockPos blockpos = new BlockPos(tileEntityData.getInt("x"), tileEntityData.getInt("y"), tileEntityData.getInt("z")); - final BlockEntity tileEntity = BlockEntity.loadStatic(blockpos, blockState, tileEntityData, world.registryAccess()); + final BlockEntity tileEntity = BlockEntity.loadStatic(blockpos, blockState, tileEntityData); if (tileEntity == null) { return Collections.emptyList(); } - itemList.add(BlockUtils.getMaterializedItemStack(tileEntity, world.registryAccess())); + itemList.add(BlockUtils.getMaterializedItemStack(null, tileEntity)); } itemList.removeIf(ItemStackUtils::isEmpty); return itemList; diff --git a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java index a28e939e4c..d6ec7f3550 100644 --- a/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java +++ b/src/main/java/com/ldtteam/structurize/placement/handlers/placement/PlacementHandlers.java @@ -29,9 +29,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.*; import static com.ldtteam.structurize.api.util.constant.Constants.UPDATE_FLAG; @@ -756,7 +754,7 @@ public List getRequiredItems( { itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); } - itemList.add(new ItemStack(((FlowerPotBlock) blockState.getBlock()).getPotted())); + itemList.add(new ItemStack(((FlowerPotBlock) blockState.getBlock()).getContent())); itemList.removeIf(ItemStackUtils::isEmpty); return itemList; } @@ -771,7 +769,7 @@ public boolean doesWorldStateMatchBlueprintState( return worldState.equals(blueprintState) && blueprintState.getBlock() instanceof FlowerPotBlock blueprintPot && worldState.getBlock() instanceof FlowerPotBlock worldPot - && blueprintPot.getPotted() == worldPot.getPotted(); + && blueprintPot.getContent() == worldPot.getContent(); } } @@ -1093,7 +1091,8 @@ public List getRequiredItems( @Nullable final CompoundTag tileEntityData, final IPlacementContext placementContext) { - { + final List itemList = new ArrayList<>(getItemsFromTileEntity(tileEntityData, blockState)); + itemList.add(BlockUtils.getItemStackFromBlockState(blockState)); itemList.removeIf(ItemStackUtils::isEmpty); return itemList; } diff --git a/src/main/java/com/ldtteam/structurize/placement/structure/AbstractStructureHandler.java b/src/main/java/com/ldtteam/structurize/placement/structure/AbstractStructureHandler.java index 977b47b0df..7056cebe9a 100644 --- a/src/main/java/com/ldtteam/structurize/placement/structure/AbstractStructureHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/structure/AbstractStructureHandler.java @@ -146,7 +146,7 @@ public String getMd5() @Override public BlockPos getCenterPos() { - return this.worldPos; + return worldPos; } @Override @@ -154,6 +154,12 @@ public PlacementSettings getSettings() { return this.settings; } + + @Override + public PlacementSettings getRotationMirror() + { + return this.settings; + } @Override public boolean isReady() diff --git a/src/main/java/com/ldtteam/structurize/placement/structure/CreativeStructureHandler.java b/src/main/java/com/ldtteam/structurize/placement/structure/CreativeStructureHandler.java index 5cb07ea6bb..313a3035cb 100644 --- a/src/main/java/com/ldtteam/structurize/placement/structure/CreativeStructureHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/structure/CreativeStructureHandler.java @@ -1,10 +1,8 @@ package com.ldtteam.structurize.placement.structure; -import com.ldtteam.structurize.api.util.ItemStackUtils; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.Structurize; import com.ldtteam.structurize.util.BlockUtils; -import com.ldtteam.structurize.util.InventoryUtils; import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.item.ItemStack; diff --git a/src/main/java/com/ldtteam/structurize/placement/structure/IStructureHandler.java b/src/main/java/com/ldtteam/structurize/placement/structure/IStructureHandler.java index 08a6693749..18ca4f9e99 100644 --- a/src/main/java/com/ldtteam/structurize/placement/structure/IStructureHandler.java +++ b/src/main/java/com/ldtteam/structurize/placement/structure/IStructureHandler.java @@ -3,6 +3,7 @@ import com.ldtteam.structurize.api.util.ItemStackUtils; import com.ldtteam.structurize.blueprints.v1.Blueprint; import com.ldtteam.structurize.api.util.Log; +import com.ldtteam.structurize.placement.IPlacementContext; import com.ldtteam.structurize.util.InventoryUtils; import com.ldtteam.structurize.util.PlacementSettings; import net.minecraft.world.level.block.state.BlockState; @@ -12,6 +13,7 @@ import net.minecraftforge.items.IItemHandler; import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.function.Function; /** * A handler for structures. @@ -59,12 +61,6 @@ default boolean isCorrectMD5(final String otherMD5) */ Level getWorld(); - /** - * Get the world position this is placed at. - * @return the position. - */ - BlockPos getWorldPos(); - /** * Getter for the placement settings. * @return the settings object. diff --git a/src/main/java/com/ldtteam/structurize/util/BlockUtils.java b/src/main/java/com/ldtteam/structurize/util/BlockUtils.java index 7ee788dda0..3ac50251b7 100644 --- a/src/main/java/com/ldtteam/structurize/util/BlockUtils.java +++ b/src/main/java/com/ldtteam/structurize/util/BlockUtils.java @@ -539,7 +539,7 @@ public static boolean doBlocksMatch(final ItemStack block, final ServerLevel wor { final IPlacementHandler handler = PlacementHandlers.getHandler(world, BlockPos.ZERO, blockState); final List itemList = - handler.getRequiredItems(world, position, blockState, tileEntity == null ? null : tileEntity.saveWithFullMetadata(world.registryAccess()), new SimplePlacementContext(false, RotationMirror.NONE)); + handler.getRequiredItems(world, position, blockState, tileEntity == null ? null : tileEntity.saveWithFullMetadata(), new SimplePlacementContext(false, new PlacementSettings())); if (!itemList.isEmpty() && ItemStackUtils.compareItemStacksIgnoreStackSize(itemList.get(0), block)) { isMatch = true; diff --git a/src/main/java/com/ldtteam/structurize/util/PlacementSettings.java b/src/main/java/com/ldtteam/structurize/util/PlacementSettings.java index f090b9fecf..8fae3a2d07 100644 --- a/src/main/java/com/ldtteam/structurize/util/PlacementSettings.java +++ b/src/main/java/com/ldtteam/structurize/util/PlacementSettings.java @@ -7,7 +7,6 @@ * Placement settings for the blueprints. * @deprecated use {@link RotationMirror} */ -@Deprecated(since = "1.20", forRemoval = true) public class PlacementSettings { /** diff --git a/src/main/java/com/ldtteam/structurize/util/TickedWorldOperation.java b/src/main/java/com/ldtteam/structurize/util/TickedWorldOperation.java index 66f719b540..65c62fa82e 100644 --- a/src/main/java/com/ldtteam/structurize/util/TickedWorldOperation.java +++ b/src/main/java/com/ldtteam/structurize/util/TickedWorldOperation.java @@ -5,6 +5,7 @@ import com.ldtteam.structurize.api.util.ItemStackUtils; import com.ldtteam.structurize.network.messages.UpdateClientRender; import com.ldtteam.structurize.placement.BlockPlacementResult; +import com.ldtteam.structurize.placement.SimplePlacementContext; import com.ldtteam.structurize.placement.StructurePhasePlacementResult; import com.ldtteam.structurize.placement.StructurePlacer; import com.ldtteam.structurize.placement.handlers.placement.IPlacementHandler; @@ -310,7 +311,7 @@ private boolean run(final ServerLevel world) { final IPlacementHandler handler = PlacementHandlers.getHandler(world, BlockPos.ZERO, blockState); final List itemList = - handler.getRequiredItems(world, here, blockState, tileEntity == null ? null : tileEntity.saveWithFullMetadata(), true); + handler.getRequiredItems(world, here, blockState, tileEntity == null ? null : tileEntity.saveWithFullMetadata(), new SimplePlacementContext(this.placer.getHandler().fancyPlacement(), this.placer.getHandler().getRotationMirror())); if (!itemList.isEmpty() && ItemStackUtils.compareItemStacksIgnoreStackSize(itemList.get(0), firstBlock)) { isMatch = true;