Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 40 additions & 51 deletions src/main/java/electrodynamics/common/network/type/FluidNetwork.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,15 @@ public FluidStack emit(FluidStack inserted, ArrayList<BlockEntity> ignored, bool
return FluidStack.EMPTY;
}

// This algorithm is not perfect, but it helps deal with tiles that do not accept the full amount allotted to them
// Greedy distribution with a running remainder. The previous implementation
// pre-divided the available amount across acceptors using truncating integer
// division, so when amount < acceptors * connections the per-share rounded to
// 0 and the trailing 1..N mB never moved, leaving residual fluid in upstream
// tanks indefinitely. Offering the full remainder to each acceptor in turn
// preserves throughput on evenly-divisible amounts while correctly delivering
// the remainder.

FluidStack perTile, prePerTile, perConnection, prePerConnection;

int size = availableAcceptors.size();

int connectionsSize, amtTaken, takenAmt;
int amtTaken;

HashSet<Direction> connections;

Expand All @@ -98,34 +100,27 @@ public FluidStack emit(FluidStack inserted, ArrayList<BlockEntity> ignored, bool
continue;
}

perTile = new FluidStack(initial.getFluid(), (int) ((double) initial.getAmount() / (double) size));
prePerTile = perTile.copy();
if (initial.isEmpty()) {
break;
}

connections = acceptorInputMap.getOrDefault(tile, new HashSet<>());

connectionsSize = connections.size();

for (Direction dir : connections) {

perConnection = new FluidStack(initial.getFluid(), (int) ((double) perTile.getAmount() / (double) connectionsSize));
prePerConnection = perConnection.copy();

amtTaken = FluidUtilities.receiveFluid(tile, dir, perConnection, false);
if (initial.isEmpty()) {
break;
}

perConnection.shrink(amtTaken);
FluidStack offer = new FluidStack(initial.getFluid(), initial.getAmount());

perTile.shrink(prePerConnection.getAmount() - perConnection.getAmount());
amtTaken = FluidUtilities.receiveFluid(tile, dir, offer, false);

connectionsSize--;
if (amtTaken > 0) {
initial.shrink(amtTaken);
taken.grow(amtTaken);
}
}

takenAmt = prePerTile.getAmount() - perTile.getAmount();

initial.shrink(takenAmt);

taken.grow(takenAmt);

size--;
}

return taken;
Expand Down Expand Up @@ -179,52 +174,46 @@ private Pair<FluidStack, Set<TileFluidPipePump>> emitToPumpSet(FluidStack transf
FluidStack initial = transfer.copy();
FluidStack taken = new FluidStack(transfer.getFluid(), 0);

FluidStack perTile, prePerTile, perConnection, prePerConnection;
// Greedy distribution; same fix as in emit(). See comment there.

HashSet<TileFluidPipePump> filledPumps = new HashSet<>();

int size = recievingTiles.size();

int connectionsSize, amtTaken, takenAmt;
int amtTaken;

HashSet<Direction> connections;

for (TileFluidPipePump tile : recievingTiles) {
if (!tile.isPowered() || ignored.contains(tile)) {
size--;
continue;
}

perTile = new FluidStack(initial.getFluid(), initial.getAmount() / size);
prePerTile = perTile.copy();
if (initial.isEmpty()) {
break;
}

connections = acceptorInputMap.getOrDefault(tile, new HashSet<>());
int beforeAmount = initial.getAmount();

connectionsSize = connections.size();
connections = acceptorInputMap.getOrDefault(tile, new HashSet<>());

for (Direction dir : connections) {

perConnection = new FluidStack(initial.getFluid(), perTile.getAmount() / connectionsSize);
prePerConnection = perConnection.copy();
if (initial.isEmpty()) {
break;
}

amtTaken = FluidUtilities.receiveFluid(tile, dir, perConnection, false);
FluidStack offer = new FluidStack(initial.getFluid(), initial.getAmount());

perConnection.shrink(amtTaken);
amtTaken = FluidUtilities.receiveFluid(tile, dir, offer, false);

perTile.shrink(prePerConnection.getAmount() - perConnection.getAmount());

connectionsSize--;
if (amtTaken > 0) {
initial.shrink(amtTaken);
taken.grow(amtTaken);
}
}

takenAmt = prePerTile.getAmount() - perTile.getAmount();

initial.shrink(takenAmt);

taken.grow(takenAmt);

filledPumps.add(tile);

size--;
if (initial.getAmount() < beforeAmount) {
filledPumps.add(tile);
}
}

return Pair.of(taken, filledPumps);
Expand Down Expand Up @@ -297,4 +286,4 @@ public FluidNetwork createInstanceConductor(Set<GenericTileFluidPipe> conductors
return new FluidNetwork(conductors);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,18 @@ public void tickServer(ComponentTickable tickable) {

int amtToProcess = Math.min(room, processAmount.getValue());

// Clamp to the actual amount available in the input tank. drain() already caps
// to whatever's stored, but the matching fill() below uses amtToProcess directly,
// so without this clamp a partially-filled input tank produces strictly more
// output fluid than was consumed (e.g. 1 mB in -> processAmount mB out at high
// energy levels). This is most visible with intermittent upstream flow.
amtToProcess = Math.min(amtToProcess, fluidHandler.getInputTanks()[0].getFluidAmount());

if (amtToProcess <= 0) {
isActive.setValue(false);
return;
}

electro.setJoulesStored(0);

isActive.setValue(true);
Expand Down Expand Up @@ -171,24 +183,52 @@ private static boolean testRecipe(ElectrolosisChamberRecipe recipe, FluidTank[]
return false;
}

/**
* Index of the fluid-output slave port in the multiblock layout
* ({@code data/electrodynamics/voltaic/multiblock/electrolosischamber.json}).
*/
private static final int FLUID_OUT_SLAVE_INDEX = 39;

private void outputToPipe() {

if (level == null || level.isClientSide()) {
return;
}

if (!isFormed.getValue()) {
return;
}

ComponentFluidHandlerMulti component = getComponent(IComponentType.FluidHandler);
Direction[] outputDirections = component.outputDirections;

Direction facing = getFacing();

// Resolve the fluid-out port's actual world position from slavePositions instead
// of guessing with a fixed (2, 0, 2) offset. The fixed offset only matched the
// port's location for east-facing builds; for the other three facings the
// neighbour lookup hit empty / wrong blocks and no fluid was ever output.
@SuppressWarnings("unchecked")
List<BlockPos> positions = (List<BlockPos>) slavePositions.getValue();
if (positions == null || positions.size() <= FLUID_OUT_SLAVE_INDEX) {
return;
}
BlockPos portPos = positions.get(FLUID_OUT_SLAVE_INDEX);
if (portPos == null) {
return;
}

for (Direction relative : outputDirections) {

Direction direction = BlockEntityUtils.getRelativeSide(facing, relative);

BlockEntity faceTile = getLevel().getBlockEntity(getBlockPos().relative(direction).offset(2, 0, 2));
BlockEntity faceTile = level.getBlockEntity(portPos.relative(direction));

if (faceTile == null) {
continue;
}

IFluidHandler handler = getLevel().getCapability(Capabilities.FluidHandler.BLOCK, faceTile.getBlockPos(),
IFluidHandler handler = level.getCapability(Capabilities.FluidHandler.BLOCK, faceTile.getBlockPos(),
faceTile.getBlockState(), faceTile, direction.getOpposite());

if (handler == null) {
Expand All @@ -199,11 +239,15 @@ private void outputToPipe() {

FluidStack tankFluid = fluidTank.getFluid();

int amtAccepted = handler.fill(tankFluid, IFluidHandler.FluidAction.EXECUTE);
if (tankFluid.isEmpty()) {
continue;
}

FluidStack taken = new FluidStack(tankFluid.getFluid(), amtAccepted);
int amtAccepted = handler.fill(tankFluid, IFluidHandler.FluidAction.EXECUTE);

fluidTank.drain(taken, IFluidHandler.FluidAction.EXECUTE);
if (amtAccepted > 0) {
fluidTank.drain(new FluidStack(tankFluid.getFluid(), amtAccepted), IFluidHandler.FluidAction.EXECUTE);
}
}
}
}
Expand Down Expand Up @@ -276,4 +320,4 @@ public ResourceLocation getMultiblockId() {
public ResourceKey<Multiblock> getResourceKey() {
return RESOURCE_KEY;
}
}
}