• 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/PendingCraftingJobResultIndexObserver.java
1
package org.cyclops.integratedcrafting.core;
2

3
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
4
import it.unimi.dsi.fastutil.ints.IntIterator;
5
import it.unimi.dsi.fastutil.objects.ObjectIterator;
6
import org.cyclops.commoncapabilities.api.ingredient.*;
7
import org.cyclops.integratedcrafting.api.crafting.CraftingJob;
8
import org.cyclops.integratedcrafting.api.network.ICraftingNetwork;
9
import org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetwork;
10
import org.cyclops.integrateddynamics.core.network.IIngredientChannelInsertPreConsumer;
11
import org.jetbrains.annotations.NotNull;
12

13
import java.util.Iterator;
14
import java.util.List;
15
import java.util.ListIterator;
16
import java.util.Map;
17

18
/**
19
 * An ingredient index observer that tracks crafting job outputs for a certain ingredient component type.
20
 *
21
 * It will observe changes and (partially) resolve awaiting crafting job outputs when applicable.
22
 *
23
 * @author rubensworks
24
 */
25
public class PendingCraftingJobResultIndexObserver<T, M> implements IIngredientChannelInsertPreConsumer<T> {
26

27
    private final IngredientComponent<T, M> ingredientComponent;
28
    private final CraftingJobHandler handler;
29
    private final ICraftingNetwork craftingNetwork;
30

UNCOV
31
    public PendingCraftingJobResultIndexObserver(IngredientComponent<T, M> ingredientComponent, CraftingJobHandler handler, ICraftingNetwork craftingNetwork) {
×
UNCOV
32
        this.ingredientComponent = ingredientComponent;
×
UNCOV
33
        this.handler = handler;
×
UNCOV
34
        this.craftingNetwork = craftingNetwork;
×
UNCOV
35
    }
×
36

37
    /**
38
     * This adds the given instance to the waiting crafting jobs directly, without having to go through the network storage and its observers.
39
     * Like the method above, this will first attempt to complete running jobs that are awaiting results.
40
     * Then, it will try to give the instance to the dependent jobs based on their missing ingredients.
41
     * @param instanceWrapper The instance to add.
42
     * @param channel The channel.
43
     * @param simulate Simulate mode.
44
     * @return The remaining instance that could not be given to any jobs that had missing ingredients.
45
     */
46
    public IngredientInstanceWrapper<T, M> addIngredient(IngredientInstanceWrapper<T, M> instanceWrapper, int channel, boolean simulate) {
47
        IIngredientMatcher<T, M> matcher = ingredientComponent.getMatcher();
×
UNCOV
48
        long instanceAmount = matcher.getQuantity(instanceWrapper.getInstance());
×
49

UNCOV
50
        Int2ObjectMap<List<Map<IngredientComponent<?, ?>, List<IPrototypedIngredient<?, ?>>>>> processingJobs = handler.getProcessingCraftingJobsPendingIngredients();
×
UNCOV
51
        ObjectIterator<Int2ObjectMap.Entry<List<Map<IngredientComponent<?, ?>, List<IPrototypedIngredient<?, ?>>>>>> jobsEntryIt = processingJobs.int2ObjectEntrySet().iterator();
×
UNCOV
52
        while (jobsEntryIt.hasNext() && instanceAmount > 0) {
×
UNCOV
53
            Int2ObjectMap.Entry<List<Map<IngredientComponent<?, ?>, List<IPrototypedIngredient<?, ?>>>>> jobsEntry = jobsEntryIt.next();
×
UNCOV
54
            int craftingJobId = jobsEntry.getIntKey();
×
55
            // Only check jobs that have a matching channel with the event
56
            CraftingJob craftingJob = handler.getAllCraftingJobs().get(jobsEntry.getIntKey());
×
UNCOV
57
            if (craftingJob != null
×
UNCOV
58
                    && (craftingJob.getChannel() == IPositionedAddonsNetwork.WILDCARD_CHANNEL || craftingJob.getChannel() == channel)) {
×
59
                Iterator<Map<IngredientComponent<?, ?>, List<IPrototypedIngredient<?, ?>>>> jobEntryIt = jobsEntry.getValue().iterator();
×
60
                while (jobEntryIt.hasNext() && instanceAmount > 0) {
×
61
                    Map<IngredientComponent<?, ?>, List<IPrototypedIngredient<?, ?>>> jobEntry = jobEntryIt.next();
×
62
                    List<IPrototypedIngredient<?, ?>> pendingIngredientsUnsafe = jobEntry.get(ingredientComponent);
×
UNCOV
63
                    if (pendingIngredientsUnsafe != null) {
×
64
                        // Remove pending ingredients based on the given instance.
65
                        List<IPrototypedIngredient<T, M>> pendingIngredients = (List<IPrototypedIngredient<T, M>>) (Object) pendingIngredientsUnsafe;
×
66

67
                        // Iterate over all pending ingredients for this ingredient component
68
                        ListIterator<IPrototypedIngredient<T, M>> it = pendingIngredients.listIterator();
×
UNCOV
69
                        while (it.hasNext() && instanceAmount > 0) {
×
70
                            IPrototypedIngredient<T, M> prototypedIngredient = it.next();
×
71
                            final long initialQuantity = matcher.getQuantity(prototypedIngredient.getPrototype());
×
72
                            long remainingQuantity = initialQuantity;
×
73

74
                            // Check if the instance matches the job's expected outputs.
75
                            if (matcher.matches(instanceWrapper.getInstance(), prototypedIngredient.getPrototype(),
×
76
                                    prototypedIngredient.getCondition())) {
×
77
                                long extractedQuantityToAssign = Math.min(remainingQuantity, instanceAmount);
×
UNCOV
78
                                remainingQuantity -= extractedQuantityToAssign;
×
79
                                T extracted = matcher.withQuantity(instanceWrapper.getInstance(), extractedQuantityToAssign);
×
80

81
                                // Move this ingredient from storage to dependent crafting jobs.
82
                                // We only consider jobs that have this instance as missing ingredient.
83
                                IntIterator dependentJobs = craftingJob.getDependentCraftingJobs().intIterator();
×
84
                                while (dependentJobs.hasNext()) {
×
85
                                    CraftingJob dependentJob = craftingNetwork.getCraftingJob(craftingJob.getChannel(), dependentJobs.nextInt());
×
86
                                    if (dependentJob != null) {
×
UNCOV
87
                                        long missingQuantity = dependentJob.getMissingIngredientQuantity(ingredientComponent, extracted);
×
UNCOV
88
                                        if (missingQuantity > 0) {
×
UNCOV
89
                                            long toExtractQuantity = Math.min(missingQuantity, extractedQuantityToAssign);
×
UNCOV
90
                                            T toExtract = matcher.withQuantity(extracted, toExtractQuantity);
×
UNCOV
91
                                            if (!simulate) {
×
UNCOV
92
                                                dependentJob.addToIngredientsStorageBuffer(ingredientComponent, toExtract);
×
93
                                            }
94
                                            instanceAmount -= toExtractQuantity;
×
95
                                            extractedQuantityToAssign -= toExtractQuantity;
×
96
                                            if (extractedQuantityToAssign == 0) {
×
97
                                                break;
×
98
                                            }
99
                                        }
100
                                    }
UNCOV
101
                                }
×
102
                            }
103

104
                            // Update the list if the prototype has changed.
UNCOV
105
                            if (!simulate) {
×
UNCOV
106
                                if (remainingQuantity <= 0) {
×
107
                                    it.remove();
×
108
                                } else if (initialQuantity != remainingQuantity) {
×
109
                                    it.set(new PrototypedIngredient<>(ingredientComponent,
×
UNCOV
110
                                            matcher.withQuantity(prototypedIngredient.getPrototype(), remainingQuantity),
×
111
                                            prototypedIngredient.getCondition()));
×
112
                                }
113
                            }
UNCOV
114
                        }
×
115

UNCOV
116
                        if (!simulate) {
×
UNCOV
117
                            onPendingIngredientsEmpty(jobsEntryIt, jobsEntry, jobEntryIt, jobEntry, pendingIngredients, craftingJobId);
×
118
                        }
119
                    }
120
                }
×
121
            }
122
        }
×
123

124
        return new IngredientInstanceWrapper<>(instanceWrapper.getComponent(), matcher.withQuantity(instanceWrapper.getInstance(), instanceAmount));
×
125
    }
126

127
    // If no prototypes for this component type for this crafting job for this job entry are pending.
128
    // If changes are made here, also change in method above!!! (only partially abstracted...)
129
    protected void onPendingIngredientsEmpty(ObjectIterator<Int2ObjectMap.Entry<List<Map<IngredientComponent<?, ?>, List<IPrototypedIngredient<?, ?>>>>>> jobsEntryIt,
130
                                             Int2ObjectMap.Entry<List<Map<IngredientComponent<?, ?>, List<IPrototypedIngredient<?, ?>>>>> jobsEntry,
131
                                             Iterator<Map<IngredientComponent<?, ?>, List<IPrototypedIngredient<?, ?>>>> jobEntryIt,
132
                                             Map<IngredientComponent<?, ?>, List<IPrototypedIngredient<?, ?>>> jobEntry,
133
                                             List<IPrototypedIngredient<T, M>> pendingIngredients,
134
                                             int craftingJobId) {
135
        if (pendingIngredients.isEmpty()) {
×
136
            // Remove observer (in next tick) when all pending ingredients are resolved
UNCOV
137
            handler.getObserversPendingDeletion().add(ingredientComponent);
×
138

139
            // Remove crafting job if needed.
140
            // No ingredients of this ingredient component type are pending
UNCOV
141
            jobEntry.remove(ingredientComponent);
×
142
            if (jobEntry.isEmpty()) {
×
143
                // No ingredients are pending for this non-blocking-mode-entry are pending
UNCOV
144
                handler.onCraftingJobEntryFinished(this.craftingNetwork, craftingJobId);
×
UNCOV
145
                jobEntryIt.remove();
×
146
            }
147
            if (jobsEntry.getValue().isEmpty()) {
×
148
                // No more entries for this crafting job are pending
149
                handler.onCraftingJobFinished(handler.getAllCraftingJobs().get(craftingJobId));
×
150
                handler.getProcessingCraftingJobsRaw().remove(craftingJobId);
×
151
                jobsEntryIt.remove();
×
152
            }
153
        }
UNCOV
154
    }
×
155

156
    @Override
157
    public T insert(int channel, @NotNull T ingredient, boolean simulate) {
UNCOV
158
        IngredientInstanceWrapper<T, M> instanceWrapper = addIngredient(new IngredientInstanceWrapper<>(ingredientComponent, ingredient), channel, simulate);
×
159
        return instanceWrapper.getInstance();
×
160
    }
161
}
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