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

CyclopsMC / IntegratedCrafting / #479011831

31 Dec 2025 10:58AM UTC coverage: 23.802% (-0.2%) from 24.029%
#479011831

push

github

rubensworks
Avoid need for running synchronous observers

Instead, we now run crafting results through crafting job completion
logic, before flushing it to the network and relying on observer logic.

This used to cause performance issues.

Related to #112

0 of 73 new or added lines in 3 files covered. (0.0%)

71 existing lines in 4 files now uncovered.

755 of 3172 relevant lines covered (23.8%)

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
        super.addDrops(target, state, itemStacks, dropMainElement, saveState);
×
178
    }
×
179

180
    public static abstract class State<P extends PartTypeInterfaceCraftingBase<P, S>, S extends PartTypeInterfaceCraftingBase.State<P, S>>
181
            extends PartStateBase<P> implements ICraftingInterface, ICraftingResultsSink {
182

183
        private final CraftingJobHandler craftingJobHandler;
184
        private final List<IngredientInstanceWrapper<?, ?>> inventoryOutputBuffer;
185

186
        private int channelCrafting = 0;
×
187
        private PartTarget target = null;
×
188
        protected INetwork network = null;
×
189
        protected IPartNetwork partNetwork = null;
×
190
        protected ICraftingNetwork craftingNetwork = null;
×
191
        private int channel = -1;
×
192
        protected ValueDeseralizationContext valueDeseralizationContext;
193
        private boolean shouldAddToCraftingNetwork = false;
×
194
        protected Player lastPlayer;
195

196
        public State() {
×
197
            this.craftingJobHandler = new CraftingJobHandler(1, true,
×
198
                    CraftingProcessOverrides.REGISTRY.getCraftingProcessOverrides(), this);
×
199
            this.inventoryOutputBuffer = Lists.newArrayList();
×
200
        }
×
201

202
        @Override
203
        public void writeToNBT(CompoundTag tag) {
204
            super.writeToNBT(tag);
×
205

206
            ListTag instanceTags = new ListTag();
×
207
            for (IngredientInstanceWrapper instanceWrapper : inventoryOutputBuffer) {
×
208
                CompoundTag instanceTag = new CompoundTag();
×
209
                instanceTag.putString("component", IngredientComponent.REGISTRY.getKey(instanceWrapper.getComponent()).toString());
×
210
                instanceTag.put("instance", instanceWrapper.getComponent().getSerializer().serializeInstance(instanceWrapper.getInstance()));
×
211
                instanceTags.add(instanceTag);
×
212
            }
×
213
            tag.put("inventoryOutputBuffer", instanceTags);
×
214

215
            this.craftingJobHandler.writeToNBT(tag);
×
216
            tag.putInt("channelCrafting", channelCrafting);
×
217
        }
×
218

219
        @Override
220
        public void readFromNBT(ValueDeseralizationContext valueDeseralizationContext, CompoundTag tag) {
221
            super.readFromNBT(valueDeseralizationContext, tag);
×
222

223
            this.inventoryOutputBuffer.clear();
×
224
            for (Tag instanceTagRaw : tag.getList("inventoryOutputBuffer", Tag.TAG_COMPOUND)) {
×
225
                CompoundTag instanceTag = (CompoundTag) instanceTagRaw;
×
226
                String componentName = instanceTag.getString("component");
×
227
                IngredientComponent<?, ?> component = IngredientComponent.REGISTRY.getValue(new ResourceLocation(componentName));
×
228
                this.inventoryOutputBuffer.add(new IngredientInstanceWrapper(component,
×
229
                        component.getSerializer().deserializeInstance(instanceTag.get("instance"))));
×
230
            }
×
231

232
            this.craftingJobHandler.readFromNBT(tag);
×
233
            this.channelCrafting = tag.getInt("channelCrafting");
×
234
        }
×
235

236
        @Override
237
        protected int getDefaultUpdateInterval() {
238
            return GeneralConfig.minCraftingInterfaceUpdateFreq;
×
239
        }
240

241
        public void setChannelCrafting(int channelCrafting) {
242
            if (this.channelCrafting != channelCrafting) {
×
243
                // Unregister from the network
244
                if (craftingNetwork != null) {
×
245
                    craftingNetwork.removeCraftingInterface(this.channelCrafting, this);
×
246
                }
247

248
                // Update the channel
249
                this.channelCrafting = channelCrafting;
×
250

251
                // Re-register to the network
252
                if (craftingNetwork != null) {
×
253
                    craftingNetwork.addCraftingInterface(this.channelCrafting, this);
×
254
                }
255

256
                sendUpdate();
×
257
            }
258
        }
×
259

260
        public int getChannelCrafting() {
261
            return channelCrafting;
×
262
        }
263

264
        public void setTarget(PartTarget target) {
265
            this.target = target;
×
266
        }
×
267

268
        public PartTarget getTarget() {
269
            return target;
×
270
        }
271

272
        public void setNetworks(@Nullable INetwork network, @Nullable ICraftingNetwork craftingNetwork,
273
                                @Nullable IPartNetwork partNetwork, int channel,
274
                                @Nullable ValueDeseralizationContext valueDeseralizationContext,
275
                                boolean initialize) {
276
            this.network = network;
×
277
            this.craftingNetwork = craftingNetwork;
×
278
            this.partNetwork = partNetwork;
×
279
            this.channel = channel;
×
280
            this.valueDeseralizationContext = valueDeseralizationContext;
×
281
            reloadRecipes(initialize);
×
282
            if (network != null) {
×
283
                this.getCraftingJobHandler().reRegisterObservers(network);
×
284
            }
285
        }
×
286

287
        public void reloadRecipes(boolean initialize) {
288
            // Do nothing
289
        }
×
290

291
        public void setLastPlayer(Player lastPlayer) {
292
            this.lastPlayer = lastPlayer;
×
293
        }
×
294

295
        public ICraftingNetwork getCraftingNetwork() {
296
            return craftingNetwork;
×
297
        }
298

299
        @Override
300
        public int getChannel() {
301
            return channel;
×
302
        }
303

304
        @Override
305
        public boolean canScheduleCraftingJobs() {
306
            return getCraftingJobHandler().canScheduleCraftingJobs();
×
307
        }
308

309
        @Override
310
        public void scheduleCraftingJob(CraftingJob craftingJob) {
311
            getCraftingJobHandler().scheduleCraftingJob(craftingJob);
×
312
        }
×
313

314
        @Override
315
        public void fillCraftingJobBufferFromStorage(CraftingJob craftingJob, Function<IngredientComponent<?, ?>, IIngredientComponentStorage> storageGetter) {
316
            getCraftingJobHandler().fillCraftingJobBufferFromStorage(craftingJob, storageGetter);
×
317
        }
×
318

319
        @Override
320
        public int getCraftingJobsCount() {
321
            return this.craftingJobHandler.getAllCraftingJobs().size();
×
322
        }
323

324
        @Override
325
        public Iterator<CraftingJob> getCraftingJobs() {
326
            return this.craftingJobHandler.getAllCraftingJobs().values().iterator();
×
327
        }
328

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

338
        @Override
339
        public CraftingJobStatus getCraftingJobStatus(ICraftingNetwork network, int channel, int craftingJobId) {
340
            return craftingJobHandler.getCraftingJobStatus(network, channel, craftingJobId);
×
341
        }
342

343
        @Override
344
        public void cancelCraftingJob(int channel, int craftingJobId) {
345
            craftingJobHandler.markCraftingJobFinished(craftingJobId);
×
346
        }
×
347

348
        @Override
349
        public PrioritizedPartPos getPosition() {
350
            return PrioritizedPartPos.of(getTarget().getCenter(), getPriority());
×
351
        }
352

353
        public CraftingJobHandler getCraftingJobHandler() {
354
            return craftingJobHandler;
×
355
        }
356

357
        public boolean shouldAddToCraftingNetwork() {
358
            return shouldAddToCraftingNetwork;
×
359
        }
360

361
        public void setShouldAddToCraftingNetwork(boolean shouldAddToCraftingNetwork) {
362
            this.shouldAddToCraftingNetwork = shouldAddToCraftingNetwork;
×
363
        }
×
364

365
        public List<IngredientInstanceWrapper<?, ?>> getInventoryOutputBuffer() {
366
            return inventoryOutputBuffer;
×
367
        }
368

369
        @Override
370
        public <T> LazyOptional<T> getCapability(Capability<T> capability, INetwork network, IPartNetwork partNetwork, PartTarget target) {
371
            if (capability == CraftingInterfaceConfig.CAPABILITY) {
×
372
                return LazyOptional.of(() -> this).cast();
×
373
            }
374

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

386
            return super.getCapability(capability, network, partNetwork, target);
×
387
        }
388

389
        protected <C, T, M> C wrapStorageCapability(Capability<C> capability, IngredientComponent<T, M> ingredientComponent) {
390
            IIngredientComponentStorage<T, M> storage = CraftingHelpers.getNetworkStorage(this.network, this.channelCrafting,
×
391
                    ingredientComponent, false);
392

393
            // Don't allow extraction, only insertion
394
            storage = new IngredientComponentStorageSlottedInsertProxy<>(storage);
×
395

396
            return ingredientComponent.getStorageWrapperHandler(capability).wrapStorage(storage);
×
397
        }
398

399
        @Override
400
        public <T, M> void addResult(IngredientComponent<T, M> ingredientComponent, T instance) {
401
            this.getInventoryOutputBuffer().add(new IngredientInstanceWrapper<>(ingredientComponent, instance));
×
402

403
            // Try to flush buffer immediately
404
            if (this.network != null) {
×
405
                this.flushInventoryOutputBuffer(this.network);
×
406
            }
407
        }
×
408

409
        public void setIngredientComponentTargetSideOverride(IngredientComponent<?, ?> ingredientComponent, Direction side) {
410
            if (getTarget().getTarget().getSide() == side) {
×
411
                craftingJobHandler.setIngredientComponentTarget(ingredientComponent, null);
×
412
            } else {
413
                craftingJobHandler.setIngredientComponentTarget(ingredientComponent, side);
×
414
            }
415
            sendUpdate();
×
416
        }
×
417

418
        public Direction getIngredientComponentTargetSideOverride(IngredientComponent<?, ?> ingredientComponent) {
419
            Direction side = craftingJobHandler.getIngredientComponentTarget(ingredientComponent);
×
420
            if (side == null) {
×
421
                side = getTarget().getTarget().getSide();
×
422
            }
423
            return side;
×
424
        }
425

426
        public void flushInventoryOutputBuffer(INetwork network) {
427
            // Try to insert each ingredient in the buffer into the network.
428
            ListIterator<IngredientInstanceWrapper<?, ?>> outputBufferIt = this.getInventoryOutputBuffer().listIterator();
×
429
            while (outputBufferIt.hasNext()) {
×
NEW
430
                IngredientInstanceWrapper<?, ?> remainingInstance = outputBufferIt.next();
×
431

432
                // First try to give the ingredients to pending crafting jobs.
NEW
433
                remainingInstance = getCraftingJobHandler().beforeFlushIngredientToNetwork(remainingInstance, channelCrafting);
×
434

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

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