• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CyclopsMC / IntegratedDynamics / 20677570370

03 Jan 2026 12:57PM UTC coverage: 53.285% (+0.2%) from 53.096%
20677570370

push

github

rubensworks
Fix crash on mechanical drying basin auto-export, Closes #1585

2867 of 8632 branches covered (33.21%)

Branch coverage included in aggregate %.

17207 of 29041 relevant lines covered (59.25%)

3.12 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

61.61
/src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityMechanicalSqueezer.java
1
package org.cyclops.integrateddynamics.blockentity;
2

3
import com.google.common.base.Predicates;
4
import com.google.common.collect.Lists;
5
import net.minecraft.core.BlockPos;
6
import net.minecraft.core.Direction;
7
import net.minecraft.core.NonNullList;
8
import net.minecraft.network.chat.Component;
9
import net.minecraft.world.MenuProvider;
10
import net.minecraft.world.entity.player.Inventory;
11
import net.minecraft.world.entity.player.Player;
12
import net.minecraft.world.inventory.AbstractContainerMenu;
13
import net.minecraft.world.item.ItemStack;
14
import net.minecraft.world.item.crafting.CraftingInput;
15
import net.minecraft.world.item.crafting.RecipeHolder;
16
import net.minecraft.world.item.crafting.RecipeType;
17
import net.minecraft.world.level.Level;
18
import net.minecraft.world.level.block.entity.BlockEntityType;
19
import net.minecraft.world.level.block.state.BlockState;
20
import net.minecraft.world.level.storage.ValueInput;
21
import net.minecraft.world.level.storage.ValueOutput;
22
import net.neoforged.neoforge.fluids.FluidStack;
23
import net.neoforged.neoforge.transfer.ResourceHandler;
24
import net.neoforged.neoforge.transfer.ResourceHandlerUtil;
25
import net.neoforged.neoforge.transfer.fluid.FluidResource;
26
import net.neoforged.neoforge.transfer.transaction.Transaction;
27
import org.cyclops.cyclopscore.datastructure.SingleCache;
28
import org.cyclops.cyclopscore.fluid.SingleUseTank;
29
import org.cyclops.cyclopscore.helper.IModHelpers;
30
import org.cyclops.cyclopscore.helper.IModHelpersNeoForge;
31
import org.cyclops.cyclopscore.persist.nbt.NBTPersist;
32
import org.cyclops.integrateddynamics.RegistryEntries;
33
import org.cyclops.integrateddynamics.block.BlockMechanicalSqueezer;
34
import org.cyclops.integrateddynamics.block.BlockMechanicalSqueezerConfig;
35
import org.cyclops.integrateddynamics.core.blockentity.BlockEntityMechanicalMachine;
36
import org.cyclops.integrateddynamics.core.recipe.handler.RecipeHandlerSqueezer;
37
import org.cyclops.integrateddynamics.core.recipe.type.RecipeMechanicalSqueezer;
38
import org.cyclops.integrateddynamics.core.recipe.type.RecipeSqueezer;
39
import org.cyclops.integrateddynamics.inventory.container.ContainerMechanicalSqueezer;
40

41
import javax.annotation.Nullable;
42
import java.util.Optional;
43
import java.util.function.Supplier;
44

45
/**
46
 * A part entity for the mechanical squeezer.
47
 * @author rubensworks
48
 */
49
public class BlockEntityMechanicalSqueezer extends BlockEntityMechanicalMachine<ItemStack, RecipeMechanicalSqueezer>
50
        implements MenuProvider {
51

52
    public static final int INVENTORY_SIZE = 5;
53

54
    private static final int SLOT_INPUT = 0;
55
    private static final int[] SLOTS_OUTPUT = {1, 2, 3, 4};
19✔
56
    private static final int TANK_SIZE = IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume() * 100;
7✔
57

58
    @NBTPersist
3✔
59
    private boolean autoEjectFluids = false;
60

61
    private final SingleUseTank tank = new SingleUseTank(TANK_SIZE);
6✔
62

63
    public BlockEntityMechanicalSqueezer(BlockPos blockPos, BlockState blockState) {
64
        super(RegistryEntries.BLOCK_ENTITY_MECHANICAL_SQUEEZER.get(), blockPos, blockState, INVENTORY_SIZE);
8✔
65

66
        // Add tank update listeners
67
        tank.addDirtyMarkListener(this::onTankChanged);
5✔
68
    }
1✔
69

70
    public static class CapabilityRegistrar extends BlockEntityMechanicalMachine.CapabilityRegistrar<BlockEntityMechanicalSqueezer> {
71
        public CapabilityRegistrar(Supplier<BlockEntityType<? extends BlockEntityMechanicalSqueezer>> blockEntityType) {
72
            super(blockEntityType);
3✔
73
        }
1✔
74

75
        @Override
76
        public void populate() {
77
            super.populate();
2✔
78

79
            add(
4✔
80
                    net.neoforged.neoforge.capabilities.Capabilities.Fluid.BLOCK,
81
                    (blockEntity, direction) -> blockEntity.getTank()
×
82
            );
83
            add(
4✔
84
                    org.cyclops.commoncapabilities.api.capability.Capabilities.RecipeHandler.BLOCK,
85
                    (blockEntity, direction) -> new RecipeHandlerSqueezer<>(blockEntity::getLevel, RegistryEntries.RECIPETYPE_MECHANICAL_SQUEEZER.get())
×
86
            );
87
        }
1✔
88
    }
89

90
    @Override
91
    protected SingleCache.ICacheUpdater<ItemStack, Optional<RecipeHolder<RecipeMechanicalSqueezer>>> createCacheUpdater() {
92
        return new SingleCache.ICacheUpdater<ItemStack, Optional<RecipeHolder<RecipeMechanicalSqueezer>>>() {
11✔
93
            @Override
94
            public Optional<RecipeHolder<RecipeMechanicalSqueezer>> getNewValue(ItemStack key) {
95
                return IModHelpers.get().getCraftingHelpers().findRecipe(getRecipeRegistry(), CraftingInput.of(1, 1, Lists.newArrayList(key)), getLevel());
20✔
96
            }
97

98
            @Override
99
            public boolean isKeyEqual(ItemStack cacheKey, ItemStack newKey) {
100
                return ItemStack.matches(cacheKey, newKey);
4✔
101
            }
102
        };
103
    }
104

105
    @Override
106
    public int[] getInputSlots() {
107
        return new int[]{SLOT_INPUT};
×
108
    }
109

110
    @Override
111
    public int[] getOutputSlots() {
112
        return SLOTS_OUTPUT;
×
113
    }
114

115
    @Override
116
    public boolean wasWorking() {
117
        return getLevel().getBlockState(getBlockPos()).getValue(BlockMechanicalSqueezer.LIT);
10✔
118
    }
119

120
    @Override
121
    public void setWorking(boolean working) {
122
        getLevel().setBlockAndUpdate(getBlockPos(), getLevel().getBlockState(getBlockPos())
13✔
123
                .setValue(BlockMechanicalSqueezer.LIT, working));
3✔
124
    }
1✔
125

126
    public SingleUseTank getTank() {
127
        return tank;
3✔
128
    }
129

130
    @Override
131
    public void read(ValueInput input) {
132
        super.read(input);
×
133
        getTank().deserialize(input, "tank");
×
134
    }
×
135

136
    @Override
137
    public void saveAdditional(ValueOutput output) {
138
        getTank().serialize(output, "tank");
5✔
139
        super.saveAdditional(output);
3✔
140
    }
1✔
141

142
    @Override
143
    protected RecipeType<RecipeMechanicalSqueezer> getRecipeRegistry() {
144
        return RegistryEntries.RECIPETYPE_MECHANICAL_SQUEEZER.get();
4✔
145
    }
146

147
    @Override
148
    protected ItemStack getCurrentRecipeCacheKey() {
149
        return getInventory().getItem(SLOT_INPUT).copy();
6✔
150
    }
151

152
    @Override
153
    public int getRecipeDuration(RecipeHolder<RecipeMechanicalSqueezer> recipe) {
154
        return recipe.value().getDuration();
5✔
155
    }
156

157
    @Override
158
    protected boolean finalizeRecipe(RecipeMechanicalSqueezer recipe, boolean simulate) {
159
        // Output items
160
        NonNullList<ItemStack> outputStacks = NonNullList.create();
2✔
161
        for (RecipeSqueezer.IngredientChance itemStackChance : recipe.assemble(getInventory().getItem(SLOT_INPUT))) {
15✔
162
            ItemStack outputStack = itemStackChance.getIngredientFirst().copy();
4✔
163
            if (!outputStack.isEmpty() && (simulate || itemStackChance.getChance() == 1.0F
11!
164
                    || itemStackChance.getChance() >= getLevel().random.nextFloat())) {
7✔
165
                IModHelpers.get().getInventoryHelpers().addStackToList(outputStacks, outputStack);
5✔
166
            }
167
        }
1✔
168
        if (!IModHelpers.get().getInventoryHelpers().addToInventory(getInventory(), SLOTS_OUTPUT, outputStacks, simulate).isEmpty()) {
10!
169
            return false;
×
170
        }
171

172
        // Output fluid
173
        Optional<FluidStack> outputFluid = recipe.getOutputFluid();
3✔
174
        if (outputFluid.isPresent()) {
3!
175
            try (var tx = Transaction.openRoot()) {
2✔
176
                int inserted = getTank().insert(FluidResource.of(outputFluid.get()), outputFluid.get().getAmount(), tx);
13✔
177
                if (!simulate) {
2✔
178
                    tx.commit();
2✔
179
                }
180
                if (inserted != outputFluid.get().getAmount()) {
6!
181
                    return false;
×
182
                }
183
            }
×
184
        }
185

186
        // Only consume items if we are not simulating
187
        if (!simulate) {
2✔
188
            getInventory().removeItem(SLOT_INPUT, 1);
6✔
189
        }
190

191
        return true;
2✔
192
    }
193

194
    @Override
195
    public int getEnergyConsumptionRate() {
196
        return BlockMechanicalSqueezerConfig.consumptionRate;
2✔
197
    }
198

199
    @Override
200
    public int getMaxEnergyStored() {
201
        return BlockMechanicalSqueezerConfig.capacity;
2✔
202
    }
203

204
    public boolean isAutoEjectFluids() {
205
        return autoEjectFluids;
3✔
206
    }
207

208
    public void setAutoEjectFluids(boolean autoEjectFluids) {
209
        this.autoEjectFluids = autoEjectFluids;
×
210
        sendUpdate();
×
211
    }
×
212

213
    @Nullable
214
    @Override
215
    public AbstractContainerMenu createMenu(int id, Inventory playerInventory, Player playerEntity) {
216
        return new ContainerMechanicalSqueezer(id, playerInventory, this.getInventory(), Optional.of(this));
×
217
    }
218

219
    @Override
220
    public Component getDisplayName() {
221
        return Component.translatable("block.integrateddynamics.mechanical_squeezer");
×
222
    }
223

224
    public static class Ticker extends BlockEntityMechanicalMachine.Ticker<ItemStack, RecipeMechanicalSqueezer, BlockEntityMechanicalSqueezer> {
3✔
225
        @Override
226
        protected void update(Level level, BlockPos pos, BlockState blockState, BlockEntityMechanicalSqueezer blockEntity) {
227
            super.update(level, pos, blockState, blockEntity);
6✔
228

229
            // Auto-eject fluid
230
            if (blockEntity.isAutoEjectFluids() && !blockEntity.getTank().isEmpty()) {
3!
231
                for (Direction side : Direction.values()) {
×
232
                    ResourceHandler<FluidResource> handler = IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(level, pos.relative(side),
×
233
                            side.getOpposite(), net.neoforged.neoforge.capabilities.Capabilities.Fluid.BLOCK).orElse(null);
×
234
                    if(handler != null) {
×
235
                        FluidStack fluidStack = blockEntity.getTank().getFluid().copy();
×
236
                        fluidStack.setAmount(Math.min(BlockMechanicalSqueezerConfig.autoEjectFluidRate, fluidStack.getAmount()));
×
237
                        try (var tx = Transaction.openRoot()) {
×
238
                            int moved = ResourceHandlerUtil.move(blockEntity.getTank(), handler, Predicates.alwaysTrue(), BlockMechanicalSqueezerConfig.autoEjectFluidRate, tx);
×
239
                            tx.commit();
×
240
                            if (moved > 0) {
×
241
                                break;
242
                            }
243
                        }
×
244
                    }
245
                }
246
            }
247
        }
1✔
248
    }
249
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc