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

CyclopsMC / IntegratedDynamics / 20210191346

14 Dec 2025 03:32PM UTC coverage: 19.514% (-33.5%) from 53.061%
20210191346

push

github

rubensworks
Remove deprecations

663 of 8728 branches covered (7.6%)

Branch coverage included in aggregate %.

6786 of 29445 relevant lines covered (23.05%)

1.09 hits per line

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

4.93
/src/main/java/org/cyclops/integrateddynamics/core/blockentity/BlockEntityMechanicalMachine.java
1
package org.cyclops.integrateddynamics.core.blockentity;
2

3
import net.minecraft.core.BlockPos;
4
import net.minecraft.core.Direction;
5
import net.minecraft.world.item.ItemStack;
6
import net.minecraft.world.item.crafting.Recipe;
7
import net.minecraft.world.item.crafting.RecipeHolder;
8
import net.minecraft.world.item.crafting.RecipeType;
9
import net.minecraft.world.level.Level;
10
import net.minecraft.world.level.block.entity.BlockEntityType;
11
import net.minecraft.world.level.block.state.BlockState;
12
import net.minecraft.world.level.storage.ValueInput;
13
import net.minecraft.world.level.storage.ValueOutput;
14
import net.neoforged.neoforge.transfer.energy.SimpleEnergyHandler;
15
import net.neoforged.neoforge.transfer.item.VanillaContainerWrapper;
16
import org.apache.commons.lang3.ArrayUtils;
17
import org.cyclops.cyclopscore.datastructure.DimPos;
18
import org.cyclops.cyclopscore.datastructure.SingleCache;
19
import org.cyclops.cyclopscore.inventory.InventorySlotMasked;
20
import org.cyclops.cyclopscore.inventory.SimpleInventory;
21
import org.cyclops.cyclopscore.persist.nbt.NBTPersist;
22
import org.cyclops.integrateddynamics.Capabilities;
23
import org.cyclops.integrateddynamics.api.network.IEnergyNetwork;
24
import org.cyclops.integrateddynamics.api.network.INetworkElement;
25
import org.cyclops.integrateddynamics.api.network.INetworkElementProvider;
26
import org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetwork;
27
import org.cyclops.integrateddynamics.capability.networkelementprovider.NetworkElementProviderSingleton;
28
import org.cyclops.integrateddynamics.core.helper.NetworkHelpers;
29
import org.cyclops.integrateddynamics.network.MechanicalMachineNetworkElement;
30

31
import java.util.Optional;
32
import java.util.function.Supplier;
33

34
/**
35
 * An abstract machine base tile entity that is able to process recipes by consuming energy.
36
 * @param <RCK> The recipe cache key type.
37
 * @param <R> The recipe type.
38
 */
39
public abstract class BlockEntityMechanicalMachine<RCK, R extends Recipe<?>> extends BlockEntityCableConnectableInventory {
40

41
    /**
42
     * The number of ticks to sleep when the recipe could not be finalized.
43
     */
44
    private static int SLEEP_TIME = 40;
×
45

46
    private final SimpleEnergyHandler energyHandler;
47

48
    @NBTPersist
×
49
    private int progress = -1;
50
    @NBTPersist
×
51
    private int sleep = -1;
52

53
    private SingleCache<RCK, Optional<RecipeHolder<R>>> recipeCache;
54

55
    public BlockEntityMechanicalMachine(BlockEntityType<?> type, BlockPos blockPos, BlockState blockState, int inventorySize) {
56
        super(type, blockPos, blockState, inventorySize, 64);
×
57

58
        this.energyHandler = new SimpleEnergyHandler(getMaxEnergyStored(), getMaxEnergyStored(), 0, 0) {
×
59
            @Override
60
            protected void onEnergyChanged(int previousAmount) {
61
                super.onEnergyChanged(previousAmount);
×
62
                setChanged();
×
63
            }
×
64
        };
65

66
        // Efficient cache to retrieve the current craftable recipe.
67
        recipeCache = new SingleCache<>(createCacheUpdater());
×
68
    }
×
69

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

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

79
            add(
4✔
80
                    Capabilities.NetworkElementProvider.BLOCK,
81
                    (blockEntity, direction) -> blockEntity.getNetworkElementProvider()
×
82
            );
83
            add(
4✔
84
                    net.neoforged.neoforge.capabilities.Capabilities.Energy.BLOCK,
85
                    (blockEntity, direction) -> blockEntity.getEnergyHandler()
×
86
            );
87
            add(
4✔
88
                    net.neoforged.neoforge.capabilities.Capabilities.Item.BLOCK,
89
                    (blockEntity, direction) -> {
90
                        int[] slots = direction == Direction.DOWN ? blockEntity.getOutputSlots() : blockEntity.getInputSlots();
×
91
                        return VanillaContainerWrapper.of(new InventorySlotMasked(blockEntity.getInventory(), slots));
×
92
                    }
93
            );
94
        }
1✔
95
    }
96

97
    @Override
98
    public INetworkElementProvider getNetworkElementProvider() {
99
        return new NetworkElementProviderSingleton() {
×
100
            @Override
101
            public INetworkElement createNetworkElement(Level world, BlockPos blockPos) {
102
                return new MechanicalMachineNetworkElement(DimPos.of(world, blockPos));
×
103
            }
104
        };
105
    }
106

107
    public SimpleEnergyHandler getEnergyHandler() {
108
        return energyHandler;
×
109
    }
110

111
    /**
112
     * @return A new cache updater instance.
113
     */
114
    protected abstract SingleCache.ICacheUpdater<RCK, Optional<RecipeHolder<R>>> createCacheUpdater();
115

116
    /**
117
     * @return The available input slots.
118
     */
119
    public abstract int[] getInputSlots();
120

121
    /**
122
     * @return The available output slots.
123
     */
124
    public abstract int[] getOutputSlots();
125

126
    /**
127
     * @return If the machine was in a working state.
128
     */
129
    public abstract boolean wasWorking();
130

131
    /**
132
     * Set the new working state.
133
     * @param working If the machine is working.
134
     */
135
    public abstract void setWorking(boolean working);
136

137
    /**
138
     * @return If the machine currently has any work to process.
139
     */
140
    public boolean hasWork() {
141
        return getCurrentRecipe() != null;
×
142
    }
143

144
    /**
145
     * @return If the machine is currently working.
146
     */
147
    public boolean isWorking() {
148
        return this.progress >= 0 && this.sleep == -1;
×
149
    }
150

151
    /**
152
     * @return If the machine is able to work in its current state.
153
     *         This for example takes into account the available energy.
154
     */
155
    public boolean canWork() {
156
        int rate = getEnergyConsumptionRate();
×
157
        return drainEnergy(rate, true) == rate && !level.hasNeighborSignal(getBlockPos());
×
158
    }
159

160
    /**
161
     * @return If the machine is currently sleeping due to a recipe that could not be finalized.
162
     */
163
    public boolean isSleeping() {
164
        return this.sleep > 0;
×
165
    }
166

167
    public void setProgress(int progress) {
168
        this.progress = progress;
×
169
    }
×
170

171
    public void setSleep(int sleep) {
172
        this.sleep = sleep;
×
173
    }
×
174

175
    public int getSleep() {
176
        return sleep;
×
177
    }
178

179
    public Optional<IEnergyNetwork> getEnergyNetwork() {
180
        return NetworkHelpers.getEnergyNetwork(getNetwork());
×
181
    }
182

183
    public void onTankChanged() {
184
        setChanged();
×
185
        getInventory().setChanged();
×
186
    }
×
187

188
    @Override
189
    protected SimpleInventory createInventory(int inventorySize, int stackSize) {
190
        return new SimpleInventory(inventorySize, stackSize) {
×
191
            @Override
192
            public boolean canPlaceItem(int i, ItemStack itemstack) {
193
                return ArrayUtils.contains(getInputSlots(), i) && super.canPlaceItem(i, itemstack);
×
194
            }
195

196
            @Override
197
            protected void onInventoryChanged() {
198
                super.onInventoryChanged();
×
199
                BlockEntityMechanicalMachine.this.sleep = -1;
×
200
            }
×
201
        };
202
    }
203

204
    /**
205
     * @return The recipe registry this machine should work with..
206
     */
207
    protected abstract RecipeType<? extends R> getRecipeRegistry();
208

209
    /**
210
     * @return The current recipe cache key that is used to determine the current input of a recipe.
211
     */
212
    protected abstract RCK getCurrentRecipeCacheKey();
213

214
    /**
215
     * @return The currently applicable recipe.
216
     */
217
    public Optional<RecipeHolder<R>> getCurrentRecipe() {
218
        return recipeCache.get(getCurrentRecipeCacheKey());
×
219
    }
220

221
    /**
222
     * @return The current recipe progress, going from 0 to maxProgress.
223
     */
224
    public int getProgress() {
225
        return progress;
×
226
    }
227

228
    /**
229
     * @return The current maximum progress.
230
     */
231
    public int getMaxProgress() {
232
        return this.getCurrentRecipe()
×
233
                .map(this::getRecipeDuration)
×
234
                .orElse(0);
×
235
    }
236

237
    /**
238
     * @param recipe A recipe.
239
     * @return The duration of a given recipe.
240
     */
241
    public abstract int getRecipeDuration(RecipeHolder<R> recipe);
242

243
    /**
244
     * Finalize a recipe.
245
     * This should insert the recipe output in the machine, and consume the input.
246
     * If the output could not be added, this method should return false.
247
     * @param recipe A recipe.
248
     * @param simulate If finalization should be simulated.
249
     * @return If finalization was successful.
250
     */
251
    protected abstract boolean finalizeRecipe(R recipe, boolean simulate);
252

253
    /**
254
     * Update the working state.
255
     */
256
    public void updateWorkingState() {
257
        boolean wasWorking = wasWorking();
×
258
        boolean isWorking = isWorking();
×
259
        if (isWorking != wasWorking) {
×
260
            setWorking(isWorking);
×
261
        }
262
    }
×
263

264
    /**
265
     * @return The energy consumption rate per (working) tick.
266
     */
267
    public abstract int getEnergyConsumptionRate();
268

269
    /**
270
     * Drain energy from the internal buffer or the attached network.
271
     * @param amount The amount of energy.
272
     * @param simulate If drainage should be simulated.
273
     * @return The drained energy.
274
     */
275
    protected int drainEnergy(int amount, boolean simulate) {
276
        int toDrain = amount;
×
277

278
        // First, check internal buffer
279
        toDrain -= this.extractEnergyInternal(toDrain, simulate);
×
280

281
        if (toDrain > 0) {
×
282
            // If we still need energy, ask it from the network.
283
            IEnergyNetwork energyNetwork = getEnergyNetwork().orElse(null);
×
284
            if (energyNetwork != null) {
×
285
                toDrain -= energyNetwork.getChannel(IPositionedAddonsNetwork.DEFAULT_CHANNEL).extract(toDrain, simulate);
×
286
            }
287
        }
288
        return amount - toDrain;
×
289
    }
290

291
    protected int extractEnergyInternal(int energy, boolean simulate) {
292
        energy = Math.max(0, energy);
×
293
        int stored = getEnergyHandler().getAmountAsInt();
×
294
        int newEnergy = Math.max(stored - energy, 0);
×
295
        if(!simulate) {
×
296
            getEnergyHandler().set(newEnergy);
×
297
        }
298
        return stored - newEnergy;
×
299
    }
300

301
    protected abstract int getMaxEnergyStored();
302

303
    @Override
304
    public void read(ValueInput input) {
305
        super.read(input);
×
306
        energyHandler.deserialize(input);
×
307
    }
×
308

309
    @Override
310
    public void saveAdditional(ValueOutput output) {
311
        super.saveAdditional(output);
×
312
        energyHandler.serialize(output);
×
313
    }
×
314

315
    public static class Ticker<RCK, R extends Recipe<?>, BE extends BlockEntityMechanicalMachine<RCK, R>> extends BlockEntityCableConnectableInventory.Ticker<BE> {
×
316
        @Override
317
        protected void update(Level level, BlockPos pos, BlockState blockState, BE blockEntity) {
318
            super.update(level, pos, blockState, blockEntity);
×
319

320
            if (blockEntity.isSleeping()) {
×
321
                blockEntity.setSleep(blockEntity.getSleep() - 1);
×
322
                blockEntity.setChanged();
×
323
            } else if (blockEntity.canWork()) {
×
324
                Optional<RecipeHolder<R>> recipeOptional = blockEntity.getCurrentRecipe();
×
325
                if (recipeOptional.isPresent()) {
×
326
                    RecipeHolder<R> recipe = recipeOptional.get();
×
327
                    if (blockEntity.getProgress() == 0 && !blockEntity.finalizeRecipe(recipe.value(), true)) {
×
328
                        blockEntity.setSleep(SLEEP_TIME);
×
329
                    } else if (blockEntity.getProgress() < blockEntity.getMaxProgress()) {
×
330
                        // // Consume energy while progressing
331
                        int toDrain = blockEntity.getEnergyConsumptionRate();
×
332
                        if (blockEntity.drainEnergy(toDrain, true) == toDrain) {
×
333
                            blockEntity.drainEnergy(toDrain, false);
×
334
                            blockEntity.setProgress(blockEntity.getProgress() + 1);
×
335
                            blockEntity.setSleep(-1);
×
336
                        } else {
337
                            blockEntity.setSleep(1);
×
338
                        }
339
                    } else {
×
340
                        // Otherwise, finish and output
341

342
                        // First check if we have enough room for the recipe output,
343
                        // if not, we sleep for a while.
344
                        if (blockEntity.finalizeRecipe(recipe.value(), true)) {
×
345
                            blockEntity.setProgress(0);
×
346
                            blockEntity.finalizeRecipe(recipe.value(), false);
×
347
                        } else {
348
                            blockEntity.setSleep(40);
×
349
                        }
350
                    }
351
                } else {
×
352
                    blockEntity.setProgress(-1);
×
353
                    blockEntity.setSleep(-1);
×
354
                }
355
            }
356

357
            // Check if a state update is needed.
358
            blockEntity.updateWorkingState();
×
359
        }
×
360
    }
361
}
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