mirror of
https://github.com/GTNewHorizons/StructureLib.git
synced 2026-11-13 00:37:55 +00:00
Place the fluid of a round that finished its structure in the same round
This commit is contained in:
1 parent
d7df0c0b5b
commit
7adaa4841d
5 files changed
+95
-9
No files matched your search
@@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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) {
|
||||
|
||||
@@ -318,6 +318,9 @@ public interface IStructureDefinition<T> {
|
||||
basePositionC,
|
||||
walker,
|
||||
"survivalBuild");
|
||||
// A fluid position is often visited before the blocks that hold it, so give the fluids that had to wait for
|
||||
// those blocks a chance to be placed in this round rather than the next one.
|
||||
walker.finishRound(world);
|
||||
return walker.getBuilt();
|
||||
}
|
||||
|
||||
|
||||
+59
@@ -1,10 +1,14 @@
|
||||
package com.gtnewhorizon.structurelib.structure;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import com.github.bsideup.jabel.Desugar;
|
||||
import com.gtnewhorizon.structurelib.StructureLibAPI;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
import com.gtnewhorizon.structurelib.structure.IStructureElement.PlaceResult;
|
||||
@@ -20,6 +24,7 @@ class SurvivalBuildStructureWalker<T> implements IStructureWalker<T>, AutoPlaceE
|
||||
|
||||
private Boolean fluidGateResult;
|
||||
private boolean fluidDeferred;
|
||||
private final List<DeferredFluid<T>> deferredFluids = new ArrayList<>();
|
||||
|
||||
private final AutoPlaceEnvironment env;
|
||||
|
||||
@@ -52,6 +57,11 @@ class SurvivalBuildStructureWalker<T> implements IStructureWalker<T>, AutoPlaceE
|
||||
env.offsetABC[2] = c;
|
||||
env.setSource(params.getSource());
|
||||
PlaceResult placeResult = element.survivalPlaceBlock(object, world, x, y, z, trigger, env);
|
||||
if (placeResult == PlaceResult.REJECT_CONTINUE && element.isFluidElement(object)) {
|
||||
// The fluid has to wait. Remember where it is, as this round may still build what it is waiting for, and
|
||||
// then it can be placed before the round is over instead of one round later.
|
||||
deferredFluids.add(new DeferredFluid<>(element, x, y, z, a, b, c));
|
||||
}
|
||||
if (placeResult != PlaceResult.SKIP && placeResult != PlaceResult.REJECT_CONTINUE && built == -1) built = 0;
|
||||
switch (placeResult) {
|
||||
case SKIP:
|
||||
@@ -74,6 +84,44 @@ class SurvivalBuildStructureWalker<T> implements IStructureWalker<T>, AutoPlaceE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Place the fluids that had to wait during this round, now that the round has built everything it was going to.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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<T> deferred : deferredFluids) {
|
||||
if (budget <= 0) break;
|
||||
IStructureElement<T> element = deferred.element();
|
||||
// The element has to see the same offsets and item source it saw during the walk, as elements may read
|
||||
// channel data or take items through them.
|
||||
env.offsetABC[0] = deferred.a();
|
||||
env.offsetABC[1] = deferred.b();
|
||||
env.offsetABC[2] = deferred.c();
|
||||
env.setSource(params.getSource());
|
||||
PlaceResult placeResult = element
|
||||
.survivalPlaceBlock(object, world, deferred.x(), deferred.y(), deferred.z(), trigger, env);
|
||||
if (placeResult != PlaceResult.ACCEPT) continue;
|
||||
if (check) element.check(object, world, deferred.x(), deferred.y(), deferred.z());
|
||||
built += 1;
|
||||
budget -= 1;
|
||||
}
|
||||
deferredFluids.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of elements this round has placed, or -1 when the structure is done.
|
||||
* <p>
|
||||
@@ -95,6 +143,11 @@ class SurvivalBuildStructureWalker<T> implements IStructureWalker<T>, AutoPlaceE
|
||||
fluidGateResult = ready;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearGateResult() {
|
||||
fluidGateResult = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markDeferred() {
|
||||
fluidDeferred = true;
|
||||
@@ -104,4 +157,10 @@ class SurvivalBuildStructureWalker<T> implements IStructureWalker<T>, AutoPlaceE
|
||||
public boolean isDeferred() {
|
||||
return fluidDeferred;
|
||||
}
|
||||
|
||||
/**
|
||||
* A fluid position a round had to leave alone, kept so that it can be tried once more when the round is over.
|
||||
*/
|
||||
@Desugar
|
||||
private record DeferredFluid<T> (IStructureElement<T> element, int x, int y, int z, int a, int b, int c) {}
|
||||
}
|
||||
Reference in new issue
Block a user