|
17 | 17 | import net.minecraftforge.fluids.FluidStack; |
18 | 18 | import net.minecraftforge.fluids.capability.IFluidHandler; |
19 | 19 | import net.minecraftforge.fluids.capability.IFluidTankProperties; |
| 20 | +import org.apache.logging.log4j.message.FormattedMessage; |
20 | 21 |
|
21 | | -import java.util.List; |
| 22 | +import java.util.*; |
22 | 23 | import java.util.function.Predicate; |
23 | 24 |
|
24 | 25 |
|
@@ -73,32 +74,128 @@ protected int doTransferExact(int transferLimit, IFluidHandler sourceHandler, IF |
73 | 74 | return transferLimit - fluidLeftToTransfer; |
74 | 75 | } |
75 | 76 |
|
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; |
98 | 164 | } |
99 | | - if (fluidLeftToTransfer == 0) break; |
100 | 165 | } |
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; |
102 | 199 | } |
103 | 200 |
|
104 | 201 | public void setTransferMode(TransferMode transferMode) { |
|
0 commit comments