diff --git a/src/main/java/com/simibubi/create/content/contraptions/Contraption.java b/src/main/java/com/simibubi/create/content/contraptions/Contraption.java index 4a2f109127..91dd190cff 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/Contraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/Contraption.java @@ -1120,6 +1120,8 @@ public void addBlocksToWorld(Level world, StructureTransform transform) { translateMultiblockControllers(transform); + GhostPlacementServerLevel ghostLevel = null; + for (boolean nonBrittles : Iterate.trueAndFalse) { for (StructureBlockInfo block : blocks.values()) { if (nonBrittles == BlockMovementChecks.isBrittle(block.state())) @@ -1140,12 +1142,25 @@ public void addBlocksToWorld(Level world, StructureTransform transform) { if (blockState.getDestroySpeed(world, targetPos) == -1 || (state.getCollisionShape(world, targetPos) .isEmpty() && !blockState.getCollisionShape(world, targetPos) - .isEmpty())) { + .isEmpty()) || world.isOutsideBuildHeight(targetPos) || (state.isAir() && !block.state().isAir())) { + state = block.state(); if (targetPos.getY() == world.getMinBuildHeight()) targetPos = targetPos.above(); world.levelEvent(LevelEvent.PARTICLES_DESTROY_BLOCK, targetPos, Block.getId(state)); - if (shouldDropBlocks) { - Block.dropResources(state, world, targetPos, null); + if (!shouldDropBlocks) + continue; + if (ghostLevel == null && world instanceof ServerLevel serverWorld) + ghostLevel = new GhostPlacementServerLevel(serverWorld); + if (ghostLevel != null) { + ghostLevel.setBlock(targetPos, state, Block.UPDATE_NONE); + BlockEntity blockEntity = ghostLevel.getBlockEntity(targetPos); + if (blockEntity != null) { + CompoundTag tag = block.nbt(); + tag = NBTProcessors.process(state, blockEntity, tag, false); + if (tag != null) + blockEntity.loadWithComponents(tag, ghostLevel.registryAccess()); + } + ghostLevel.destroyBlock(targetPos, true); } continue; } diff --git a/src/main/java/com/simibubi/create/content/contraptions/GhostPlacementServerLevel.java b/src/main/java/com/simibubi/create/content/contraptions/GhostPlacementServerLevel.java new file mode 100644 index 0000000000..bd04ad0394 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/GhostPlacementServerLevel.java @@ -0,0 +1,70 @@ +package com.simibubi.create.content.contraptions; + +import javax.annotation.Nullable; + +import net.createmod.catnip.levelWrappers.WrappedServerLevel; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.HashMap; +import java.util.Map; + +public class GhostPlacementServerLevel extends WrappedServerLevel { + + protected final Map blockStates = new HashMap<>(); + protected final Map blockEntities = new HashMap<>(); + + public GhostPlacementServerLevel(ServerLevel level) { + super(level); + } + + @Override + public boolean setBlock(BlockPos pos, BlockState newState, int flags) { + pos = pos.immutable(); + getBlockState(pos).onRemove(this, pos, newState,false); + blockStates.put(pos, newState); + return true; + } + + @Override + public BlockState getBlockState(BlockPos pos) { + return blockStates.getOrDefault(pos, Blocks.AIR.defaultBlockState()); + } + + @Nullable + public BlockEntity getBlockEntity(BlockPos pos) { + BlockState blockState = getBlockState(pos); + BlockEntity blockEntity = blockEntities.getOrDefault(pos, null); + if (blockState.hasBlockEntity() && blockEntity == null) { + blockEntity = ((EntityBlock) blockState.getBlock()).newBlockEntity(pos, blockState); + if (blockEntity != null) + blockEntity.setLevel(this); + pos = pos.immutable(); + blockEntities.put(pos, blockEntity); + } + return blockEntity; + } + + @Override + public void removeBlockEntity(BlockPos pos) { + blockEntities.remove(pos); + } + + @Override + public boolean destroyBlock(BlockPos pos, boolean dropBlock, @Nullable Entity entity, int recursionLeft) { + BlockState blockState = getBlockState(pos); + if (blockState.isAir()) + return false; + if (dropBlock) + Block.dropResources(blockState, this, pos, getBlockEntity(pos), entity, ItemStack.EMPTY); + setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_NONE); + return true; + } +}