Skip to content

Commit

Permalink
more optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
eragaxshim committed Sep 23, 2024
1 parent 14219d6 commit 52ec9bf
Showing 1 changed file with 88 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public class PumpMachine extends TieredEnergyMachine implements IAutoOutputFluid
private final Deque<BlockPos> fluidSourceBlocks = new ArrayDeque<>();
private final Deque<BlockPos> blocksToCheck = new ArrayDeque<>();
private PumpQueue pumpQueue = null;
private boolean initializedQueue = false;
private final boolean initializedQueue = false;
@Getter
@Persisted
private int pumpHeadY;
Expand Down Expand Up @@ -251,15 +251,42 @@ private SearchResult searchNext(Level level, BlockPos headPosBelow, BlockPos sea
return null;
}

record PumpPath(List<BlockPos> path, FluidType fluidType) { }
/**
* Update the pump queue if it is empty.
* @param fluidType Use this if the pump queue must have the same fluid type because it was already decided in the
* pump cycle.
*/
private void updatePumpQueue(@Nullable FluidType fluidType) {
if (getLevel() == null) return;

if (pumpQueue != null && !pumpQueue.queue().isEmpty()) {
return;
}

BlockPos headPos = getPos().below(pumpHeadY);

BlockPos downPos = headPos.below(1);
var downBlock = getLevel().getBlockState(downPos);

if (!(downBlock.getBlock() instanceof LiquidBlock)) {
pumpQueue = null;
return;
}

if (fluidType != null && downBlock.getFluidState().getFluidType() != fluidType) {
pumpQueue = null;
return;
}

pumpQueue = buildPumpQueue(getLevel(), headPos, downBlock.getFluidState().getFluidType(), queueSize(), true);
}

/**
* Does a "depth-first"-ish search to find a path to a source. It prioritizes going up and away from the pump head.
* If the path it finds only contains sources at the level below the pump head, it will keep looking until it finds
* one that has a source at a higher location. If it cannot find one, it will return the original path.
*/
@Nullable
private PumpQueue findSourcePath(Level level, BlockPos headPos, FluidType fluidType, int queueSourceAmount, boolean upSources) {
private PumpQueue buildPumpQueue(Level level, BlockPos headPos, FluidType fluidType, int queueSourceAmount, boolean upSources) {
Set<BlockPos> checked = new ObjectOpenHashSet<>();

BlockPos headPosBelow = headPos.below();
Expand All @@ -282,12 +309,12 @@ private PumpQueue findSourcePath(Level level, BlockPos headPos, FluidType fluidT
int previousSources = 0;
Queue<Deque<BlockPos>> paths = new ArrayDeque<>();
List<BlockPos> sources = new ArrayList<>();
// We do at most 1000 iterations to try and find source blocks
while (!pathStack.isEmpty() && iterations < 1000) {
// Peeks at the tail
BlockPos searchHead = pathStack.get(pathStack.size() - 1);
//BlockPos lastSource = sourceStack.peekLast();
SearchResult next = searchNext(level, headPosBelow, searchHead, fluidType, maxPumpRange, upSources, checked);

SearchResult next = searchNext(level, headPosBelow, searchHead, fluidType, maxPumpRange, upSources, checked);

iterations++;

Expand Down Expand Up @@ -335,7 +362,7 @@ private PumpQueue findSourcePath(Level level, BlockPos headPos, FluidType fluidT
pathToLastSource.addAll(nonSources);
// Also add the source itself
pathToLastSource.add(next.pos());
// Clear it
// Reset non-sources because we just added them and found a source
nonSources.clear();
sources.add(next.pos());
} else {
Expand All @@ -348,15 +375,12 @@ private PumpQueue findSourcePath(Level level, BlockPos headPos, FluidType fluidT
if (upSources) {
// If we found none, we try again without the restriction
if (paths.isEmpty()) {
return findSourcePath(level, headPos, fluidType, queueSourceAmount, false);
return buildPumpQueue(level, headPos, fluidType, queueSourceAmount, false);
}
System.out.println("iterations up " + iterations);

return new PumpQueue(paths, fluidType);
}

System.out.println("iterations " + iterations);

// Only after everything except the block directly below the pipe is pumped, do we want to pump it
// Otherwise we might advance the pump head prematurely
if (paths.isEmpty() && level.getBlockState(headPosBelow).getFluidState().isSource()) {
Expand All @@ -366,29 +390,30 @@ private PumpQueue findSourcePath(Level level, BlockPos headPos, FluidType fluidT
return new PumpQueue(paths, fluidType);
}

private void updateQueueState(int queueSourceAmount) {
/**
* Advances the pump head if the block below is air and the pump queue is empty.
*/
private boolean canAdvancePumpHead() {
// position of the pump head, i.e. the position of the lowest mining pipe
BlockPos headPos = getPos().below(pumpHeadY);

if (pumpQueue == null || pumpQueue.queue.isEmpty()) {
Level level;
// TODO set back to /4
if(getOffsetTimer() % Math.max(getPumpingCycleLength()/8, 1) == 0 && ((level = getLevel()) != null)) {
if((level = getLevel()) != null) {
BlockPos downPos = headPos.below(1);
var downBlock = level.getBlockState(downPos);

if (downBlock.getBlock() instanceof LiquidBlock) {
FluidType fluidType = downBlock.getFluidState().getFluidType();
pumpQueue = findSourcePath(getLevel(), headPos, fluidType, queueSourceAmount, true);
} else if (downBlock.isAir()) {
if (downBlock.isAir()) {
this.pumpHeadY++;

if (level instanceof ServerLevel serverLevel) {
serverLevel.setBlockAndUpdate(downPos, GTBlocks.MINER_PIPE.getDefaultState());
}
return true;
}
}
}
return false;
}

@Override
Expand All @@ -404,10 +429,18 @@ public void onMachineRemoved() {

record SourceState(BlockState state, BlockPos pos) {}

/**
* Does a full pump cycle, trying to do the required number of pumps. It will rebuild the queue if it becomes
* empty without having fulfilled its required number of pumps. All paths computed in the queue are checked
* if they are still valid and consist only of the right fluid.
*/
private void pumpCycle() {
if (getLevel() == null) {
Level level;
if ((level = getLevel()) == null) {
return;
}
// Will only update if the queue is empty
updatePumpQueue(null);
int pumps = pumpsPerCycle();

// We try to pump `pumps` amount of source blocks, using multiple paths if necessary
Expand All @@ -419,7 +452,11 @@ private void pumpCycle() {

// We iterate through the positions to check if it is still a valid path, saving the states
for (BlockPos pos : pumpPath) {
BlockState state = getLevel().getBlockState(pos);
// Stop once an unloaded block is found
if (!level.isLoaded(pos)) {
break;
}
BlockState state = level.getBlockState(pos);
if (state.getBlock() instanceof LiquidBlock liquidBlock &&
(liquidBlock.getFluidState(state)).getFluidType() == pumpQueue.fluidType()) {
states.add(new SourceState(state, pos));
Expand All @@ -445,7 +482,6 @@ private void pumpCycle() {
this.fluidSourceBlocks.remove(pos);
pumped = true;
pumps--;
System.out.println("Pumped: " + pos);
}
}
}
Expand All @@ -454,14 +490,19 @@ private void pumpCycle() {
if (pumpPath.isEmpty()) {
pumpQueue.queue().remove();
}

// If we have pumps left over and there is still more to be pumped at the current level
// (But it wasn't in the queue because maybe it's the final source block below the pump head)
// We still want to be able to pump
if (pumps > 0 && pumpQueue.queue().isEmpty()) {
updatePumpQueue(pumpQueue.fluidType());
}
}

// Use energy if any pumps happened at all
if (pumped) {
energyContainer.changeEnergy(-GTValues.V[getTier()] * 2);
}


}

public void update() {
Expand All @@ -471,30 +512,43 @@ public void update() {

// do not do anything without enough energy supplied
if (energyContainer.getEnergyStored() < GTValues.V[getTier()] * 2) {
System.out.println("pump not enough energy");
return;
}
int pumps = pumpsPerCycle();
// Try to put 5 times as many in the queue as there are pumps
updateQueueState(pumps*5);
if (getOffsetTimer() % getPumpingCycleLength() == 0) {
System.out.println("trying pump.");
// Try to put 5 times as many in the queue as there are pumps in the cycle
// In practice only EV tier has more than 1 pump per cycle
// The queue can contain at most the y-levels at the pump head or just the y-level below, so for many oil veins
// It will not be the ideal size
boolean advanced = false;
if (getOffsetTimer() % (getPumpingCycleLength() * 2L) == 0) {
advanced = canAdvancePumpHead();
}
if (!advanced && getOffsetTimer() % getPumpingCycleLength() == 0) {
pumpCycle();
}
}

private float getPumpTierMultiplier() {
// For tier 1
private int queueSize() {
return 5*pumpsPerCycle();
}

private float ticksPerPump() {
// How many ticks pass per pump. This is the ideal amount and thus can be less than 1
// For LV this is 80/1 = 80
float tierMultiplier = (float) (1 << (getTier() - 1));
return tierMultiplier / PUMP_SPEED_BASE;
return PUMP_SPEED_BASE / tierMultiplier;
}

private int pumpsPerCycle() {
return Math.max(1, (int) getPumpTierMultiplier());
// The pumping cycle length can not be less than 20, so to ensure we still have the right amount of pumps
// We need to compensate with pumps per cycle

return (int) (getPumpingCycleLength() / ticksPerPump());
}

private int getPumpingCycleLength() {
return Math.max(1, (int) (1f / getPumpTierMultiplier()));
// For basic pumps this means once every 80 ticks
// It never pumps more than once every 20 ticks, but pumps more per cycle to compensate
return Math.max(20, (int) ticksPerPump());
}

//////////////////////////////////////
Expand Down

0 comments on commit 52ec9bf

Please sign in to comment.