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

CyclopsMC / IntegratedCrafting / #479011835

02 Jan 2026 09:46AM UTC coverage: 24.316% (+0.5%) from 23.802%
#479011835

push

github

rubensworks
Fix crafting storage not dropping when breaking crafting interfaces

0 of 5 new or added lines in 1 file covered. (0.0%)

221 existing lines in 3 files now uncovered.

755 of 3105 relevant lines covered (24.32%)

0.24 hits per line

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

0.0
/src/main/java/org/cyclops/integratedcrafting/core/part/PartTypeInterfaceCraftingBase.java
1
package org.cyclops.integratedcrafting.core.part;
2

3
import com.google.common.collect.Lists;
4
import net.minecraft.core.Direction;
5
import net.minecraft.nbt.CompoundTag;
6
import net.minecraft.nbt.ListTag;
7
import net.minecraft.nbt.Tag;
8
import net.minecraft.resources.ResourceLocation;
9
import net.minecraft.world.entity.player.Player;
10
import net.minecraft.world.item.ItemStack;
11
import net.minecraftforge.common.capabilities.Capability;
12
import net.minecraftforge.common.util.LazyOptional;
13
import org.cyclops.commoncapabilities.api.ingredient.IPrototypedIngredient;
14
import org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;
15
import org.cyclops.commoncapabilities.api.ingredient.IngredientInstanceWrapper;
16
import org.cyclops.commoncapabilities.api.ingredient.storage.IIngredientComponentStorage;
17
import org.cyclops.integratedcrafting.GeneralConfig;
18
import org.cyclops.integratedcrafting.api.crafting.CraftingJob;
19
import org.cyclops.integratedcrafting.api.crafting.CraftingJobStatus;
20
import org.cyclops.integratedcrafting.api.crafting.ICraftingInterface;
21
import org.cyclops.integratedcrafting.api.crafting.ICraftingResultsSink;
22
import org.cyclops.integratedcrafting.api.network.ICraftingNetwork;
23
import org.cyclops.integratedcrafting.capability.network.CraftingInterfaceConfig;
24
import org.cyclops.integratedcrafting.capability.network.CraftingNetworkConfig;
25
import org.cyclops.integratedcrafting.core.CraftingHelpers;
26
import org.cyclops.integratedcrafting.core.CraftingJobHandler;
27
import org.cyclops.integratedcrafting.core.CraftingProcessOverrides;
28
import org.cyclops.integratedcrafting.ingredient.storage.IngredientComponentStorageSlottedInsertProxy;
29
import org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;
30
import org.cyclops.integrateddynamics.api.network.INetwork;
31
import org.cyclops.integrateddynamics.api.network.IPartNetwork;
32
import org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetworkIngredients;
33
import org.cyclops.integrateddynamics.api.part.PartPos;
34
import org.cyclops.integrateddynamics.api.part.PartTarget;
35
import org.cyclops.integrateddynamics.api.part.PrioritizedPartPos;
36
import org.cyclops.integrateddynamics.capability.network.PositionedAddonsNetworkIngredientsHandlerConfig;
37
import org.cyclops.integrateddynamics.core.helper.NetworkHelpers;
38
import org.cyclops.integrateddynamics.core.part.PartStateBase;
39

40
import javax.annotation.Nullable;
41
import java.util.Iterator;
42
import java.util.List;
43
import java.util.ListIterator;
44
import java.util.Map;
45
import java.util.function.Function;
46

47
/**
48
 * Base logic for parts that do crafting interfacing.
49
 * @author rubensworks
50
 */
51
public abstract class PartTypeInterfaceCraftingBase<P extends PartTypeInterfaceCraftingBase<P, S>, S extends PartTypeInterfaceCraftingBase.State<P, S>> extends PartTypeCraftingBase<P, S> {
52

53
    public PartTypeInterfaceCraftingBase(String name) {
54
        super(name);
×
55
    }
×
56

57
    @Override
58
    public void afterNetworkReAlive(INetwork network, IPartNetwork partNetwork, PartTarget target, S state) {
59
        super.afterNetworkReAlive(network, partNetwork, target, state);
×
60
        addTargetToNetwork(network, target, state, true);
×
61
    }
×
62

63
    @Override
64
    public void onNetworkRemoval(INetwork network, IPartNetwork partNetwork, PartTarget target, S state) {
65
        super.onNetworkRemoval(network, partNetwork, target, state);
×
66
        removeTargetFromNetwork(network, target.getTarget(), state);
×
67
    }
×
68

69
    @Override
70
    public void onNetworkAddition(INetwork network, IPartNetwork partNetwork, PartTarget target, S state) {
71
        super.onNetworkAddition(network, partNetwork, target, state);
×
72
        addTargetToNetwork(network, target, state, true);
×
73
    }
×
74

75
    @Override
76
    public void setPriorityAndChannel(INetwork network, IPartNetwork partNetwork, PartTarget target, S state, int priority, int channel) {
77
        // We need to do this because the crafting network is not automagically aware of the priority changes,
78
        // so we have to re-add it.
79
        removeTargetFromNetwork(network, target.getTarget(), state);
×
80
        super.setPriorityAndChannel(network, partNetwork, target, state, priority, channel);
×
81
        addTargetToNetwork(network, target, state, false);
×
82
    }
×
83

84
    protected Capability<ICraftingNetwork> getNetworkCapability() {
85
        return CraftingNetworkConfig.CAPABILITY;
×
86
    }
87

88
    protected void addTargetToNetwork(INetwork network, PartTarget pos, S state, boolean initialize) {
89
        network.getCapability(getNetworkCapability())
×
90
                .ifPresent(craftingNetwork -> {
×
91
                    int channelCrafting = state.getChannelCrafting();
×
92
                    state.setTarget(pos);
×
93
                    state.setNetworks(network, craftingNetwork, NetworkHelpers.getPartNetworkChecked(network), channelCrafting, ValueDeseralizationContext.of(pos.getCenter().getPos().getLevel(true)), initialize);
×
94
                    state.setShouldAddToCraftingNetwork(true);
×
95
                });
×
96
    }
×
97

98
    protected void removeTargetFromNetwork(INetwork network, PartPos pos, S state) {
99
        ICraftingNetwork craftingNetwork = state.getCraftingNetwork();
×
100
        if (craftingNetwork != null) {
×
101
            network.getCapability(getNetworkCapability())
×
102
                    .ifPresent(n -> n.removeCraftingInterface(state.getChannelCrafting(), state));
×
103
        }
104
        state.setNetworks(null, null, null, -1, null, false);
×
105
        state.setTarget(null);
×
106
    }
×
107

108
    @Override
109
    public boolean isUpdate(S state) {
110
        return true;
×
111
    }
112

113
    @Override
114
    public int getMinimumUpdateInterval(S state) {
115
        return state.getDefaultUpdateInterval();
×
116
    }
117

118
    @Nullable
119
    protected static <T, M> IngredientInstanceWrapper<T, M> insertIntoNetwork(IngredientInstanceWrapper<T, M> wrapper,
120
                                                                              INetwork network, int channel) {
121
        IPositionedAddonsNetworkIngredients<T, M> storageNetwork = wrapper.getComponent()
×
122
                .getCapability(PositionedAddonsNetworkIngredientsHandlerConfig.CAPABILITY)
×
123
                .map(n -> (IPositionedAddonsNetworkIngredients<T, M>) n.getStorage(network).orElse(null))
×
124
                .orElse(null);
×
125
        if (storageNetwork != null) {
×
126
            IIngredientComponentStorage<T, M> storage = storageNetwork.getChannel(channel);
×
127
            T remaining = storage.insert(wrapper.getInstance(), false);
×
128
            if (wrapper.getComponent().getMatcher().isEmpty(remaining)) {
×
129
                return null;
×
130
            } else {
131
                return new IngredientInstanceWrapper<>(wrapper.getComponent(), remaining);
×
132
            }
133
        }
134
        return wrapper;
×
135
    }
136

137
    @Override
138
    public void update(INetwork network, IPartNetwork partNetwork, PartTarget target, S state) {
139
        super.update(network, partNetwork, target, state);
×
140

141
        // Init network data in part state if it has not been done yet.
142
        // This can occur when the part chunk is being reloaded.
143
        if (state.getCraftingNetwork() == null) {
×
144
            addTargetToNetwork(network, target, state, false);
×
145
        }
146

147
        int channel = state.getChannelCrafting();
×
148

149
        // Update the network data in the part state
150
        if (state.shouldAddToCraftingNetwork()) {
×
151
            ICraftingNetwork craftingNetwork = network.getCapability(getNetworkCapability()).orElse(null);
×
152
            craftingNetwork.addCraftingInterface(channel, state);
×
153
            state.setShouldAddToCraftingNetwork(false);
×
154
        }
155

156
        // Push any pending output ingredients into the network
157
        state.flushInventoryOutputBuffer(network);
×
158

159
        // Block job ticking if there still are outputs in our crafting result buffer.
160
        if (state.getInventoryOutputBuffer().isEmpty()) {
×
161
            // Tick the job handler
162
            PartPos targetPos = state.getTarget().getTarget();
×
163
            state.getCraftingJobHandler().update(network, channel, targetPos);
×
164
        }
165
    }
×
166

167
    @Override
168
    public void addDrops(PartTarget target, S state, List<ItemStack> itemStacks, boolean dropMainElement, boolean saveState) {
169
        // Drop any remaining output ingredients (only items)
170
        for (IngredientInstanceWrapper<?, ?> ingredientInstanceWrapper : state.getInventoryOutputBuffer()) {
×
171
            if (ingredientInstanceWrapper.getComponent() == IngredientComponent.ITEMSTACK) {
×
172
                itemStacks.add((ItemStack) ingredientInstanceWrapper.getInstance());
×
173
            }
174
        }
×
175
        state.getInventoryOutputBuffer().clear();
×
176

177
        // Drop buffered items from running crafting jobs (only items)
NEW
178
        for (CraftingJob craftingJob : state.getCraftingJobHandler().getAllCraftingJobs().values()) {
×
NEW
179
            for (ItemStack instance : craftingJob.getIngredientsStorageBuffer().getInstances(IngredientComponent.ITEMSTACK)) {
×
NEW
180
                itemStacks.add(instance);
×
NEW
181
            }
×
NEW
182
        }
×
183

184
        super.addDrops(target, state, itemStacks, dropMainElement, saveState);
×
185
    }
×
186

187
    public static abstract class State<P extends PartTypeInterfaceCraftingBase<P, S>, S extends PartTypeInterfaceCraftingBase.State<P, S>>
188
            extends PartStateBase<P> implements ICraftingInterface, ICraftingResultsSink {
189

190
        private final CraftingJobHandler craftingJobHandler;
191
        private final List<IngredientInstanceWrapper<?, ?>> inventoryOutputBuffer;
192

193
        private int channelCrafting = 0;
×
194
        private PartTarget target = null;
×
195
        protected INetwork network = null;
×
196
        protected IPartNetwork partNetwork = null;
×
197
        protected ICraftingNetwork craftingNetwork = null;
×
198
        private int channel = -1;
×
199
        protected ValueDeseralizationContext valueDeseralizationContext;
200
        private boolean shouldAddToCraftingNetwork = false;
×
201
        protected Player lastPlayer;
202

203
        public State() {
×
204
            this.craftingJobHandler = new CraftingJobHandler(1, true,
×
205
                    CraftingProcessOverrides.REGISTRY.getCraftingProcessOverrides(), this);
×
206
            this.inventoryOutputBuffer = Lists.newArrayList();
×
207
        }
×
208

209
        @Override
210
        public void writeToNBT(CompoundTag tag) {
211
            super.writeToNBT(tag);
×
212

213
            ListTag instanceTags = new ListTag();
×
214
            for (IngredientInstanceWrapper instanceWrapper : inventoryOutputBuffer) {
×
215
                CompoundTag instanceTag = new CompoundTag();
×
216
                instanceTag.putString("component", IngredientComponent.REGISTRY.getKey(instanceWrapper.getComponent()).toString());
×
217
                instanceTag.put("instance", instanceWrapper.getComponent().getSerializer().serializeInstance(instanceWrapper.getInstance()));
×
218
                instanceTags.add(instanceTag);
×
219
            }
×
220
            tag.put("inventoryOutputBuffer", instanceTags);
×
221

222
            this.craftingJobHandler.writeToNBT(tag);
×
223
            tag.putInt("channelCrafting", channelCrafting);
×
224
        }
×
225

226
        @Override
227
        public void readFromNBT(ValueDeseralizationContext valueDeseralizationContext, CompoundTag tag) {
228
            super.readFromNBT(valueDeseralizationContext, tag);
×
229

230
            this.inventoryOutputBuffer.clear();
×
231
            for (Tag instanceTagRaw : tag.getList("inventoryOutputBuffer", Tag.TAG_COMPOUND)) {
×
232
                CompoundTag instanceTag = (CompoundTag) instanceTagRaw;
×
233
                String componentName = instanceTag.getString("component");
×
234
                IngredientComponent<?, ?> component = IngredientComponent.REGISTRY.getValue(new ResourceLocation(componentName));
×
235
                this.inventoryOutputBuffer.add(new IngredientInstanceWrapper(component,
×
236
                        component.getSerializer().deserializeInstance(instanceTag.get("instance"))));
×
237
            }
×
238

239
            this.craftingJobHandler.readFromNBT(tag);
×
240
            this.channelCrafting = tag.getInt("channelCrafting");
×
241
        }
×
242

243
        @Override
244
        protected int getDefaultUpdateInterval() {
245
            return GeneralConfig.minCraftingInterfaceUpdateFreq;
×
246
        }
247

248
        public void setChannelCrafting(int channelCrafting) {
249
            if (this.channelCrafting != channelCrafting) {
×
250
                // Unregister from the network
251
                if (craftingNetwork != null) {
×
252
                    craftingNetwork.removeCraftingInterface(this.channelCrafting, this);
×
253
                }
254

255
                // Update the channel
256
                this.channelCrafting = channelCrafting;
×
257

258
                // Re-register to the network
259
                if (craftingNetwork != null) {
×
260
                    craftingNetwork.addCraftingInterface(this.channelCrafting, this);
×
261
                }
262

263
                sendUpdate();
×
264
            }
265
        }
×
266

267
        public int getChannelCrafting() {
268
            return channelCrafting;
×
269
        }
270

271
        public void setTarget(PartTarget target) {
272
            this.target = target;
×
273
        }
×
274

275
        public PartTarget getTarget() {
276
            return target;
×
277
        }
278

279
        public void setNetworks(@Nullable INetwork network, @Nullable ICraftingNetwork craftingNetwork,
280
                                @Nullable IPartNetwork partNetwork, int channel,
281
                                @Nullable ValueDeseralizationContext valueDeseralizationContext,
282
                                boolean initialize) {
283
            this.network = network;
×
284
            this.craftingNetwork = craftingNetwork;
×
285
            this.partNetwork = partNetwork;
×
286
            this.channel = channel;
×
287
            this.valueDeseralizationContext = valueDeseralizationContext;
×
288
            reloadRecipes(initialize);
×
289
        }
×
290

291
        public void reloadRecipes(boolean initialize) {
292
            // Do nothing
UNCOV
293
        }
×
294

295
        public void setLastPlayer(Player lastPlayer) {
296
            this.lastPlayer = lastPlayer;
×
UNCOV
297
        }
×
298

299
        public ICraftingNetwork getCraftingNetwork() {
300
            return craftingNetwork;
×
301
        }
302

303
        @Override
304
        public int getChannel() {
UNCOV
305
            return channel;
×
306
        }
307

308
        @Override
309
        public boolean canScheduleCraftingJobs() {
UNCOV
310
            return getCraftingJobHandler().canScheduleCraftingJobs();
×
311
        }
312

313
        @Override
314
        public void scheduleCraftingJob(CraftingJob craftingJob) {
UNCOV
315
            getCraftingJobHandler().scheduleCraftingJob(craftingJob);
×
UNCOV
316
        }
×
317

318
        @Override
319
        public void fillCraftingJobBufferFromStorage(CraftingJob craftingJob, Function<IngredientComponent<?, ?>, IIngredientComponentStorage> storageGetter) {
UNCOV
320
            getCraftingJobHandler().fillCraftingJobBufferFromStorage(craftingJob, storageGetter);
×
UNCOV
321
        }
×
322

323
        @Override
324
        public int getCraftingJobsCount() {
UNCOV
325
            return this.craftingJobHandler.getAllCraftingJobs().size();
×
326
        }
327

328
        @Override
329
        public Iterator<CraftingJob> getCraftingJobs() {
UNCOV
330
            return this.craftingJobHandler.getAllCraftingJobs().values().iterator();
×
331
        }
332

333
        @Override
334
        public List<Map<IngredientComponent<?, ?>, List<IPrototypedIngredient<?, ?>>>> getPendingCraftingJobOutputs(int craftingJobId) {
UNCOV
335
            List<Map<IngredientComponent<?, ?>, List<IPrototypedIngredient<?, ?>>>> pending = this.craftingJobHandler.getProcessingCraftingJobsPendingIngredients().get(craftingJobId);
×
UNCOV
336
            if (pending == null) {
×
UNCOV
337
                pending = Lists.newArrayList();
×
338
            }
339
            return pending;
×
340
        }
341

342
        @Override
343
        public CraftingJobStatus getCraftingJobStatus(ICraftingNetwork network, int channel, int craftingJobId) {
UNCOV
344
            return craftingJobHandler.getCraftingJobStatus(network, channel, craftingJobId);
×
345
        }
346

347
        @Override
348
        public void cancelCraftingJob(int channel, int craftingJobId) {
UNCOV
349
            craftingJobHandler.markCraftingJobFinished(craftingJobId);
×
UNCOV
350
        }
×
351

352
        @Override
353
        public PrioritizedPartPos getPosition() {
UNCOV
354
            return PrioritizedPartPos.of(getTarget().getCenter(), getPriority());
×
355
        }
356

357
        public CraftingJobHandler getCraftingJobHandler() {
UNCOV
358
            return craftingJobHandler;
×
359
        }
360

361
        public boolean shouldAddToCraftingNetwork() {
UNCOV
362
            return shouldAddToCraftingNetwork;
×
363
        }
364

365
        public void setShouldAddToCraftingNetwork(boolean shouldAddToCraftingNetwork) {
UNCOV
366
            this.shouldAddToCraftingNetwork = shouldAddToCraftingNetwork;
×
UNCOV
367
        }
×
368

369
        public List<IngredientInstanceWrapper<?, ?>> getInventoryOutputBuffer() {
370
            return inventoryOutputBuffer;
×
371
        }
372

373
        @Override
374
        public <T> LazyOptional<T> getCapability(Capability<T> capability, INetwork network, IPartNetwork partNetwork, PartTarget target) {
UNCOV
375
            if (capability == CraftingInterfaceConfig.CAPABILITY) {
×
UNCOV
376
                return LazyOptional.of(() -> this).cast();
×
377
            }
378

379
            // Expose the whole storage
UNCOV
380
            if (this.network != null) {
×
UNCOV
381
                IngredientComponent<?, ?> ingredientComponent = IngredientComponent.getIngredientComponentForStorageCapability(capability);
×
UNCOV
382
                if (ingredientComponent != null) {
×
383
                    T cap = wrapStorageCapability(capability, ingredientComponent);
×
384
                    if (cap != null) {
×
385
                        return LazyOptional.of(() -> cap);
×
386
                    }
387
                }
388
            }
389

UNCOV
390
            return super.getCapability(capability, network, partNetwork, target);
×
391
        }
392

393
        protected <C, T, M> C wrapStorageCapability(Capability<C> capability, IngredientComponent<T, M> ingredientComponent) {
UNCOV
394
            IIngredientComponentStorage<T, M> storage = CraftingHelpers.getNetworkStorage(this.network, this.channelCrafting,
×
395
                    ingredientComponent, false);
396

397
            // Don't allow extraction, only insertion
UNCOV
398
            storage = new IngredientComponentStorageSlottedInsertProxy<>(storage);
×
399

UNCOV
400
            return ingredientComponent.getStorageWrapperHandler(capability).wrapStorage(storage);
×
401
        }
402

403
        @Override
404
        public <T, M> void addResult(IngredientComponent<T, M> ingredientComponent, T instance) {
UNCOV
405
            this.getInventoryOutputBuffer().add(new IngredientInstanceWrapper<>(ingredientComponent, instance));
×
406

407
            // Try to flush buffer immediately
408
            if (this.network != null) {
×
UNCOV
409
                this.flushInventoryOutputBuffer(this.network);
×
410
            }
411
        }
×
412

413
        public void setIngredientComponentTargetSideOverride(IngredientComponent<?, ?> ingredientComponent, Direction side) {
414
            if (getTarget().getTarget().getSide() == side) {
×
UNCOV
415
                craftingJobHandler.setIngredientComponentTarget(ingredientComponent, null);
×
416
            } else {
417
                craftingJobHandler.setIngredientComponentTarget(ingredientComponent, side);
×
418
            }
UNCOV
419
            sendUpdate();
×
420
        }
×
421

422
        public Direction getIngredientComponentTargetSideOverride(IngredientComponent<?, ?> ingredientComponent) {
423
            Direction side = craftingJobHandler.getIngredientComponentTarget(ingredientComponent);
×
UNCOV
424
            if (side == null) {
×
UNCOV
425
                side = getTarget().getTarget().getSide();
×
426
            }
427
            return side;
×
428
        }
429

430
        public void flushInventoryOutputBuffer(INetwork network) {
431
            // Try to insert each ingredient in the buffer into the network.
UNCOV
432
            ListIterator<IngredientInstanceWrapper<?, ?>> outputBufferIt = this.getInventoryOutputBuffer().listIterator();
×
UNCOV
433
            while (outputBufferIt.hasNext()) {
×
UNCOV
434
                IngredientInstanceWrapper<?, ?> remainingInstance = outputBufferIt.next();
×
435

436
                // First try to give the ingredients to pending crafting jobs.
437
                remainingInstance = getCraftingJobHandler().beforeFlushIngredientToNetwork(remainingInstance, channelCrafting);
×
438

439
                // If none of the jobs need it, dump it into the network.
440
                remainingInstance = insertIntoNetwork(remainingInstance,
×
UNCOV
441
                        network, this.getChannelCrafting());
×
UNCOV
442
                if (remainingInstance == null) {
×
443
                    outputBufferIt.remove();
×
444
                } else {
445
                    outputBufferIt.set(remainingInstance);
×
446
                }
UNCOV
447
            }
×
448
        }
×
449
    }
450

451
}
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