From 6bc8cea2bfd51a4cc4d72adf0ccb3b82e9a81723 Mon Sep 17 00:00:00 2001 From: ABKQPO <93412322+ABKQPO@users.noreply.github.com> Date: Fri, 11 Sep 2026 10:13:09 +0800 Subject: [PATCH] Roll a placement back to what was there rather than to air --- .../structurelib/StructureLibAPI.java | 22 +++++++++++++++++++ .../structure/FluidAutoplace.java | 3 +-- .../structure/StructureUtility.java | 19 +++++++++++----- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/gtnewhorizon/structurelib/StructureLibAPI.java b/src/main/java/com/gtnewhorizon/structurelib/StructureLibAPI.java index 3748f9f7..bfaed15a 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/StructureLibAPI.java +++ b/src/main/java/com/gtnewhorizon/structurelib/StructureLibAPI.java @@ -2,6 +2,8 @@ package com.gtnewhorizon.structurelib; import static com.gtnewhorizon.structurelib.StructureLib.proxy; +import javax.annotation.Nullable; + import net.minecraft.block.Block; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; @@ -328,6 +330,26 @@ public class StructureLibAPI { return block.isAir(w, x, y, z) || block.isReplaceable(w, x, y, z); } + /** + * Put back the block that was at a position, which is what a placement that could not be paid for should roll back + * to. + *

+ * Restoring the previous state instead of clearing the position matters whenever the position held something + * autoplace is allowed to replace, e.g. a fluid that is not a source block: the player would otherwise be left with + * a hole where their fluid used to be. + * + * @param world world to write to + * @param x x coord + * @param y y coord + * @param z z coord + * @param block the block that was there, or null for air + * @param meta the meta that was there + */ + public static void restoreBlock(World world, int x, int y, int z, @Nullable Block block, int meta) { + if (block == null || block.isAir(world, x, y, z)) world.setBlockToAir(x, y, z); + else world.setBlock(x, y, z, block, meta, 3); + } + /** * Send chat to player, but throttled. * diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/FluidAutoplace.java b/src/main/java/com/gtnewhorizon/structurelib/structure/FluidAutoplace.java index ce48cb3d..0b9b4ccd 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/structure/FluidAutoplace.java +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/FluidAutoplace.java @@ -319,8 +319,7 @@ public class FluidAutoplace { } private static void restore(World world, int x, int y, int z, Block block, int meta) { - if (block == null || block.isAir(world, x, y, z)) world.setBlockToAir(x, y, z); - else world.setBlock(x, y, z, block, meta, 3); + StructureLibAPI.restoreBlock(world, x, y, z, block, meta); } private static void reportMissingFluid(AutoPlaceEnvironment env, FluidBlockRequirement requirement, int missing) { diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/StructureUtility.java b/src/main/java/com/gtnewhorizon/structurelib/structure/StructureUtility.java index 8c36536d..4a0f311e 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/structure/StructureUtility.java +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/StructureUtility.java @@ -194,7 +194,7 @@ public class StructureUtility { @Override public boolean placeBlock(Object o, World world, int x, int y, int z, ItemStack trigger) { - world.setBlock(x, y, z, Blocks.air, 0, 2); + world.setBlock(x, y, z, Blocks.air, 0, 3); return false; } @@ -204,7 +204,8 @@ public class StructureUtility { AutoPlaceEnvironment env) { if (check(o, world, x, y, z)) return PlaceResult.SKIP; if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, env.getActor())) return PlaceResult.REJECT; - world.setBlock(x, y, z, Blocks.air, 0, 2); + // Notify the neighbours, so that e.g. a fluid next to a position that had to be emptied flows back into it. + world.setBlock(x, y, z, Blocks.air, 0, 3); return PlaceResult.ACCEPT; } }; @@ -494,6 +495,8 @@ public class StructureUtility { new ChatComponentTranslation("structurelib.autoplace.missing_block", getStackChatName(stack))); return PlaceResult.REJECT; } + Block previousBlock = world.getBlock(x, y, z); + int previousMeta = world.getBlockMetadata(x, y, z); if (block instanceof ICustomBlockSetting blockCustom) { blockCustom.setBlock(world, x, y, z, meta); } else if (!stack.copy() @@ -501,8 +504,9 @@ public class StructureUtility { return PlaceResult.REJECT; } if (!s.takeOne(stack, false)) { - // rollback - world.setBlockToAir(x, y, z); + // Roll back to what was there before, which is often a fluid that this block was placed over, so that a + // position is never left empty by a placement that was not paid for. + StructureLibAPI.restoreBlock(world, x, y, z, previousBlock, previousMeta); } return PlaceResult.ACCEPT; } @@ -600,11 +604,14 @@ public class StructureUtility { new ChatComponentTranslation("structurelib.autoplace.missing_block", getStackChatName(stack))); return PlaceResult.REJECT; } + Block previousBlock = world.getBlock(x, y, z); + int previousMeta = world.getBlockMetadata(x, y, z); if (!stack.copy().tryPlaceItemIntoWorld(actor, world, x, y, z, ForgeDirection.UP.ordinal(), 0.5f, 0.5f, 0.5f)) return PlaceResult.REJECT; if (!s.takeOne(stack, false)) - // this is bad! probably an exploit somehow. Let's nullify the block we just placed instead - world.setBlockToAir(x, y, z); + // this is bad! probably an exploit somehow. Put back what was there instead of clearing the position, as + // that would destroy the fluid this block was placed over. + StructureLibAPI.restoreBlock(world, x, y, z, previousBlock, previousMeta); return PlaceResult.ACCEPT; }