Skip to content

Commit 8ee188e

Browse files
committed
Tanks with I/O connections now increase the throughput of fluid pipe networks
1 parent 242a889 commit 8ee188e

File tree

17 files changed

+309
-44
lines changed

17 files changed

+309
-44
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ dependencies {
280280

281281
implementation "me.shedaniel.cloth:cloth-config-neoforge:${project.cloth_config_version}"
282282

283-
/*localRuntimeOnly*/ compileOnly("curse.maven:jade-324717:${project.jade_file_id}")
283+
localRuntimeOnly compileOnly("curse.maven:jade-324717:${project.jade_file_id}")
284284

285285
// TODO: compileOnly because implementation will cause datagen to crash... hopefully kubejs fixes it soon
286286
/*localRuntimeOnly*/ compileOnly("dev.latvian.mods:kubejs-neoforge:${project.kubejs_version}")

src/client/java/aztech/modern_industrialization/MIClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ private static void registerAdditionalModels(ModelEvent.RegisterAdditional event
210210

211211
private static final List<Runnable> blockEntityRendererRegistrations = new ArrayList<>();
212212

213-
public static <T extends BlockEntity, U extends T> void registerBlockEntityRenderer(Supplier<BlockEntityType<U>> bet,
213+
public static <T extends BlockEntity, U extends T> void registerBlockEntityRenderer(Supplier<? extends BlockEntityType<? extends U>> bet,
214214
BlockEntityRendererProvider<T> renderer) {
215215
blockEntityRendererRegistrations.add(() -> BlockEntityRenderers.register(bet.get(), renderer));
216216
}

src/client/java/aztech/modern_industrialization/proxy/ClientProxy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public BlockEntityWithoutLevelRenderer getCustomRenderer() {
102102
}
103103

104104
@Override
105-
public void registerPartTankClient(Supplier<BlockEntityType<AbstractTankBlockEntity>> blockEntityType, int meanRgb) {
105+
public void registerPartTankClient(Supplier<? extends BlockEntityType<? extends AbstractTankBlockEntity>> blockEntityType, int meanRgb) {
106106
MIClient.registerBlockEntityRenderer(blockEntityType, context -> new TankRenderer(TextureHelper.getOverlayTextColor(meanRgb)));
107107
}
108108

src/main/java/aztech/modern_industrialization/blocks/storage/tank/TankBlockEntity.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
*/
2424
package aztech.modern_industrialization.blocks.storage.tank;
2525

26+
import aztech.modern_industrialization.pipes.fluid.FluidNetworkExtensionTank;
2627
import aztech.modern_industrialization.thirdparty.fabrictransfer.api.bridge.SlotFluidHandler;
2728
import net.minecraft.core.BlockPos;
2829
import net.minecraft.world.InteractionHand;
@@ -32,6 +33,7 @@
3233
import net.neoforged.neoforge.fluids.FluidUtil;
3334

3435
public class TankBlockEntity extends AbstractTankBlockEntity {
36+
public final FluidNetworkExtensionTank fluidHandler = new FluidNetworkExtensionTank(this);
3537

3638
public TankBlockEntity(BlockEntityType<?> bet, BlockPos pos, BlockState state) {
3739
super(bet, pos, state);

src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/LargeTankMultiblockBlockEntity.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,7 @@ private static ShapeSelection.LineInfo createLineInfo(int[] sizes, MIText baseTe
9696
public static void registerFluidAPI(BlockEntityType<?> bet) {
9797
MICapabilities.onEvent(event -> {
9898
event.registerBlockEntity(Capabilities.FluidHandler.BLOCK, bet, (be, direction) -> {
99-
LargeTankMultiblockBlockEntity tank = ((LargeTankMultiblockBlockEntity) be);
100-
if (tank.isShapeValid()) {
101-
return tank.fluidStorage.getFluidHandler();
102-
} else {
103-
return EmptyFluidHandler.INSTANCE;
104-
}
99+
return ((LargeTankMultiblockBlockEntity) be).getExposedFluidHandler();
105100
});
106101
});
107102
}

src/main/java/aztech/modern_industrialization/machines/components/FluidStorageComponent.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
import aztech.modern_industrialization.api.machine.component.FluidAccess;
2727
import aztech.modern_industrialization.machines.IComponent;
28-
import aztech.modern_industrialization.thirdparty.fabrictransfer.api.bridge.SlotFluidHandler;
28+
import aztech.modern_industrialization.pipes.fluid.FluidNetworkExtensionTank;
2929
import aztech.modern_industrialization.thirdparty.fabrictransfer.api.fluid.FluidVariant;
3030
import aztech.modern_industrialization.thirdparty.fabrictransfer.api.storage.base.SingleVariantStorage;
3131
import com.google.common.base.Preconditions;
@@ -48,7 +48,7 @@ protected long getCapacity(FluidVariant variant) {
4848
return capacity;
4949
}
5050
};
51-
private final IFluidHandler fluidHandler = new SlotFluidHandler(singleStorageVariant);
51+
private final IFluidHandler fluidHandler = new FluidNetworkExtensionTank(singleStorageVariant);
5252

5353
public SingleVariantStorage<FluidVariant> getFluidStorage() {
5454
return singleStorageVariant;

src/main/java/aztech/modern_industrialization/materials/part/TankPart.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
import aztech.modern_industrialization.items.ContainerItem;
3838
import aztech.modern_industrialization.items.SortOrder;
3939
import aztech.modern_industrialization.proxy.CommonProxy;
40-
import aztech.modern_industrialization.thirdparty.fabrictransfer.api.bridge.SlotFluidHandler;
4140
import aztech.modern_industrialization.thirdparty.fabrictransfer.api.fluid.FluidVariant;
4241
import java.util.function.BiConsumer;
4342
import net.minecraft.world.level.block.Block;
@@ -71,7 +70,7 @@ public PartTemplate of(long bucketCapacity) {
7170
}
7271

7372
public PartTemplate of(PartEnglishNameFormatter englishNameFormatter, long bucketCapacity, @Nullable String maybePathOverridden) {
74-
MutableObject<BlockEntityType<AbstractTankBlockEntity>> bet = new MutableObject<>();
73+
MutableObject<BlockEntityType<TankBlockEntity>> bet = new MutableObject<>();
7574
long capacity = FluidType.BUCKET_VOLUME * bucketCapacity;
7675

7776
PartTemplate tank = new PartTemplate(englishNameFormatter, key())
@@ -103,7 +102,7 @@ public PartTemplate of(PartEnglishNameFormatter englishNameFormatter, long bucke
103102
});
104103

105104
MICapabilities.onEvent(event -> {
106-
event.registerBlockEntity(Capabilities.FluidHandler.BLOCK, bet.getValue(), (be, side) -> new SlotFluidHandler(be));
105+
event.registerBlockEntity(Capabilities.FluidHandler.BLOCK, bet.getValue(), (be, side) -> be.fluidHandler);
107106

108107
var item = (TankItem) blockDefinition.asItem();
109108
event.registerItem(Capabilities.FluidHandler.ITEM, (stack, ignored) -> new ContainerItem.FluidHandler(stack, item), item);

src/main/java/aztech/modern_industrialization/pipes/PipeStatsCollector.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
package aztech.modern_industrialization.pipes;
2525

2626
public class PipeStatsCollector {
27-
private static int REFRESH_RATE = 20;
27+
private static final int REFRESH_RATE = 20;
2828

2929
private long lastStat = 0;
3030
private long currentTot = 0;

src/main/java/aztech/modern_industrialization/pipes/fluid/FluidNetwork.java

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,21 @@
2929
import aztech.modern_industrialization.pipes.api.PipeNetworkNode;
3030
import aztech.modern_industrialization.thirdparty.fabrictransfer.api.fluid.FluidVariant;
3131
import com.google.common.primitives.Ints;
32+
import com.mojang.logging.LogUtils;
3233
import java.util.ArrayList;
3334
import java.util.Collections;
3435
import java.util.Comparator;
3536
import java.util.List;
3637
import net.minecraft.server.level.ServerLevel;
3738
import net.neoforged.neoforge.fluids.capability.IFluidHandler;
39+
import org.slf4j.Logger;
3840

3941
public class FluidNetwork extends PipeNetwork {
42+
private static final Logger LOGGER = LogUtils.getLogger();
43+
4044
final int nodeCapacity;
4145
final PipeStatsCollector stats = new PipeStatsCollector();
46+
final PipeStatsCollector capacityStats = new PipeStatsCollector();
4247

4348
public FluidNetwork(int id, PipeNetworkData data, int nodeCapacity) {
4449
super(id, data == null ? new FluidNetworkData(FluidVariant.blank()) : data);
@@ -49,11 +54,12 @@ public FluidNetwork(int id, PipeNetworkData data, int nodeCapacity) {
4954
public void tick(ServerLevel world) {
5055
// Gather targets and hopefully set fluid
5156
List<FluidTarget> targets = new ArrayList<>();
57+
List<FluidNetworkExtensionTank> extensions = new ArrayList<>();
5258
long networkAmount = 0;
5359
int loadedNodeCount = 0;
5460
for (var entry : iterateTickingNodes()) {
5561
FluidNetworkNode fluidNode = (FluidNetworkNode) entry.getNode();
56-
fluidNode.gatherTargetsAndPickFluid(world, entry.getPos(), targets);
62+
fluidNode.gatherTargetsAndPickFluid(world, entry.getPos(), targets, extensions);
5763
// Amount goes after the gather...() call because the gather...() call cleans
5864
// invalid amounts.
5965
networkAmount += fluidNode.amount;
@@ -65,15 +71,38 @@ public void tick(ServerLevel world) {
6571
long extracted = 0, inserted = 0;
6672

6773
if (!fluid.isBlank()) {
74+
var it = extensions.iterator();
75+
while (it.hasNext()) {
76+
var extension = it.next();
77+
if (extension.tryClaimForNetwork(world, fluid)) {
78+
networkAmount += extension.storage().getAmount();
79+
networkCapacity += extension.getCapacity();
80+
extension.clear();
81+
} else {
82+
it.remove();
83+
}
84+
}
85+
6886
// Extract from targets into the network
6987
extracted = transferByPriority(TransferOperation.EXTRACT, targets, fluid, networkCapacity - networkAmount);
7088
networkAmount += extracted;
7189
// Insert into the targets from the network
7290
inserted = transferByPriority(TransferOperation.INSERT, targets, fluid, networkAmount);
7391
networkAmount -= inserted;
7492

75-
// Split fluid evenly across the nodes
76-
// Rebalance fluid inside the nodes
93+
// Rebalance fluid inside the extensions and nodes
94+
var sortedExtensions = new ArrayList<>(extensions);
95+
sortedExtensions.sort((ext1, ext2) -> -Long.compare(ext1.getCapacity(), ext2.getCapacity()));
96+
97+
long removedCapacity = 0;
98+
for (var extension : sortedExtensions) {
99+
var capacity = extension.getCapacity();
100+
long toInsert = (long) Math.ceil((double) networkAmount * capacity / (networkCapacity - removedCapacity));
101+
extension.releaseFromNetwork(fluid, toInsert);
102+
networkAmount -= toInsert;
103+
removedCapacity += capacity;
104+
}
105+
77106
for (var entry : iterateTickingNodes()) {
78107
FluidNetworkNode fluidNode = (FluidNetworkNode) entry.getNode();
79108
fluidNode.amount = networkAmount / loadedNodeCount;
@@ -83,6 +112,7 @@ public void tick(ServerLevel world) {
83112
}
84113

85114
stats.addValue(Math.max(extracted, inserted));
115+
capacityStats.addValue(networkCapacity);
86116

87117
for (var entry : iterateTickingNodes()) {
88118
((FluidNetworkNode) entry.getNode()).afterTick(world, entry.getPos());
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2020 Azercoco & Technici4n
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package aztech.modern_industrialization.pipes.fluid;
25+
26+
import aztech.modern_industrialization.thirdparty.fabrictransfer.api.bridge.SlotFluidHandler;
27+
import aztech.modern_industrialization.thirdparty.fabrictransfer.api.fluid.FluidVariant;
28+
import aztech.modern_industrialization.thirdparty.fabrictransfer.api.storage.base.SingleSlotStorage;
29+
import aztech.modern_industrialization.thirdparty.fabrictransfer.api.transaction.Transaction;
30+
import net.minecraft.world.level.Level;
31+
32+
/**
33+
* Act as a pipe network extension when connected to a fluid pipe in I/O mode.
34+
*/
35+
public class FluidNetworkExtensionTank extends SlotFluidHandler {
36+
private static final int NOT_CLAIMED = -1;
37+
private int lastClaimTick = NOT_CLAIMED;
38+
39+
public FluidNetworkExtensionTank(SingleSlotStorage<FluidVariant> storage) {
40+
super(storage);
41+
}
42+
43+
public boolean tryClaimForNetwork(Level level, FluidVariant networkFluid) {
44+
if (storage.getAmount() != 0 && !storage.getResource().equals(networkFluid)) {
45+
return false;
46+
}
47+
48+
try (var tx = Transaction.openOuter()) {
49+
storage.extract(networkFluid, storage.getAmount(), tx);
50+
long inserted = storage.insert(networkFluid, storage.getCapacity(), tx);
51+
if (inserted != storage.getCapacity()) {
52+
// Tank locked to a different fluid
53+
return false;
54+
}
55+
}
56+
57+
int tick = level.getServer().getTickCount();
58+
if (tick > lastClaimTick) {
59+
lastClaimTick = tick;
60+
return true;
61+
} else {
62+
return false;
63+
}
64+
}
65+
66+
public void clear() {
67+
if (storage.getAmount() == 0) {
68+
return;
69+
}
70+
try (var tx = Transaction.openOuter()) {
71+
storage.extract(storage.getResource(), storage.getAmount(), tx);
72+
tx.commit();
73+
}
74+
if (storage.getAmount() > 0) {
75+
throw new IllegalStateException("Internal MI error: extension %s should be empty after clearing it.".formatted(this));
76+
}
77+
}
78+
79+
public void releaseFromNetwork(FluidVariant fluid, long amount) {
80+
if (storage.getAmount() > 0) {
81+
throw new IllegalStateException("Internal MI error: extension %s should be empty when being released.".formatted(this));
82+
}
83+
lastClaimTick = NOT_CLAIMED;
84+
if (fluid.isBlank()) {
85+
if (storage.getAmount() != 0) {
86+
throw new IllegalStateException("Internal MI error: releasing extension %s from network with non-empty tank.".formatted(this));
87+
}
88+
}
89+
try (var tx = Transaction.openOuter()) {
90+
long inserted = storage.insert(fluid, amount, tx);
91+
tx.commit();
92+
if (inserted != amount) {
93+
throw new IllegalStateException(
94+
"Internal MI error: releasing extension %s, only inserted %d out of %d.".formatted(this, inserted, amount));
95+
}
96+
}
97+
}
98+
99+
public long getCapacity() {
100+
return storage.getCapacity();
101+
}
102+
103+
@Override
104+
protected boolean disallowIo() {
105+
return lastClaimTick != NOT_CLAIMED;
106+
}
107+
108+
// TODO: lock and unlock insertion?
109+
110+
@Override
111+
public String toString() {
112+
return "FluidNetworkExtensionTank{" + storage + '}';
113+
}
114+
}

0 commit comments

Comments
 (0)