From 8ee188e0a55aa5394570eeb9a078966ef191b28c Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Thu, 26 Dec 2024 20:01:06 +0100 Subject: [PATCH] Tanks with I/O connections now increase the throughput of fluid pipe networks --- build.gradle | 2 +- .../modern_industrialization/MIClient.java | 2 +- .../proxy/ClientProxy.java | 2 +- .../blocks/storage/tank/TankBlockEntity.java | 2 + .../LargeTankMultiblockBlockEntity.java | 7 +- .../components/FluidStorageComponent.java | 4 +- .../materials/part/TankPart.java | 5 +- .../pipes/PipeStatsCollector.java | 2 +- .../pipes/fluid/FluidNetwork.java | 36 +++++- .../fluid/FluidNetworkExtensionTank.java | 114 ++++++++++++++++++ .../pipes/fluid/FluidNetworkNode.java | 14 ++- .../proxy/CommonProxy.java | 2 +- .../test/FluidPipeTests.java | 82 ++++++++++--- .../test/framework/MIGameTestHelper.java | 32 ++++- .../test/framework/PipeBuilder.java | 13 +- .../api/bridge/SlotFluidHandler.java | 22 +++- .../en_us/entries/midgame/fluid_transfer.json | 12 +- 17 files changed, 309 insertions(+), 44 deletions(-) create mode 100644 src/main/java/aztech/modern_industrialization/pipes/fluid/FluidNetworkExtensionTank.java diff --git a/build.gradle b/build.gradle index 8208d4a65..e5ce3b117 100644 --- a/build.gradle +++ b/build.gradle @@ -280,7 +280,7 @@ dependencies { implementation "me.shedaniel.cloth:cloth-config-neoforge:${project.cloth_config_version}" - /*localRuntimeOnly*/ compileOnly("curse.maven:jade-324717:${project.jade_file_id}") + localRuntimeOnly compileOnly("curse.maven:jade-324717:${project.jade_file_id}") // TODO: compileOnly because implementation will cause datagen to crash... hopefully kubejs fixes it soon /*localRuntimeOnly*/ compileOnly("dev.latvian.mods:kubejs-neoforge:${project.kubejs_version}") diff --git a/src/client/java/aztech/modern_industrialization/MIClient.java b/src/client/java/aztech/modern_industrialization/MIClient.java index a8d422d97..744c8617b 100644 --- a/src/client/java/aztech/modern_industrialization/MIClient.java +++ b/src/client/java/aztech/modern_industrialization/MIClient.java @@ -210,7 +210,7 @@ private static void registerAdditionalModels(ModelEvent.RegisterAdditional event private static final List blockEntityRendererRegistrations = new ArrayList<>(); - public static void registerBlockEntityRenderer(Supplier> bet, + public static void registerBlockEntityRenderer(Supplier> bet, BlockEntityRendererProvider renderer) { blockEntityRendererRegistrations.add(() -> BlockEntityRenderers.register(bet.get(), renderer)); } diff --git a/src/client/java/aztech/modern_industrialization/proxy/ClientProxy.java b/src/client/java/aztech/modern_industrialization/proxy/ClientProxy.java index d8ef3c28e..aec4026ef 100644 --- a/src/client/java/aztech/modern_industrialization/proxy/ClientProxy.java +++ b/src/client/java/aztech/modern_industrialization/proxy/ClientProxy.java @@ -102,7 +102,7 @@ public BlockEntityWithoutLevelRenderer getCustomRenderer() { } @Override - public void registerPartTankClient(Supplier> blockEntityType, int meanRgb) { + public void registerPartTankClient(Supplier> blockEntityType, int meanRgb) { MIClient.registerBlockEntityRenderer(blockEntityType, context -> new TankRenderer(TextureHelper.getOverlayTextColor(meanRgb))); } diff --git a/src/main/java/aztech/modern_industrialization/blocks/storage/tank/TankBlockEntity.java b/src/main/java/aztech/modern_industrialization/blocks/storage/tank/TankBlockEntity.java index dee6469c4..a9a29dda3 100644 --- a/src/main/java/aztech/modern_industrialization/blocks/storage/tank/TankBlockEntity.java +++ b/src/main/java/aztech/modern_industrialization/blocks/storage/tank/TankBlockEntity.java @@ -23,6 +23,7 @@ */ package aztech.modern_industrialization.blocks.storage.tank; +import aztech.modern_industrialization.pipes.fluid.FluidNetworkExtensionTank; import aztech.modern_industrialization.thirdparty.fabrictransfer.api.bridge.SlotFluidHandler; import net.minecraft.core.BlockPos; import net.minecraft.world.InteractionHand; @@ -32,6 +33,7 @@ import net.neoforged.neoforge.fluids.FluidUtil; public class TankBlockEntity extends AbstractTankBlockEntity { + public final FluidNetworkExtensionTank fluidHandler = new FluidNetworkExtensionTank(this); public TankBlockEntity(BlockEntityType bet, BlockPos pos, BlockState state) { super(bet, pos, state); diff --git a/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/LargeTankMultiblockBlockEntity.java b/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/LargeTankMultiblockBlockEntity.java index c1a3d7da1..f4cb2bd20 100644 --- a/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/LargeTankMultiblockBlockEntity.java +++ b/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/LargeTankMultiblockBlockEntity.java @@ -96,12 +96,7 @@ private static ShapeSelection.LineInfo createLineInfo(int[] sizes, MIText baseTe public static void registerFluidAPI(BlockEntityType bet) { MICapabilities.onEvent(event -> { event.registerBlockEntity(Capabilities.FluidHandler.BLOCK, bet, (be, direction) -> { - LargeTankMultiblockBlockEntity tank = ((LargeTankMultiblockBlockEntity) be); - if (tank.isShapeValid()) { - return tank.fluidStorage.getFluidHandler(); - } else { - return EmptyFluidHandler.INSTANCE; - } + return ((LargeTankMultiblockBlockEntity) be).getExposedFluidHandler(); }); }); } diff --git a/src/main/java/aztech/modern_industrialization/machines/components/FluidStorageComponent.java b/src/main/java/aztech/modern_industrialization/machines/components/FluidStorageComponent.java index bc3d7367e..10996b293 100644 --- a/src/main/java/aztech/modern_industrialization/machines/components/FluidStorageComponent.java +++ b/src/main/java/aztech/modern_industrialization/machines/components/FluidStorageComponent.java @@ -25,7 +25,7 @@ import aztech.modern_industrialization.api.machine.component.FluidAccess; import aztech.modern_industrialization.machines.IComponent; -import aztech.modern_industrialization.thirdparty.fabrictransfer.api.bridge.SlotFluidHandler; +import aztech.modern_industrialization.pipes.fluid.FluidNetworkExtensionTank; import aztech.modern_industrialization.thirdparty.fabrictransfer.api.fluid.FluidVariant; import aztech.modern_industrialization.thirdparty.fabrictransfer.api.storage.base.SingleVariantStorage; import com.google.common.base.Preconditions; @@ -48,7 +48,7 @@ protected long getCapacity(FluidVariant variant) { return capacity; } }; - private final IFluidHandler fluidHandler = new SlotFluidHandler(singleStorageVariant); + private final IFluidHandler fluidHandler = new FluidNetworkExtensionTank(singleStorageVariant); public SingleVariantStorage getFluidStorage() { return singleStorageVariant; diff --git a/src/main/java/aztech/modern_industrialization/materials/part/TankPart.java b/src/main/java/aztech/modern_industrialization/materials/part/TankPart.java index 9b5285bf6..6d71b976a 100644 --- a/src/main/java/aztech/modern_industrialization/materials/part/TankPart.java +++ b/src/main/java/aztech/modern_industrialization/materials/part/TankPart.java @@ -37,7 +37,6 @@ import aztech.modern_industrialization.items.ContainerItem; import aztech.modern_industrialization.items.SortOrder; import aztech.modern_industrialization.proxy.CommonProxy; -import aztech.modern_industrialization.thirdparty.fabrictransfer.api.bridge.SlotFluidHandler; import aztech.modern_industrialization.thirdparty.fabrictransfer.api.fluid.FluidVariant; import java.util.function.BiConsumer; import net.minecraft.world.level.block.Block; @@ -71,7 +70,7 @@ public PartTemplate of(long bucketCapacity) { } public PartTemplate of(PartEnglishNameFormatter englishNameFormatter, long bucketCapacity, @Nullable String maybePathOverridden) { - MutableObject> bet = new MutableObject<>(); + MutableObject> bet = new MutableObject<>(); long capacity = FluidType.BUCKET_VOLUME * bucketCapacity; PartTemplate tank = new PartTemplate(englishNameFormatter, key()) @@ -103,7 +102,7 @@ public PartTemplate of(PartEnglishNameFormatter englishNameFormatter, long bucke }); MICapabilities.onEvent(event -> { - event.registerBlockEntity(Capabilities.FluidHandler.BLOCK, bet.getValue(), (be, side) -> new SlotFluidHandler(be)); + event.registerBlockEntity(Capabilities.FluidHandler.BLOCK, bet.getValue(), (be, side) -> be.fluidHandler); var item = (TankItem) blockDefinition.asItem(); event.registerItem(Capabilities.FluidHandler.ITEM, (stack, ignored) -> new ContainerItem.FluidHandler(stack, item), item); diff --git a/src/main/java/aztech/modern_industrialization/pipes/PipeStatsCollector.java b/src/main/java/aztech/modern_industrialization/pipes/PipeStatsCollector.java index 44752a6c2..a9643aa55 100644 --- a/src/main/java/aztech/modern_industrialization/pipes/PipeStatsCollector.java +++ b/src/main/java/aztech/modern_industrialization/pipes/PipeStatsCollector.java @@ -24,7 +24,7 @@ package aztech.modern_industrialization.pipes; public class PipeStatsCollector { - private static int REFRESH_RATE = 20; + private static final int REFRESH_RATE = 20; private long lastStat = 0; private long currentTot = 0; diff --git a/src/main/java/aztech/modern_industrialization/pipes/fluid/FluidNetwork.java b/src/main/java/aztech/modern_industrialization/pipes/fluid/FluidNetwork.java index 5592b1218..fe479364a 100644 --- a/src/main/java/aztech/modern_industrialization/pipes/fluid/FluidNetwork.java +++ b/src/main/java/aztech/modern_industrialization/pipes/fluid/FluidNetwork.java @@ -29,16 +29,21 @@ import aztech.modern_industrialization.pipes.api.PipeNetworkNode; import aztech.modern_industrialization.thirdparty.fabrictransfer.api.fluid.FluidVariant; import com.google.common.primitives.Ints; +import com.mojang.logging.LogUtils; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import net.minecraft.server.level.ServerLevel; import net.neoforged.neoforge.fluids.capability.IFluidHandler; +import org.slf4j.Logger; public class FluidNetwork extends PipeNetwork { + private static final Logger LOGGER = LogUtils.getLogger(); + final int nodeCapacity; final PipeStatsCollector stats = new PipeStatsCollector(); + final PipeStatsCollector capacityStats = new PipeStatsCollector(); public FluidNetwork(int id, PipeNetworkData data, int nodeCapacity) { super(id, data == null ? new FluidNetworkData(FluidVariant.blank()) : data); @@ -49,11 +54,12 @@ public FluidNetwork(int id, PipeNetworkData data, int nodeCapacity) { public void tick(ServerLevel world) { // Gather targets and hopefully set fluid List targets = new ArrayList<>(); + List extensions = new ArrayList<>(); long networkAmount = 0; int loadedNodeCount = 0; for (var entry : iterateTickingNodes()) { FluidNetworkNode fluidNode = (FluidNetworkNode) entry.getNode(); - fluidNode.gatherTargetsAndPickFluid(world, entry.getPos(), targets); + fluidNode.gatherTargetsAndPickFluid(world, entry.getPos(), targets, extensions); // Amount goes after the gather...() call because the gather...() call cleans // invalid amounts. networkAmount += fluidNode.amount; @@ -65,6 +71,18 @@ public void tick(ServerLevel world) { long extracted = 0, inserted = 0; if (!fluid.isBlank()) { + var it = extensions.iterator(); + while (it.hasNext()) { + var extension = it.next(); + if (extension.tryClaimForNetwork(world, fluid)) { + networkAmount += extension.storage().getAmount(); + networkCapacity += extension.getCapacity(); + extension.clear(); + } else { + it.remove(); + } + } + // Extract from targets into the network extracted = transferByPriority(TransferOperation.EXTRACT, targets, fluid, networkCapacity - networkAmount); networkAmount += extracted; @@ -72,8 +90,19 @@ public void tick(ServerLevel world) { inserted = transferByPriority(TransferOperation.INSERT, targets, fluid, networkAmount); networkAmount -= inserted; - // Split fluid evenly across the nodes - // Rebalance fluid inside the nodes + // Rebalance fluid inside the extensions and nodes + var sortedExtensions = new ArrayList<>(extensions); + sortedExtensions.sort((ext1, ext2) -> -Long.compare(ext1.getCapacity(), ext2.getCapacity())); + + long removedCapacity = 0; + for (var extension : sortedExtensions) { + var capacity = extension.getCapacity(); + long toInsert = (long) Math.ceil((double) networkAmount * capacity / (networkCapacity - removedCapacity)); + extension.releaseFromNetwork(fluid, toInsert); + networkAmount -= toInsert; + removedCapacity += capacity; + } + for (var entry : iterateTickingNodes()) { FluidNetworkNode fluidNode = (FluidNetworkNode) entry.getNode(); fluidNode.amount = networkAmount / loadedNodeCount; @@ -83,6 +112,7 @@ public void tick(ServerLevel world) { } stats.addValue(Math.max(extracted, inserted)); + capacityStats.addValue(networkCapacity); for (var entry : iterateTickingNodes()) { ((FluidNetworkNode) entry.getNode()).afterTick(world, entry.getPos()); diff --git a/src/main/java/aztech/modern_industrialization/pipes/fluid/FluidNetworkExtensionTank.java b/src/main/java/aztech/modern_industrialization/pipes/fluid/FluidNetworkExtensionTank.java new file mode 100644 index 000000000..029e81227 --- /dev/null +++ b/src/main/java/aztech/modern_industrialization/pipes/fluid/FluidNetworkExtensionTank.java @@ -0,0 +1,114 @@ +/* + * MIT License + * + * Copyright (c) 2020 Azercoco & Technici4n + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package aztech.modern_industrialization.pipes.fluid; + +import aztech.modern_industrialization.thirdparty.fabrictransfer.api.bridge.SlotFluidHandler; +import aztech.modern_industrialization.thirdparty.fabrictransfer.api.fluid.FluidVariant; +import aztech.modern_industrialization.thirdparty.fabrictransfer.api.storage.base.SingleSlotStorage; +import aztech.modern_industrialization.thirdparty.fabrictransfer.api.transaction.Transaction; +import net.minecraft.world.level.Level; + +/** + * Act as a pipe network extension when connected to a fluid pipe in I/O mode. + */ +public class FluidNetworkExtensionTank extends SlotFluidHandler { + private static final int NOT_CLAIMED = -1; + private int lastClaimTick = NOT_CLAIMED; + + public FluidNetworkExtensionTank(SingleSlotStorage storage) { + super(storage); + } + + public boolean tryClaimForNetwork(Level level, FluidVariant networkFluid) { + if (storage.getAmount() != 0 && !storage.getResource().equals(networkFluid)) { + return false; + } + + try (var tx = Transaction.openOuter()) { + storage.extract(networkFluid, storage.getAmount(), tx); + long inserted = storage.insert(networkFluid, storage.getCapacity(), tx); + if (inserted != storage.getCapacity()) { + // Tank locked to a different fluid + return false; + } + } + + int tick = level.getServer().getTickCount(); + if (tick > lastClaimTick) { + lastClaimTick = tick; + return true; + } else { + return false; + } + } + + public void clear() { + if (storage.getAmount() == 0) { + return; + } + try (var tx = Transaction.openOuter()) { + storage.extract(storage.getResource(), storage.getAmount(), tx); + tx.commit(); + } + if (storage.getAmount() > 0) { + throw new IllegalStateException("Internal MI error: extension %s should be empty after clearing it.".formatted(this)); + } + } + + public void releaseFromNetwork(FluidVariant fluid, long amount) { + if (storage.getAmount() > 0) { + throw new IllegalStateException("Internal MI error: extension %s should be empty when being released.".formatted(this)); + } + lastClaimTick = NOT_CLAIMED; + if (fluid.isBlank()) { + if (storage.getAmount() != 0) { + throw new IllegalStateException("Internal MI error: releasing extension %s from network with non-empty tank.".formatted(this)); + } + } + try (var tx = Transaction.openOuter()) { + long inserted = storage.insert(fluid, amount, tx); + tx.commit(); + if (inserted != amount) { + throw new IllegalStateException( + "Internal MI error: releasing extension %s, only inserted %d out of %d.".formatted(this, inserted, amount)); + } + } + } + + public long getCapacity() { + return storage.getCapacity(); + } + + @Override + protected boolean disallowIo() { + return lastClaimTick != NOT_CLAIMED; + } + + // TODO: lock and unlock insertion? + + @Override + public String toString() { + return "FluidNetworkExtensionTank{" + storage + '}'; + } +} diff --git a/src/main/java/aztech/modern_industrialization/pipes/fluid/FluidNetworkNode.java b/src/main/java/aztech/modern_industrialization/pipes/fluid/FluidNetworkNode.java index 55924363f..342d59c74 100644 --- a/src/main/java/aztech/modern_industrialization/pipes/fluid/FluidNetworkNode.java +++ b/src/main/java/aztech/modern_industrialization/pipes/fluid/FluidNetworkNode.java @@ -65,7 +65,7 @@ public class FluidNetworkNode extends PipeNetworkNode { * Add all valid targets to the target list, and pick the fluid for the network * if no fluid is set. */ - void gatherTargetsAndPickFluid(ServerLevel world, BlockPos pos, List targets) { + void gatherTargetsAndPickFluid(ServerLevel world, BlockPos pos, List targets, List extensions) { FluidNetworkData data = (FluidNetworkData) network.data; FluidNetwork network = (FluidNetwork) this.network; @@ -84,7 +84,11 @@ void gatherTargetsAndPickFluid(ServerLevel world, BlockPos pos, List getFluidTooltip(FluidVariant variant) { public void withStandardItemRenderer(Consumer stupidClientProperties) { } - public void registerPartTankClient(Supplier> blockEntityType, int meanRgb) { + public void registerPartTankClient(Supplier> blockEntityType, int meanRgb) { } public void registerPartBarrelClient(Supplier> blockEntityType, int meanRgb) { diff --git a/src/main/java/aztech/modern_industrialization/test/FluidPipeTests.java b/src/main/java/aztech/modern_industrialization/test/FluidPipeTests.java index 7dd0455ca..5ab5b1141 100644 --- a/src/main/java/aztech/modern_industrialization/test/FluidPipeTests.java +++ b/src/main/java/aztech/modern_industrialization/test/FluidPipeTests.java @@ -24,9 +24,14 @@ package aztech.modern_industrialization.test; import aztech.modern_industrialization.MI; +import aztech.modern_industrialization.blocks.storage.tank.TankBlockEntity; +import aztech.modern_industrialization.materials.MIMaterials; import aztech.modern_industrialization.pipes.api.PipeNetworkType; +import aztech.modern_industrialization.pipes.fluid.FluidNetworkNode; import aztech.modern_industrialization.test.framework.MIGameTest; import aztech.modern_industrialization.test.framework.MIGameTestHelper; +import aztech.modern_industrialization.thirdparty.fabrictransfer.api.fluid.FluidVariant; +import aztech.modern_industrialization.thirdparty.fabrictransfer.api.transaction.Transaction; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.level.material.Fluids; @@ -37,18 +42,16 @@ public class FluidPipeTests { @MIGameTest public void testSinglePipeThroughput(MIGameTestHelper helper) { helper.creativeTank(new BlockPos(0, 1, 0), Fluids.WATER); - helper.emptyTank(new BlockPos(2, 1, 0)); + var tankToFill = new BlockPos(2, 1, 0); + helper.emptyTank(tankToFill); helper.pipe(new BlockPos(1, 1, 0), fluidPipe, pipe -> { - pipe.addConnection(Direction.EAST); - pipe.addConnection(Direction.WEST); - // Switch WEST to OUT - pipe.removeConnection(Direction.WEST); - pipe.removeConnection(Direction.WEST); + pipe.addInConnection(Direction.EAST); + pipe.addOutConnection(Direction.WEST); }); helper.startSequence() .thenIdle(1) .thenExecute(() -> { - helper.assertFluid(new BlockPos(2, 1, 0), Fluids.WATER, 1000); + helper.assertFluid(tankToFill, Fluids.WATER, 1000); }) .thenSucceed(); } @@ -56,20 +59,71 @@ public void testSinglePipeThroughput(MIGameTestHelper helper) { @MIGameTest public void testDoublePipeThroughput(MIGameTestHelper helper) { helper.creativeTank(new BlockPos(0, 1, 0), Fluids.WATER); - helper.emptyTank(new BlockPos(2, 1, 0)); + var tankToFill = new BlockPos(2, 1, 0); + helper.emptyTank(tankToFill); helper.pipe(new BlockPos(1, 1, 0), fluidPipe, pipe -> { - pipe.addConnection(Direction.EAST); - pipe.addConnection(Direction.WEST); - // Switch WEST to OUT - pipe.removeConnection(Direction.WEST); - pipe.removeConnection(Direction.WEST); + pipe.addInConnection(Direction.EAST); + pipe.addOutConnection(Direction.WEST); }); helper.pipe(new BlockPos(1, 2, 0), fluidPipe, pipe -> { }); helper.startSequence() .thenIdle(1) .thenExecute(() -> { - helper.assertFluid(new BlockPos(2, 1, 0), Fluids.WATER, 2000); + helper.assertFluid(tankToFill, Fluids.WATER, 2000); + }) + .thenSucceed(); + } + + @MIGameTest + public void testTankExtendedThroughput(MIGameTestHelper helper) { + helper.creativeTank(new BlockPos(0, 1, 0), Fluids.WATER); + var tankToFill = new BlockPos(2, 1, 0); + helper.emptyTank(tankToFill); + var extensionTank = new BlockPos(1, 2, 0); + helper.emptyTank(extensionTank, MIMaterials.BRONZE); + var pipePos = new BlockPos(1, 1, 0); + helper.pipe(pipePos, fluidPipe, pipe -> { + pipe.addInConnection(Direction.EAST); + pipe.addOutConnection(Direction.WEST); + pipe.addInOutConnection(Direction.UP); + }); + helper.startSequence() + .thenIdle(1) + .thenExecute(() -> { + helper.assertFluid(tankToFill, Fluids.WATER, 5000); + helper.assertNoFluid(extensionTank); + }) + .thenExecute(() -> { + // Replace by a bronze tank (with 4000 of capacity). + helper.emptyTank(tankToFill, MIMaterials.BRONZE); + }) + .thenIdle(1) + .thenExecute(() -> { + helper.assertFluid(tankToFill, Fluids.WATER, 4000); + helper.assertFluid(extensionTank, Fluids.WATER, 800); + var node = (FluidNetworkNode) helper.getPipeNode(pipePos, fluidPipe); + if (node.getAmount() != 200) { + helper.fail("Expected 200 fluid in pipe, got " + node.getAmount(), pipePos); + } + }) + .thenExecute(() -> { + helper.destroyBlock(tankToFill); + helper.emptyTank(tankToFill, MIMaterials.BRONZE); + // Replace by lava locked tank to make sure it gets ignored + helper.destroyBlock(extensionTank); + helper.emptyTank(extensionTank, MIMaterials.BRONZE); + var tank = (TankBlockEntity) helper.getBlockEntity(extensionTank); + try (var tx = Transaction.openOuter()) { + // hacky way to toggle lock :P + tank.insert(FluidVariant.of(Fluids.LAVA), 1000, tx); + tank.toggleLocked(); + } + }) + .thenIdle(1) + .thenExecute(() -> { + helper.assertFluid(tankToFill, Fluids.WATER, 1000); + helper.assertNoFluid(extensionTank); }) .thenSucceed(); } diff --git a/src/main/java/aztech/modern_industrialization/test/framework/MIGameTestHelper.java b/src/main/java/aztech/modern_industrialization/test/framework/MIGameTestHelper.java index df3d8e111..4c9f27c36 100644 --- a/src/main/java/aztech/modern_industrialization/test/framework/MIGameTestHelper.java +++ b/src/main/java/aztech/modern_industrialization/test/framework/MIGameTestHelper.java @@ -26,8 +26,10 @@ import aztech.modern_industrialization.MIBlock; import aztech.modern_industrialization.blocks.storage.tank.creativetank.CreativeTankBlockEntity; import aztech.modern_industrialization.materials.MIMaterials; +import aztech.modern_industrialization.materials.Material; import aztech.modern_industrialization.materials.part.MIParts; import aztech.modern_industrialization.pipes.MIPipes; +import aztech.modern_industrialization.pipes.api.PipeNetworkNode; import aztech.modern_industrialization.pipes.api.PipeNetworkType; import aztech.modern_industrialization.pipes.impl.PipeBlockEntity; import aztech.modern_industrialization.thirdparty.fabrictransfer.api.fluid.FluidVariant; @@ -52,7 +54,11 @@ public void creativeTank(BlockPos pos, Fluid fluid) { } public void emptyTank(BlockPos pos) { - setBlock(pos, MIMaterials.IRIDIUM.getPart(MIParts.TANK).asBlock()); + emptyTank(pos, MIMaterials.IRIDIUM); + } + + public void emptyTank(BlockPos pos, Material material) { + setBlock(pos, material.getPart(MIParts.TANK).asBlock()); } public void assertAir(BlockPos pos) { @@ -70,6 +76,17 @@ public void pipe(BlockPos pos, PipeNetworkType type, Consumer storage) implements IFluidHandler { +public class SlotFluidHandler implements IFluidHandler { + protected final SingleSlotStorage storage; + + public SlotFluidHandler(SingleSlotStorage storage) { + this.storage = storage; + } + @Override public int getTanks() { return 1; @@ -44,7 +50,7 @@ public int getTanks() { @Override public int fill(FluidStack stack, FluidAction action) { - if (stack.isEmpty()) { + if (disallowIo() || stack.isEmpty()) { return 0; } try (var tx = Transaction.hackyOpen()) { @@ -58,7 +64,7 @@ public int fill(FluidStack stack, FluidAction action) { @Override public FluidStack drain(FluidStack resource, FluidAction action) { - if (resource.isEmpty() || storage.isResourceBlank() || !storage.getResource().matches(resource)) { + if (disallowIo() || resource.isEmpty() || storage.isResourceBlank() || !storage.getResource().matches(resource)) { return FluidStack.EMPTY; } return drain(resource.getAmount(), action); @@ -66,7 +72,7 @@ public FluidStack drain(FluidStack resource, FluidAction action) { @Override public FluidStack drain(int amount, FluidAction action) { - if (amount <= 0) { + if (disallowIo() || amount <= 0) { return FluidStack.EMPTY; } try (var tx = Transaction.hackyOpen()) { @@ -91,4 +97,12 @@ public int getTankCapacity(int slot) { public boolean isFluidValid(int slot, @NotNull FluidStack stack) { return true; } + + public SingleSlotStorage storage() { + return storage; + } + + protected boolean disallowIo() { + return false; + } } diff --git a/src/main/resources/assets/modern_industrialization/patchouli_books/book/en_us/entries/midgame/fluid_transfer.json b/src/main/resources/assets/modern_industrialization/patchouli_books/book/en_us/entries/midgame/fluid_transfer.json index ed47c8b5a..39bb436d0 100644 --- a/src/main/resources/assets/modern_industrialization/patchouli_books/book/en_us/entries/midgame/fluid_transfer.json +++ b/src/main/resources/assets/modern_industrialization/patchouli_books/book/en_us/entries/midgame/fluid_transfer.json @@ -9,8 +9,16 @@ }, { "type": "text", - "text": "How much fluid is transferred every tick is then bounded by the total capacity of the network. For example, 25 fluid pipes linked together can transfer up to 25000 mb/t of fluid - assuming you can produce that much fluid of course!$(br2)Note that you might be limited by the transfer rates of the tanks/machines the pipes are connected to." + "text": "Regular and large tanks from Modern Industrialization will also link with the pipe network if they are connected to it using a bidirectional connection. This means I/O in the menu, or the double arrows on the pipe itself.$(br2)Tanks have a much larger internal buffer than pipes, so they can store a lot more fluid." + }, + { + "type": "text", + "text": "How much fluid is transferred every tick is then bounded by the total capacity of the network. For example, 25 fluid pipes linked together can transfer up to 25000 mb/t of fluid - assuming you can produce that much fluid of course!$(br2)Adding a single steel tank (16 buckets of storage) with an I/O connection will bring the total network rate to 41000 mb/t." + }, + { + "type": "text", + "text": "Note that you might be limited by the transfer rates of the tanks/machines the pipes are connected to." } ], "sortnum": 203 -} \ No newline at end of file +}