From 7adaa4841dd0603001a4a52d6f942b6557e43fe4 Mon Sep 17 00:00:00 2001 From: ABKQPO <93412322+ABKQPO@users.noreply.github.com> Date: Fri, 11 Sep 2026 09:55:22 +0800 Subject: [PATCH] Place the fluid of a round that finished its structure in the same round --- .../structure/AutoPlaceEnvironment.java | 13 ++++ .../structure/FluidAutoplace.java | 28 ++++++--- .../structure/IStructureDefinition.java | 3 + .../SurvivalBuildStructureWalker.java | 59 +++++++++++++++++++ .../assets/structurelib/lang/zh_CN.lang | 1 + 5 files changed, 95 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/AutoPlaceEnvironment.java b/src/main/java/com/gtnewhorizon/structurelib/structure/AutoPlaceEnvironment.java index 0e839f1b..525e93cb 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/structure/AutoPlaceEnvironment.java +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/AutoPlaceEnvironment.java @@ -340,6 +340,10 @@ public class AutoPlaceEnvironment { if (fluidRoundState != null) fluidRoundState.setGateResult(ready); } + void clearFluidGateResult() { + if (fluidRoundState != null) fluidRoundState.clearGateResult(); + } + /** * Flag that at least one fluid element wanted to place its fluid, but had to wait for the structure to be built * first. An autoplace round that did nothing but wait reports zero placed elements instead of reporting that the @@ -407,6 +411,15 @@ public class AutoPlaceEnvironment { */ void setGateResult(boolean ready); + /** + * Forget the answer of the fluid placement gate, so that it is asked again. + *
+ * The answer describes the world as it was before this round built anything, so it has to be dropped once the + * round has placed something and the fluids that had to wait are retried. An implementation that keeps no + * answer at all can leave this alone. + */ + default void clearGateResult() {} + /** * Remember that a fluid element had to wait for the structure to be built first. */ diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/FluidAutoplace.java b/src/main/java/com/gtnewhorizon/structurelib/structure/FluidAutoplace.java index c9d7468c..ce48cb3d 100644 --- a/src/main/java/com/gtnewhorizon/structurelib/structure/FluidAutoplace.java +++ b/src/main/java/com/gtnewhorizon/structurelib/structure/FluidAutoplace.java @@ -63,9 +63,11 @@ public class FluidAutoplace { */ LENIENT, /** - * Only place the fluid once every non fluid element of the structure is satisfied. This is the default, and - * together with the fact that an autoplace round places a limited number of elements it effectively builds the - * structure first and fills it with fluid afterwards. + * Only place the fluid once every non fluid element of the structure is satisfied. This is the default. + *
+ * The fluid is placed as soon as the structure can hold it, which is normally the round that finishes the + * structure: the fluids that round could not place yet are tried once more when it is done building, so they do + * not have to wait for another round. */ STRICT } @@ -195,9 +197,9 @@ public class FluidAutoplace { /** * Whether a fluid may be placed at this position right now. *
- * With {@link GateMode#STRICT} the answer is computed once per autoplace round and cached on the environment, as it
- * costs a walk over the whole structure. That also means a fluid is at worst one round late, which is what keeps
- * the walk cheap on big multiblocks.
+ * With {@link GateMode#STRICT} the answer is computed by a walk over the whole structure and cached for the rest of
+ * the round, as that walk is expensive. The cache is dropped as soon as the round has built something, so that the
+ * fluids which waited for it are not held back by an answer that predates those placements.
*
* @param world world to place in
* @param x x coord
@@ -302,15 +304,23 @@ public class FluidAutoplace {
private static boolean placeFluidBlock(World world, int x, int y, int z, FluidBlockRequirement requirement) {
FluidStack fluid = requirement.cost();
+ Block block = requirement.block();
if (requirement.placer() != null) {
- return requirement.placer().place(world, x, y, z, requirement.block(), requirement.meta(), fluid);
+ if (!requirement.placer().place(world, x, y, z, block, requirement.meta(), fluid)) return false;
+ } else {
+ // The notification flags matter here. A fluid that is put into the world silently neither flows nor tells
+ // its neighbours about itself, so it would sit where it was placed until something else happens to touch
+ // it, which for water inside a multiblock can be never.
+ if (!world.setBlock(x, y, z, block, requirement.meta(), 3)) return false;
}
- return world.setBlock(x, y, z, requirement.block(), requirement.meta(), 2);
+ // A fluid that is never ticked does not spread, and a fluid placed as a flowing one would never settle.
+ world.scheduleBlockUpdate(x, y, z, block, block.tickRate(world));
+ return true;
}
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, 2);
+ else world.setBlock(x, y, z, block, meta, 3);
}
private static void reportMissingFluid(AutoPlaceEnvironment env, FluidBlockRequirement requirement, int missing) {
diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureDefinition.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureDefinition.java
index 7158942e..a1eda53f 100644
--- a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureDefinition.java
+++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureDefinition.java
@@ -318,6 +318,9 @@ public interface IStructureDefinition
+ * A fluid position is often visited before the blocks that are meant to hold it, so a round that finishes the
+ * structure would otherwise leave the fluid to the next round even though there is nothing left to wait for. The
+ * gate is asked again, as the answer it gave was about the world as it was before this round built anything.
+ *
+ * Like the walk itself, this places at most as many elements as the round still has budget left, and whatever it
+ * cannot reach is left to the next round.
+ *
+ * @param world the world this round was run in
+ */
+ void finishRound(World world) {
+ if (deferredFluids.isEmpty()) return;
+ // A round that built nothing has not changed the answer of the gate either.
+ if (built <= 0) return;
+
+ int budget = elementBudget - built;
+ env.clearFluidGateResult();
+ for (DeferredFluid
@@ -95,6 +143,11 @@ class SurvivalBuildStructureWalker