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

CyclopsMC / IntegratedDynamics / 19675908292

25 Nov 2025 04:02PM UTC coverage: 53.043% (+0.005%) from 53.038%
19675908292

push

github

rubensworks
Merge remote-tracking branch 'origin/master-1.21-lts' into master-1.21

2883 of 8780 branches covered (32.84%)

Branch coverage included in aggregate %.

17356 of 29376 relevant lines covered (59.08%)

3.07 hits per line

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

55.48
/src/main/java/org/cyclops/integrateddynamics/blockentity/BlockEntityDryingBasin.java
1
package org.cyclops.integrateddynamics.blockentity;
2

3
import net.minecraft.core.BlockPos;
4
import net.minecraft.core.Direction;
5
import net.minecraft.core.NonNullList;
6
import net.minecraft.core.particles.BlockParticleOption;
7
import net.minecraft.core.particles.ItemParticleOption;
8
import net.minecraft.core.particles.ParticleTypes;
9
import net.minecraft.world.Containers;
10
import net.minecraft.world.item.ItemStack;
11
import net.minecraft.world.item.crafting.RecipeHolder;
12
import net.minecraft.world.item.crafting.RecipeType;
13
import net.minecraft.world.level.Level;
14
import net.minecraft.world.level.block.Blocks;
15
import net.minecraft.world.level.block.entity.BlockEntityTicker;
16
import net.minecraft.world.level.block.entity.BlockEntityType;
17
import net.minecraft.world.level.block.state.BlockState;
18
import net.minecraft.world.level.storage.ValueInput;
19
import net.minecraft.world.level.storage.ValueOutput;
20
import net.neoforged.neoforge.fluids.FluidStack;
21
import net.neoforged.neoforge.fluids.capability.IFluidHandler;
22
import net.neoforged.neoforge.items.wrapper.InvWrapper;
23
import org.apache.commons.lang3.tuple.Pair;
24
import org.cyclops.cyclopscore.blockentity.BlockEntityTickerDelayed;
25
import org.cyclops.cyclopscore.blockentity.CyclopsBlockEntity;
26
import org.cyclops.cyclopscore.capability.registrar.BlockEntityCapabilityRegistrar;
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.inventory.SimpleInventory;
32
import org.cyclops.cyclopscore.inventory.SimpleInventoryState;
33
import org.cyclops.cyclopscore.persist.nbt.NBTPersist;
34
import org.cyclops.cyclopscore.recipe.type.IInventoryFluid;
35
import org.cyclops.cyclopscore.recipe.type.InventoryFluid;
36
import org.cyclops.integrateddynamics.IntegratedDynamics;
37
import org.cyclops.integrateddynamics.RegistryEntries;
38
import org.cyclops.integrateddynamics.core.recipe.handler.RecipeHandlerDryingBasin;
39
import org.cyclops.integrateddynamics.core.recipe.type.RecipeDryingBasin;
40

41
import java.util.Optional;
42
import java.util.function.Supplier;
43

44
/**
45
 * A part entity for drying stuff.
46
 * @author rubensworks
47
 */
48
public class BlockEntityDryingBasin extends CyclopsBlockEntity {
49

50
    private static final int WOOD_IGNITION_TEMPERATURE = 573; // 300 degrees celcius
51

52
    private final SimpleInventory inventory;
53
    private final SingleUseTank tank;
54

55
    @NBTPersist
2✔
56
    private Float randomRotation = 0F;
2✔
57
    @NBTPersist
3✔
58
    private int progress = 0;
59
    @NBTPersist
3✔
60
    private int fire = 0;
61

62
    private SingleCache<Pair<ItemStack, FluidStack>, Optional<RecipeHolder<RecipeDryingBasin>>> recipeCache;
63

64
    public BlockEntityDryingBasin(BlockPos blockPos, BlockState blockState) {
65
        super(RegistryEntries.BLOCK_ENTITY_DRYING_BASIN.get(), blockPos, blockState);
7✔
66

67
        // Create inventory and tank
68
        this.inventory = new SimpleInventory(1, 1) {
16✔
69
            @Override
70
            public boolean canPlaceItem(int i, ItemStack itemstack) {
71
                return getItem(0).isEmpty();
×
72
            }
73

74
            @Override
75
            public void setItem(int slotId, ItemStack itemstack) {
76
                super.setItem(slotId, itemstack);
4✔
77
                BlockEntityDryingBasin.this.randomRotation = level.random.nextFloat() * 360;
11✔
78
                sendUpdate();
3✔
79
            }
1✔
80
        };
81
        this.tank = new SingleUseTank(IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume());
8✔
82

83
        // Add dirty mark listeners to inventory and tank
84
        this.inventory.addDirtyMarkListener(this::sendUpdate);
5✔
85
        this.tank.addDirtyMarkListener(this.inventory::setChanged);
9✔
86

87
        // Efficient cache to retrieve the current craftable recipe.
88
        recipeCache = new SingleCache<>(new SingleCache.ICacheUpdater<Pair<ItemStack, FluidStack>, Optional<RecipeHolder<RecipeDryingBasin>>>() {
15✔
89
            @Override
90
            public Optional<RecipeHolder<RecipeDryingBasin>> getNewValue(Pair<ItemStack, FluidStack> key) {
91
                IInventoryFluid recipeInput = new InventoryFluid(
8✔
92
                        NonNullList.of(ItemStack.EMPTY, key.getLeft()),
10✔
93
                        NonNullList.of(FluidStack.EMPTY, key.getRight()));
6✔
94
                return IModHelpers.get().getCraftingHelpers().findRecipe(getRegistry(), recipeInput, getLevel());
11✔
95
            }
96

97
            @Override
98
            public boolean isKeyEqual(Pair<ItemStack, FluidStack> cacheKey, Pair<ItemStack, FluidStack> newKey) {
99
                return cacheKey == null || newKey == null ||
6!
100
                        (ItemStack.matches(cacheKey.getLeft(), newKey.getLeft()) &&
8!
101
                                FluidStack.matches(cacheKey.getRight(), newKey.getRight()));
9!
102
            }
103
        });
104
    }
1✔
105

106
    public static class CapabilityRegistrar extends BlockEntityCapabilityRegistrar<BlockEntityDryingBasin> {
107
        public CapabilityRegistrar(Supplier<BlockEntityType<? extends BlockEntityDryingBasin>> blockEntityType) {
108
            super(blockEntityType);
3✔
109
        }
1✔
110

111
        @Override
112
        public void populate() {
113
            add(
4✔
114
                    net.neoforged.neoforge.capabilities.Capabilities.ItemHandler.BLOCK,
115
                    (blockEntity, direction) -> new InvWrapper(blockEntity.getInventory())
×
116
            );
117
            add(
4✔
118
                    org.cyclops.commoncapabilities.api.capability.Capabilities.InventoryState.BLOCK,
119
                    (blockEntity, direction) -> new SimpleInventoryState(blockEntity.getInventory())
×
120
            );
121
            add(
4✔
122
                    net.neoforged.neoforge.capabilities.Capabilities.FluidHandler.BLOCK,
123
                    (blockEntity, direction) -> blockEntity.getTank()
3✔
124
            );
125
            add(
4✔
126
                    org.cyclops.commoncapabilities.api.capability.Capabilities.RecipeHandler.BLOCK,
127
                    (blockEntity, direction) -> new RecipeHandlerDryingBasin<>(blockEntity::getLevel, RegistryEntries.RECIPETYPE_DRYING_BASIN.get())
×
128
            );
129
        }
1✔
130
    }
131

132
    public int getProgress() {
133
        return progress;
3✔
134
    }
135

136
    public void setProgress(int progress) {
137
        this.progress = progress;
3✔
138
    }
1✔
139

140
    public int getFire() {
141
        return fire;
3✔
142
    }
143

144
    public void setFire(int fire) {
145
        this.fire = fire;
3✔
146
    }
1✔
147

148
    public SimpleInventory getInventory() {
149
        return inventory;
3✔
150
    }
151

152
    public SingleUseTank getTank() {
153
        return tank;
3✔
154
    }
155

156
    @Override
157
    public void read(ValueInput input) {
158
        inventory.readFromNBT(input, "inventory");
×
159
        tank.deserialize(input, "tank");
×
160
        super.read(input);
×
161
    }
×
162

163
    @Override
164
    public void saveAdditional(ValueOutput output) {
165
        inventory.writeToNBT(output, "inventory");
5✔
166
        tank.serialize(output, "tank");
5✔
167
        super.saveAdditional(output);
3✔
168
    }
1✔
169

170
    protected RecipeType<RecipeDryingBasin> getRegistry() {
171
        return RegistryEntries.RECIPETYPE_DRYING_BASIN.get();
4✔
172
    }
173

174
    public Optional<RecipeHolder<RecipeDryingBasin>> getCurrentRecipe() {
175
        return recipeCache.get(Pair.of(getInventory().getItem(0).copy(), IModHelpersNeoForge.get().getFluidHelpers().copy(getTank().getFluid())));
17✔
176
    }
177

178
    /**
179
     * Get the random rotation for displaying the item.
180
     * @return The random rotation.
181
     */
182
    public float getRandomRotation() {
183
        return randomRotation;
×
184
    }
185

186
    @Override
187
    public void preRemoveSideEffects(BlockPos pos, BlockState state) {
188
        super.preRemoveSideEffects(pos, state);
×
189
        Containers.dropContents(level, pos, this.getInventory());
×
190
    }
×
191

192
    public static class TickerServer extends BlockEntityTickerDelayed<BlockEntityDryingBasin> {
3✔
193
        @Override
194
        protected void update(Level level, BlockPos pos, BlockState blockState, BlockEntityDryingBasin blockEntity) {
195
            super.update(level, pos, blockState, blockEntity);
6✔
196

197
            Optional<RecipeHolder<RecipeDryingBasin>> currentRecipe = blockEntity.getCurrentRecipe();
3✔
198
            if (!blockEntity.getTank().isEmpty() && blockEntity.getTank().getFluid().getFluid().getFluidType().getTemperature(blockEntity.getTank().getFluid()) >= WOOD_IGNITION_TEMPERATURE) {
15!
199
                blockEntity.setFire(blockEntity.getFire() + 1);
×
200
                if (blockEntity.getFire() >= 100) {
×
201
                    level.setBlockAndUpdate(pos, Blocks.FIRE.defaultBlockState());
×
202
                } else if (level.isEmptyBlock(pos.relative(Direction.UP)) && level.random.nextInt(10) == 0) {
×
203
                    level.setBlockAndUpdate(pos.relative(Direction.UP), Blocks.FIRE.defaultBlockState());
×
204
                }
205

206
            } else if (currentRecipe.isPresent()) {
3✔
207
                RecipeDryingBasin recipe = currentRecipe.get().value();
6✔
208
                if (blockEntity.getProgress() >= recipe.getDuration()) {
5✔
209
                    // Consume input fluid
210
                    int amount = IModHelpersNeoForge.get().getFluidHelpers().getAmount(recipe.getInputFluid().orElse(FluidStack.EMPTY));
9✔
211
                    blockEntity.getTank().drain(amount, IFluidHandler.FluidAction.EXECUTE);
6✔
212

213
                    // Produce output item
214
                    ItemStack output = recipe.getOutputItemFirst().orElse(ItemStack.EMPTY);
6✔
215
                    if (!output.isEmpty()) {
3!
216
                        output = output.copy();
3✔
217
                        blockEntity.getInventory().setItem(0, output);
6✔
218
                    } else {
219
                        blockEntity.getInventory().setItem(0, ItemStack.EMPTY);
×
220
                    }
221

222
                    // Produce output fluid
223
                    if (recipe.getOutputFluid().isPresent()) {
4!
224
                        if (blockEntity.getTank().fill(recipe.getOutputFluid().get(), IFluidHandler.FluidAction.EXECUTE) == 0) {
×
225
                            IntegratedDynamics.clog(org.apache.logging.log4j.Level.ERROR, "Encountered an invalid recipe: " + currentRecipe.get().id());
×
226
                        }
227
                    }
228

229
                    blockEntity.setProgress(0);
3✔
230
                } else {
1✔
231
                    blockEntity.setProgress(blockEntity.getProgress() + 1);
6✔
232
                    blockEntity.setChanged();
2✔
233
                }
234
                blockEntity.setFire(0);
3✔
235
            } else {
1✔
236
                if ((blockEntity.getProgress() > 0) || (blockEntity.getFire() > 0)) {
6!
237
                    blockEntity.setProgress(0);
×
238
                    blockEntity.setFire(0);
×
239
                    blockEntity.setChanged();
×
240
                }
241
            }
242
        }
1✔
243
    }
244

245
    public static class TickerClient implements BlockEntityTicker<BlockEntityDryingBasin> {
×
246
        @Override
247
        public void tick(Level level, BlockPos pos, BlockState blockState, BlockEntityDryingBasin blockEntity) {
248
            if(blockEntity.getProgress() > 0 && level.random.nextInt(5) == 0) {
×
249
                if(!blockEntity.getTank().isEmpty()) {
×
250
                    BlockState blockStateFluid = blockEntity.getTank().getFluid().getFluid().getFluidType().getBlockForFluidState(level, pos,
×
251
                            blockEntity.getTank().getFluid().getFluid().defaultFluidState());
×
252
                    if(blockStateFluid != null) {
×
253
                        level.addParticle(new BlockParticleOption(ParticleTypes.FALLING_DUST, blockStateFluid),
×
254
                                pos.getX() + Math.random() * 0.8D + 0.1D, pos.getY() + Math.random() * 0.1D + 0.9D,
×
255
                                pos.getZ() + Math.random() * 0.8D + 0.1D, 0, 0.1D, 0);
×
256
                    }
257
                }
258
                if(!blockEntity.getInventory().getItem(0).isEmpty()) {
×
259
                    ItemStack itemStack = blockEntity.getInventory().getItem(0);
×
260
                    level.addParticle(new ItemParticleOption(ParticleTypes.ITEM, itemStack),
×
261
                            pos.getX() + Math.random() * 0.8D + 0.1D, pos.getY() + Math.random() * 0.1D + 0.9D,
×
262
                            pos.getZ() + Math.random() * 0.8D + 0.1D, 0, 0.1D, 0);
×
263
                }
264
            }
265
        }
×
266
    }
267
}
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