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

CyclopsMC / IntegratedDynamics / 20379848724

19 Dec 2025 07:04PM UTC coverage: 45.108% (-0.05%) from 45.158%
20379848724

push

github

rubensworks
Bump mod version

2611 of 8556 branches covered (30.52%)

Branch coverage included in aggregate %.

11852 of 23507 relevant lines covered (50.42%)

2.39 hits per line

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

56.64
/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.HolderLookup;
6
import net.minecraft.core.NonNullList;
7
import net.minecraft.core.particles.BlockParticleOption;
8
import net.minecraft.core.particles.ItemParticleOption;
9
import net.minecraft.core.particles.ParticleTypes;
10
import net.minecraft.nbt.CompoundTag;
11
import net.minecraft.world.item.ItemStack;
12
import net.minecraft.world.item.crafting.RecipeHolder;
13
import net.minecraft.world.item.crafting.RecipeType;
14
import net.minecraft.world.level.Level;
15
import net.minecraft.world.level.block.Blocks;
16
import net.minecraft.world.level.block.entity.BlockEntityTicker;
17
import net.minecraft.world.level.block.entity.BlockEntityType;
18
import net.minecraft.world.level.block.state.BlockState;
19
import net.neoforged.neoforge.fluids.FluidStack;
20
import net.neoforged.neoforge.fluids.capability.IFluidHandler;
21
import org.apache.commons.lang3.tuple.Pair;
22
import org.cyclops.cyclopscore.blockentity.BlockEntityTickerDelayed;
23
import org.cyclops.cyclopscore.blockentity.CyclopsBlockEntity;
24
import org.cyclops.cyclopscore.capability.registrar.BlockEntityCapabilityRegistrar;
25
import org.cyclops.cyclopscore.datastructure.SingleCache;
26
import org.cyclops.cyclopscore.fluid.SingleUseTank;
27
import org.cyclops.cyclopscore.helper.CraftingHelpers;
28
import org.cyclops.cyclopscore.helper.FluidHelpers;
29
import org.cyclops.cyclopscore.inventory.SimpleInventory;
30
import org.cyclops.cyclopscore.inventory.SimpleInventoryState;
31
import org.cyclops.cyclopscore.persist.nbt.NBTPersist;
32
import org.cyclops.cyclopscore.recipe.type.IInventoryFluid;
33
import org.cyclops.cyclopscore.recipe.type.InventoryFluid;
34
import org.cyclops.integrateddynamics.IntegratedDynamics;
35
import org.cyclops.integrateddynamics.RegistryEntries;
36
import org.cyclops.integrateddynamics.core.recipe.handler.RecipeHandlerDryingBasin;
37
import org.cyclops.integrateddynamics.core.recipe.type.RecipeDryingBasin;
38

39
import java.util.Optional;
40
import java.util.function.Supplier;
41

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

48
    private static final int WOOD_IGNITION_TEMPERATURE = 573; // 300 degrees celcius
49

50
    private final SimpleInventory inventory;
51
    private final SingleUseTank tank;
52

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

60
    private SingleCache<Pair<ItemStack, FluidStack>, Optional<RecipeHolder<RecipeDryingBasin>>> recipeCache;
61

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

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

72
            @Override
73
            public void setItem(int slotId, ItemStack itemstack) {
74
                super.setItem(slotId, itemstack);
4✔
75
                BlockEntityDryingBasin.this.randomRotation = level.random.nextFloat() * 360;
11✔
76
                sendUpdate();
3✔
77
            }
1✔
78
        };
79
        this.tank = new SingleUseTank(FluidHelpers.BUCKET_VOLUME);
6✔
80

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

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

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

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

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

130
    public int getProgress() {
131
        return progress;
3✔
132
    }
133

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

138
    public int getFire() {
139
        return fire;
3✔
140
    }
141

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

146
    public SimpleInventory getInventory() {
147
        return inventory;
3✔
148
    }
149

150
    public SingleUseTank getTank() {
151
        return tank;
3✔
152
    }
153

154
    @Override
155
    public void read(CompoundTag tag, HolderLookup.Provider provider) {
156
        inventory.readFromNBT(provider, tag, "inventory");
×
157
        tank.readFromNBT(provider, tag, "tank");
×
158
        super.read(tag, provider);
×
159
    }
×
160

161
    @Override
162
    public void saveAdditional(CompoundTag tag, HolderLookup.Provider provider) {
163
        inventory.writeToNBT(provider, tag, "inventory");
6✔
164
        tank.writeToNBT(provider, tag, "tank");
7✔
165
        super.saveAdditional(tag, provider);
4✔
166
    }
1✔
167

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

172
    public Optional<RecipeHolder<RecipeDryingBasin>> getCurrentRecipe() {
173
        return recipeCache.get(Pair.of(getInventory().getItem(0).copy(), FluidHelpers.copy(getTank().getFluid())));
15✔
174
    }
175

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

184
    public static class TickerServer extends BlockEntityTickerDelayed<BlockEntityDryingBasin> {
3✔
185
        @Override
186
        protected void update(Level level, BlockPos pos, BlockState blockState, BlockEntityDryingBasin blockEntity) {
187
            super.update(level, pos, blockState, blockEntity);
6✔
188

189
            Optional<RecipeHolder<RecipeDryingBasin>> currentRecipe = blockEntity.getCurrentRecipe();
3✔
190
            if (!blockEntity.getTank().isEmpty() && blockEntity.getTank().getFluid().getFluid().getFluidType().getTemperature(blockEntity.getTank().getFluid()) >= WOOD_IGNITION_TEMPERATURE) {
15!
191
                blockEntity.setFire(blockEntity.getFire() + 1);
×
192
                if (blockEntity.getFire() >= 100) {
×
193
                    level.setBlockAndUpdate(pos, Blocks.FIRE.defaultBlockState());
×
194
                } else if (level.isEmptyBlock(pos.relative(Direction.UP)) && level.random.nextInt(10) == 0) {
×
195
                    level.setBlockAndUpdate(pos.relative(Direction.UP), Blocks.FIRE.defaultBlockState());
×
196
                }
197

198
            } else if (currentRecipe.isPresent()) {
3✔
199
                RecipeDryingBasin recipe = currentRecipe.get().value();
6✔
200
                if (blockEntity.getProgress() >= recipe.getDuration()) {
5✔
201
                    // Consume input fluid
202
                    int amount = FluidHelpers.getAmount(recipe.getInputFluid().orElse(FluidStack.EMPTY));
7✔
203
                    blockEntity.getTank().drain(amount, IFluidHandler.FluidAction.EXECUTE);
6✔
204

205
                    // Produce output item
206
                    ItemStack output = recipe.getOutputItemFirst();
3✔
207
                    if (!output.isEmpty()) {
3!
208
                        output = output.copy();
3✔
209
                        blockEntity.getInventory().setItem(0, output);
6✔
210
                    } else {
211
                        blockEntity.getInventory().setItem(0, ItemStack.EMPTY);
×
212
                    }
213

214
                    // Produce output fluid
215
                    if (recipe.getOutputFluid().isPresent()) {
4!
216
                        if (blockEntity.getTank().fill(recipe.getOutputFluid().get(), IFluidHandler.FluidAction.EXECUTE) == 0) {
×
217
                            IntegratedDynamics.clog(org.apache.logging.log4j.Level.ERROR, "Encountered an invalid recipe: " + currentRecipe.get().id());
×
218
                        }
219
                    }
220

221
                    blockEntity.setProgress(0);
3✔
222
                } else {
1✔
223
                    blockEntity.setProgress(blockEntity.getProgress() + 1);
6✔
224
                    blockEntity.setChanged();
2✔
225
                }
226
                blockEntity.setFire(0);
3✔
227
            } else {
1✔
228
                if ((blockEntity.getProgress() > 0) || (blockEntity.getFire() > 0)) {
6!
229
                    blockEntity.setProgress(0);
×
230
                    blockEntity.setFire(0);
×
231
                    blockEntity.setChanged();
×
232
                }
233
            }
234
        }
1✔
235
    }
236

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