Skip to content

Commit 795c200

Browse files
authored
New algorithm for Fluid Regulator Keep Exact Mode (#1717)
1 parent ccaf6c1 commit 795c200

File tree

2 files changed

+385
-25
lines changed

2 files changed

+385
-25
lines changed

src/main/java/gregtech/common/covers/CoverFluidRegulator.java

Lines changed: 122 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@
1717
import net.minecraftforge.fluids.FluidStack;
1818
import net.minecraftforge.fluids.capability.IFluidHandler;
1919
import net.minecraftforge.fluids.capability.IFluidTankProperties;
20+
import org.apache.logging.log4j.message.FormattedMessage;
2021

21-
import java.util.List;
22+
import java.util.*;
2223
import java.util.function.Predicate;
2324

2425

@@ -73,32 +74,128 @@ protected int doTransferExact(int transferLimit, IFluidHandler sourceHandler, IF
7374
return transferLimit - fluidLeftToTransfer;
7475
}
7576

76-
protected int doKeepExact(int transferLimit, IFluidHandler sourceHandler, IFluidHandler destHandler, Predicate<FluidStack> fluidFilter, int keepAmount) {
77-
int fluidLeftToTransfer = transferLimit;
78-
for (IFluidTankProperties tankProperties : sourceHandler.getTankProperties()) {
79-
FluidStack sourceFluid = tankProperties.getContents();
80-
if (sourceFluid == null || sourceFluid.amount == 0 || !fluidFilter.test(sourceFluid)) continue;
81-
sourceFluid.amount = keepAmount;
82-
FluidStack destFluid = destHandler.drain(sourceFluid, false);
83-
int amountToDrainAndFill;
84-
//no fluid in destination
85-
if (destFluid == null) {
86-
amountToDrainAndFill = Math.min(keepAmount, fluidLeftToTransfer);
87-
//if the amount of fluid in the destination is sufficient or the destinations fluid isnt equal to the sources
88-
//how to check if destHandler is full?
89-
} else if (destFluid.amount >= keepAmount || !destFluid.isFluidEqual(sourceFluid)) {
90-
continue;
91-
} else {
92-
//if keepAmount is larger than the transferLimit we will have to stock it over several ticks (seconds?)
93-
amountToDrainAndFill = Math.min(keepAmount - destFluid.amount, fluidLeftToTransfer);
94-
}
95-
sourceFluid.amount = amountToDrainAndFill;
96-
if (GTFluidUtils.transferExactFluidStack(sourceHandler, destHandler, sourceFluid.copy())) {
97-
fluidLeftToTransfer -= sourceFluid.amount;
77+
/**
78+
* Performs one tick worth of Keep Exact behavior.
79+
* @param transferLimit the maximum amount in milliBuckets that may be transferred in one tick
80+
* @param sourceHandler source(s) to move fluids from
81+
* @param destHandler destination(s) to move fluids to
82+
* @param fluidFilter a predicate which determines what fluids may be moved
83+
* @param keepAmount the desired amount in milliBuckets of a particular fluid in the destination
84+
* @return the total amount in milliBuckets of all fluids transferred from source to dest by this method
85+
*/
86+
protected int doKeepExact(final int transferLimit,
87+
final IFluidHandler sourceHandler,
88+
final IFluidHandler destHandler,
89+
final Predicate<FluidStack> fluidFilter,
90+
final int keepAmount) {
91+
92+
if(sourceHandler == null || destHandler == null || fluidFilter == null || keepAmount <= 0)
93+
return 0;
94+
95+
final Map<FluidStack, Integer> sourceFluids =
96+
collectDistinctFluids(sourceHandler, IFluidTankProperties::canDrain, fluidFilter);
97+
final Map<FluidStack, Integer> destFluids =
98+
collectDistinctFluids(destHandler, IFluidTankProperties::canFill, fluidFilter);
99+
100+
int transferred = 0;
101+
for(FluidStack fluidStack : sourceFluids.keySet()) {
102+
if(transferred >= transferLimit)
103+
break;
104+
105+
// if fluid needs to be moved to meet the Keep Exact value
106+
int amountInDest;
107+
if((amountInDest = destFluids.getOrDefault(fluidStack, 0)) < keepAmount) {
108+
109+
// move the lesser of the remaining transfer limit and the difference in actual vs keep exact amount
110+
int amountToMove = Math.min(transferLimit - transferred,
111+
keepAmount - amountInDest);
112+
113+
// Nothing to do here, try the next fluid.
114+
if(amountToMove <= 0)
115+
continue;
116+
117+
// Simulate a drain of this fluid from the source tanks
118+
FluidStack drainedResult = sourceHandler.drain(copyFluidStackWithAmount(fluidStack, amountToMove), false);
119+
120+
// Can't drain this fluid. Try the next one.
121+
if(drainedResult == null || drainedResult.amount <= 0 || !fluidStack.equals(drainedResult))
122+
continue;
123+
124+
// account for the possibility that the drain might give us less than requested
125+
final int drainable = Math.min(amountToMove, drainedResult.amount);
126+
127+
// Simulate a fill of the drained amount
128+
int fillResult = destHandler.fill(copyFluidStackWithAmount(fluidStack, drainable), false);
129+
130+
// Can't fill, try the next fluid.
131+
if(fillResult <= 0)
132+
continue;
133+
134+
// This Fluid can be drained and filled, so let's move the most that will actually work.
135+
int fluidToMove = Math.min(drainable, fillResult);
136+
FluidStack drainedActual = sourceHandler.drain(copyFluidStackWithAmount(fluidStack, fluidToMove), true);
137+
138+
// Account for potential error states from the drain
139+
if(drainedActual == null)
140+
throw new RuntimeException("Misbehaving fluid container: drain produced null after simulation succeeded");
141+
142+
if(!fluidStack.equals(drainedActual))
143+
throw new RuntimeException("Misbehaving fluid container: drain produced a different fluid than the simulation");
144+
145+
if(drainedActual.amount != fluidToMove)
146+
throw new RuntimeException(new FormattedMessage(
147+
"Misbehaving fluid container: drain expected: {}, actual: {}",
148+
fluidToMove,
149+
drainedActual.amount).getFormattedMessage());
150+
151+
152+
// Perform Fill
153+
int filledActual = destHandler.fill(copyFluidStackWithAmount(fluidStack, fluidToMove), true);
154+
155+
// Account for potential error states from the fill
156+
if(filledActual != fluidToMove)
157+
throw new RuntimeException(new FormattedMessage(
158+
"Misbehaving fluid container: fill expected: {}, actual: {}",
159+
fluidToMove,
160+
filledActual).getFormattedMessage());
161+
162+
// update the transferred amount
163+
transferred += fluidToMove;
98164
}
99-
if (fluidLeftToTransfer == 0) break;
100165
}
101-
return transferLimit - fluidLeftToTransfer;
166+
167+
return transferred;
168+
}
169+
170+
/**
171+
* Copies a FluidStack and sets its amount to the specified value.
172+
*
173+
* @param fs the original fluid stack to copy
174+
* @param amount the amount to set the copied FluidStack to
175+
* @return the copied FluidStack with the specified amount
176+
*/
177+
private static FluidStack copyFluidStackWithAmount(FluidStack fs, int amount) {
178+
FluidStack fs2 = fs.copy();
179+
fs2.amount = amount;
180+
return fs2;
181+
}
182+
183+
private Map<FluidStack, Integer> collectDistinctFluids(IFluidHandler handler,
184+
Predicate<IFluidTankProperties> tankTypeFilter,
185+
Predicate<FluidStack> fluidTypeFilter) {
186+
187+
final Map<FluidStack, Integer> summedFluids = new HashMap<>();
188+
Arrays.stream(handler.getTankProperties())
189+
.filter(tankTypeFilter)
190+
.map(IFluidTankProperties::getContents)
191+
.filter(Objects::nonNull)
192+
.filter(fluidTypeFilter)
193+
.forEach(fs -> {
194+
summedFluids.putIfAbsent(fs, 0);
195+
summedFluids.computeIfPresent(fs, (k,v) -> v + fs.amount);
196+
});
197+
198+
return summedFluids;
102199
}
103200

104201
public void setTransferMode(TransferMode transferMode) {

0 commit comments

Comments
 (0)