Skip to content

Commit 8c748ae

Browse files
committed
Fix #625: Make large tank controller and hatches output fill level with comparator
1 parent c8d5031 commit 8c748ae

File tree

7 files changed

+173
-19
lines changed

7 files changed

+173
-19
lines changed

src/main/java/aztech/modern_industrialization/debug/DebugCommands.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,6 @@ private static int dumpStats(ServerPlayer player) {
174174
private static int buildMultiblock(CommandSourceStack src, BlockPos controllerPos) {
175175
var be = src.getLevel().getBlockEntity(controllerPos);
176176
if (be instanceof MultiblockMachineBlockEntity multiblock) {
177-
var shape = multiblock.getActiveShape();
178177
var shapeMatcher = multiblock.createShapeMatcher();
179178
int updatedBlocks = shapeMatcher.buildMultiblock(src.getLevel());
180179

src/main/java/aztech/modern_industrialization/machines/blockentities/hatches/LargeTankHatch.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public boolean upgradesToSteel() {
8484
public void unlink() {
8585
super.unlink();
8686
controller = null;
87+
setChanged();
8788
invalidateCapabilities();
8889
}
8990

@@ -94,6 +95,7 @@ public void unlink() {
9495

9596
public void setController(LargeTankMultiblockBlockEntity controller) {
9697
this.controller = controller;
98+
setChanged();
9799
invalidateCapabilities();
98100
}
99101

@@ -106,4 +108,18 @@ public static void registerFluidApi(BlockEntityType<?> bet) {
106108
event.registerBlockEntity(Capabilities.FluidHandler.BLOCK, bet, (be, direction) -> ((LargeTankHatch) be).getStorage());
107109
});
108110
}
111+
112+
@Override
113+
protected boolean hasComparatorOutput() {
114+
return true;
115+
}
116+
117+
@Override
118+
protected int getComparatorOutput() {
119+
if (controller != null) {
120+
return controller.getComparatorOutput();
121+
} else {
122+
return 0;
123+
}
124+
}
109125
}

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

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import aztech.modern_industrialization.machines.models.MachineModelClientData;
4444
import aztech.modern_industrialization.machines.multiblocks.*;
4545
import aztech.modern_industrialization.thirdparty.fabrictransfer.api.fluid.FluidVariant;
46+
import aztech.modern_industrialization.thirdparty.fabrictransfer.api.storage.StorageUtil;
4647
import aztech.modern_industrialization.util.Tickable;
4748
import java.util.List;
4849
import java.util.stream.IntStream;
@@ -153,7 +154,7 @@ public LargeTankMultiblockBlockEntity(BEP bep) {
153154
super(bep, new MachineGuiParameters.Builder("large_tank", false).build(), new OrientationComponent.Params(false, false, false));
154155

155156
activeShape = new ActiveShapeComponent(shapeTemplates);
156-
fluidStorage = new FluidStorageComponent();
157+
fluidStorage = new FluidStorageComponent(this::setChanged);
157158

158159
this.registerComponents(activeShape, fluidStorage);
159160

@@ -228,7 +229,6 @@ protected MachineModelClientData getMachineModelData() {
228229
public void tick() {
229230
if (!level.isClientSide) {
230231
link();
231-
setChanged();
232232
if (!this.getFluidData().equals(oldFluidData)) {
233233
oldFluidData = this.getFluidData();
234234
sync(false);
@@ -251,13 +251,19 @@ protected void onRematch(ShapeMatcher shapeMatcher) {
251251
long capacity = getCapacityFromComponents(getXComponent(index), getYComponent(index), getZComponent(index));
252252
fluidStorage.setCapacity(capacity);
253253

254-
invalidateCapabilities();
254+
// Already set it here such that the setController and setChanged calls below
255+
// will see the correct comparator output of the tank
256+
shapeValid.shapeValid = true;
257+
255258
for (var hatch : shapeMatcher.getMatchedHatches()) {
256259
if (hatch instanceof LargeTankHatch tankHatch) {
257260
tankHatch.setController(this);
258261
}
259262
}
260263
}
264+
265+
setChanged();
266+
invalidateCapabilities();
261267
}
262268

263269
public IFluidHandler getExposedFluidHandler() {
@@ -277,7 +283,6 @@ public double getFullnessFraction() {
277283
}
278284

279285
public int[] getCornerPosition() {
280-
281286
int index = activeShape.getActiveShapeIndex();
282287
int sizeX = X_SIZES[getXComponent(index)];
283288
int sizeY = Y_SIZES[getYComponent(index)];
@@ -311,4 +316,17 @@ public List<Component> getTooltips() {
311316
return List.of(new MITooltips.Line(MIText.LargeTankTooltips).arg(BUCKET_PER_STRUCTURE_BLOCK).build());
312317
}
313318

319+
@Override
320+
protected boolean hasComparatorOutput() {
321+
return true;
322+
}
323+
324+
@Override
325+
public int getComparatorOutput() {
326+
if (isShapeValid()) {
327+
return StorageUtil.calculateComparatorOutput(fluidStorage.getFluidStorage());
328+
} else {
329+
return 0;
330+
}
331+
}
314332
}

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import aztech.modern_industrialization.machines.IComponent;
2929
import aztech.modern_industrialization.pipes.fluid.FluidNetworkExtensionTank;
3030
import aztech.modern_industrialization.thirdparty.fabrictransfer.api.fluid.FluidVariant;
31+
import aztech.modern_industrialization.thirdparty.fabrictransfer.api.storage.base.ResourceAmount;
3132
import aztech.modern_industrialization.thirdparty.fabrictransfer.api.storage.base.SingleVariantStorage;
3233
import com.google.common.base.Preconditions;
3334
import net.minecraft.core.HolderLookup;
@@ -36,9 +37,10 @@
3637

3738
public class FluidStorageComponent implements IComponent, FluidAccess {
3839

40+
private final Runnable changeCallback;
3941
private long capacity;
4042

41-
SingleVariantStorage<FluidVariant> singleStorageVariant = new SingleVariantStorage<FluidVariant>() {
43+
SingleVariantStorage<FluidVariant> singleStorageVariant = new SingleVariantStorage<>() {
4244
@Override
4345
protected FluidVariant getBlankVariant() {
4446
return FluidVariant.blank();
@@ -48,9 +50,20 @@ protected FluidVariant getBlankVariant() {
4850
protected long getCapacity(FluidVariant variant) {
4951
return capacity;
5052
}
53+
54+
@Override
55+
protected void onRootCommit(ResourceAmount<FluidVariant> originalState) {
56+
if (!originalState.resource().equals(variant) || originalState.amount() != amount) {
57+
changeCallback.run();
58+
}
59+
}
5160
};
5261
private final IFluidHandler fluidHandler = new FluidNetworkExtensionTank(singleStorageVariant);
5362

63+
public FluidStorageComponent(Runnable changeCallback) {
64+
this.changeCallback = changeCallback;
65+
}
66+
5467
public SingleVariantStorage<FluidVariant> getFluidStorage() {
5568
return singleStorageVariant;
5669
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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+
25+
package aztech.modern_industrialization.test;
26+
27+
import aztech.modern_industrialization.machines.blockentities.multiblocks.LargeTankMultiblockBlockEntity;
28+
import aztech.modern_industrialization.machines.init.MultiblockHatches;
29+
import aztech.modern_industrialization.machines.init.MultiblockMachines;
30+
import aztech.modern_industrialization.test.framework.MIGameTest;
31+
import aztech.modern_industrialization.test.framework.MIGameTestHelper;
32+
import net.minecraft.core.BlockPos;
33+
import net.minecraft.core.Direction;
34+
import net.minecraft.world.level.block.Blocks;
35+
import net.minecraft.world.level.block.ComparatorBlock;
36+
import net.minecraft.world.level.block.RedStoneWireBlock;
37+
import net.minecraft.world.level.material.Fluids;
38+
import net.neoforged.neoforge.fluids.FluidStack;
39+
import net.neoforged.neoforge.fluids.capability.IFluidHandler;
40+
41+
public class MultiblockTests {
42+
@MIGameTest
43+
public void largeTankEmitsRedstone(MIGameTestHelper helper) {
44+
var controllerPos = new BlockPos(3, 1, 2);
45+
helper.setBlock(controllerPos, MultiblockMachines.LARGE_TANK.asBlock());
46+
47+
LargeTankMultiblockBlockEntity largeTank = helper.getBlockEntity(controllerPos);
48+
largeTank.createShapeMatcher().buildMultiblock(helper.getLevel());
49+
50+
var comparatorPos0 = new BlockPos(3, 1, 1);
51+
helper.setBlock(comparatorPos0.below(), Blocks.STONE);
52+
helper.setBlock(comparatorPos0, Blocks.COMPARATOR.defaultBlockState().setValue(ComparatorBlock.FACING, Direction.SOUTH));
53+
54+
var hatchPos1 = new BlockPos(3, 0, 4);
55+
helper.setBlock(hatchPos1, MultiblockHatches.LARGE_TANK.asBlock());
56+
var comparatorPos1 = new BlockPos(3, 0, 5);
57+
helper.setBlock(comparatorPos1, Blocks.COMPARATOR);
58+
59+
var hatchPos2 = new BlockPos(4, 0, 2);
60+
helper.setBlock(hatchPos2, MultiblockHatches.LARGE_TANK.asBlock());
61+
var comparatorPos2 = new BlockPos(5, 0, 2);
62+
helper.setBlock(comparatorPos2, Blocks.COMPARATOR.defaultBlockState().setValue(ComparatorBlock.FACING, Direction.WEST));
63+
var redstonePos = new BlockPos(6, 0, 2);
64+
helper.setBlock(redstonePos, Blocks.REDSTONE_WIRE);
65+
66+
helper.startSequence()
67+
.thenIdle(1) // formation time
68+
.thenExecute(() -> {
69+
helper.assertBlockProperty(comparatorPos0, ComparatorBlock.POWERED, false);
70+
helper.assertBlockProperty(comparatorPos1, ComparatorBlock.POWERED, false);
71+
helper.assertBlockProperty(comparatorPos2, ComparatorBlock.POWERED, false);
72+
helper.assertBlockProperty(redstonePos, RedStoneWireBlock.POWER, 0);
73+
})
74+
.thenExecute(() -> {
75+
largeTank.getExposedFluidHandler().fill(new FluidStack(Fluids.WATER, 1), IFluidHandler.FluidAction.EXECUTE);
76+
})
77+
.thenIdle(3) // redstone update time (gametests run after the level tick so the delay is 1 more than usual)
78+
.thenExecute(() -> {
79+
helper.assertBlockProperty(comparatorPos0, ComparatorBlock.POWERED, true);
80+
helper.assertBlockProperty(comparatorPos1, ComparatorBlock.POWERED, true);
81+
helper.assertBlockProperty(comparatorPos2, ComparatorBlock.POWERED, true);
82+
helper.assertBlockProperty(redstonePos, RedStoneWireBlock.POWER, 1);
83+
})
84+
.thenExecute(() -> {
85+
largeTank.getExposedFluidHandler().fill(new FluidStack(Fluids.WATER, Integer.MAX_VALUE), IFluidHandler.FluidAction.EXECUTE);
86+
})
87+
.thenIdle(3) // redstone update time
88+
.thenExecute(() -> {
89+
helper.assertBlockProperty(comparatorPos0, ComparatorBlock.POWERED, true);
90+
helper.assertBlockProperty(comparatorPos1, ComparatorBlock.POWERED, true);
91+
helper.assertBlockProperty(comparatorPos2, ComparatorBlock.POWERED, true);
92+
helper.assertBlockProperty(redstonePos, RedStoneWireBlock.POWER, 15);
93+
})
94+
.thenExecute(() -> {
95+
helper.setBlock(hatchPos1.above(2), Blocks.AIR);
96+
})
97+
.thenIdle(3) // redstone update time
98+
.thenExecute(() -> {
99+
helper.assertBlockProperty(comparatorPos0, ComparatorBlock.POWERED, false);
100+
helper.assertBlockProperty(comparatorPos1, ComparatorBlock.POWERED, false);
101+
helper.assertBlockProperty(comparatorPos2, ComparatorBlock.POWERED, false);
102+
helper.assertBlockProperty(redstonePos, RedStoneWireBlock.POWER, 0);
103+
})
104+
.thenSucceed();
105+
}
106+
}

src/main/java/aztech/modern_industrialization/test/framework/MIGameTestHelper.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import net.minecraft.gametest.framework.GameTestInfo;
4444
import net.minecraft.world.level.block.Blocks;
4545
import net.minecraft.world.level.material.Fluid;
46+
import net.neoforged.neoforge.capabilities.BlockCapability;
4647
import net.neoforged.neoforge.capabilities.Capabilities;
4748
import org.jetbrains.annotations.Nullable;
4849

@@ -91,11 +92,16 @@ public PipeNetworkNode getPipeNode(BlockPos pos, PipeNetworkType type) {
9192
throw new RuntimeException("Unreachable!");
9293
}
9394

94-
public void assertFluid(BlockPos pos, Fluid fluid, int amount) {
95-
var fluidHandler = getLevel().getCapability(Capabilities.FluidHandler.BLOCK, absolutePos(pos), null);
96-
if (fluidHandler == null) {
97-
fail("Could not find fluid handler", pos);
95+
public <T, C extends @Nullable Object> T requireCapability(BlockCapability<T, C> cap, BlockPos pos, C context) {
96+
var ret = getLevel().getCapability(cap, absolutePos(pos), context);
97+
if (ret == null) {
98+
fail("Could not find capability " + cap.name(), pos);
9899
}
100+
return ret;
101+
}
102+
103+
public void assertFluid(BlockPos pos, Fluid fluid, int amount) {
104+
var fluidHandler = requireCapability(Capabilities.FluidHandler.BLOCK, pos, null);
99105
boolean foundAny = false;
100106
var fluidName = BuiltInRegistries.FLUID.getKey(fluid);
101107
for (int i = 0; i < fluidHandler.getTanks(); ++i) {
@@ -115,10 +121,7 @@ public void assertFluid(BlockPos pos, Fluid fluid, int amount) {
115121
}
116122

117123
public void assertNoFluid(BlockPos pos) {
118-
var fluidHandler = getLevel().getCapability(Capabilities.FluidHandler.BLOCK, absolutePos(pos), null);
119-
if (fluidHandler == null) {
120-
fail("Could not find fluid handler", pos);
121-
}
124+
var fluidHandler = requireCapability(Capabilities.FluidHandler.BLOCK, pos, null);
122125
for (int i = 0; i < fluidHandler.getTanks(); ++i) {
123126
var stack = fluidHandler.getFluidInTank(i);
124127
if (!stack.isEmpty()) {
@@ -128,10 +131,7 @@ public void assertNoFluid(BlockPos pos) {
128131
}
129132

130133
public void assertEnergy(BlockPos pos, long energy, @Nullable Direction side) {
131-
var miEnergyHandler = getLevel().getCapability(EnergyApi.SIDED, absolutePos(pos), side);
132-
if (miEnergyHandler == null) {
133-
fail("Could not find energy handler", pos);
134-
}
134+
var miEnergyHandler = requireCapability(EnergyApi.SIDED, pos, side);
135135
long storedEnergy = miEnergyHandler.getAmount();
136136
if (storedEnergy != energy) {
137137
fail("Expected energy to be " + energy + ", was " + storedEnergy, pos);

src/main/java/aztech/modern_industrialization/test/framework/MIGameTests.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import aztech.modern_industrialization.MI;
2828
import aztech.modern_industrialization.test.FluidPipeTests;
2929
import aztech.modern_industrialization.test.GeneratorTests;
30+
import aztech.modern_industrialization.test.MultiblockTests;
3031
import java.util.ArrayList;
3132
import java.util.List;
3233
import java.util.Locale;
@@ -40,7 +41,8 @@ private MIGameTests() {
4041

4142
private static final List<Class<?>> TEST_CLASSES = List.of(
4243
FluidPipeTests.class,
43-
GeneratorTests.class);
44+
GeneratorTests.class,
45+
MultiblockTests.class);
4446

4547
@GameTestGenerator
4648
public static List<TestFunction> generateTests() {

0 commit comments

Comments
 (0)