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

CyclopsMC / IntegratedCrafting / #479011834

31 Dec 2025 02:03PM UTC coverage: 23.802% (-1.1%) from 24.876%
#479011834

push

github

rubensworks
Fix incorrect commoncapabilities version

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

3
import com.google.common.collect.Iterators;
4
import com.google.common.collect.Multimap;
5
import com.google.common.collect.MultimapBuilder;
6
import com.google.common.collect.Sets;
7
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
8
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
9
import it.unimi.dsi.fastutil.ints.IntListIterator;
10
import net.minecraft.world.level.Level;
11
import net.minecraftforge.server.ServerLifecycleHooks;
12
import org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition;
13
import org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;
14
import org.cyclops.commoncapabilities.api.ingredient.storage.IIngredientComponentStorage;
15
import org.cyclops.cyclopscore.datastructure.MultitransformIterator;
16
import org.cyclops.integratedcrafting.api.crafting.CraftingJob;
17
import org.cyclops.integratedcrafting.api.crafting.CraftingJobDependencyGraph;
18
import org.cyclops.integratedcrafting.api.crafting.ICraftingInterface;
19
import org.cyclops.integratedcrafting.api.crafting.UnavailableCraftingInterfacesException;
20
import org.cyclops.integratedcrafting.api.network.ICraftingNetwork;
21
import org.cyclops.integratedcrafting.api.recipe.ICraftingJobIndexModifiable;
22
import org.cyclops.integratedcrafting.api.recipe.IRecipeIndexModifiable;
23
import org.cyclops.integratedcrafting.core.CraftingHelpers;
24
import org.cyclops.integratedcrafting.core.CraftingJobIndexDefault;
25
import org.cyclops.integratedcrafting.core.RecipeIndexDefault;
26
import org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetwork;
27

28
import javax.annotation.Nullable;
29
import java.util.Collection;
30
import java.util.Iterator;
31
import java.util.Set;
32
import java.util.function.Function;
33
import java.util.stream.Collectors;
34

35
/**
36
 * A crafting handler network with multiple channels.
37
 * @author rubensworks
38
 */
39
public class CraftingNetwork implements ICraftingNetwork {
×
40

41
    private final Set<ICraftingInterface> allCraftingInterfaces = Sets.newHashSet();
×
42
    private final Int2ObjectMap<Set<ICraftingInterface>> craftingInterfaces = new Int2ObjectOpenHashMap<>();
×
43

44
    private final Multimap<IRecipeDefinition, ICraftingInterface> allRecipeCraftingInterfaces = newRecipeCraftingInterfacesMap();
×
45
    private final Int2ObjectMap<Multimap<IRecipeDefinition, ICraftingInterface>> recipeCraftingInterfaces = new Int2ObjectOpenHashMap<>();
×
46

47
    private final IRecipeIndexModifiable allRecipesIndex = new RecipeIndexDefault();
×
48
    private final Int2ObjectMap<IRecipeIndexModifiable> recipeIndexes = new Int2ObjectOpenHashMap<>();
×
49

50
    private final ICraftingJobIndexModifiable allIndexedCraftingJobs = new CraftingJobIndexDefault();
×
51
    private final Int2ObjectMap<ICraftingJobIndexModifiable> indexedCraftingJobs = new Int2ObjectOpenHashMap<>();
×
52

53
    private final Int2ObjectMap<ICraftingInterface> allCraftingJobsToInterface = new Int2ObjectOpenHashMap<>();
×
54
    private final Int2ObjectMap<Int2ObjectMap<ICraftingInterface>> channeledCraftingJobsToInterface = new Int2ObjectOpenHashMap<>();
×
55

56
    private final CraftingJobDependencyGraph craftingJobDependencyGraph = new CraftingJobDependencyGraph();
×
57

58
    protected static Multimap<IRecipeDefinition, ICraftingInterface> newRecipeCraftingInterfacesMap() {
59
        return MultimapBuilder.hashKeys().treeSetValues(ICraftingInterface.createComparator()).build();
×
60
    }
61

62
    @Override
63
    public int[] getChannels() {
64
        return craftingInterfaces.keySet().toIntArray();
×
65
    }
66

67
    @Override
68
    public Set<ICraftingInterface> getCraftingInterfaces(int channel) {
69
        if (channel == IPositionedAddonsNetwork.WILDCARD_CHANNEL) {
×
70
            return allCraftingInterfaces;
×
71
        }
72
        Set<ICraftingInterface> craftingInterfaces = this.craftingInterfaces.get(channel);
×
73
        if (craftingInterfaces == null) {
×
74
            craftingInterfaces = Sets.newTreeSet(ICraftingInterface.createComparator());
×
75
            this.craftingInterfaces.put(channel, craftingInterfaces);
×
76
        }
77
        return craftingInterfaces;
×
78
    }
79

80
    @Override
81
    public Multimap<IRecipeDefinition, ICraftingInterface> getRecipeCraftingInterfaces(int channel) {
82
        if (channel == IPositionedAddonsNetwork.WILDCARD_CHANNEL) {
×
83
            return allRecipeCraftingInterfaces;
×
84
        }
85
        Multimap<IRecipeDefinition, ICraftingInterface> recipeCraftingInterfaces = this.recipeCraftingInterfaces.get(channel);
×
86
        if (recipeCraftingInterfaces == null) {
×
87
            recipeCraftingInterfaces = newRecipeCraftingInterfacesMap();
×
88
            this.recipeCraftingInterfaces.put(channel, recipeCraftingInterfaces);
×
89
        }
90
        return recipeCraftingInterfaces;
×
91
    }
92

93
    @Override
94
    public IRecipeIndexModifiable getRecipeIndex(int channel) {
95
        if (channel == IPositionedAddonsNetwork.WILDCARD_CHANNEL) {
×
96
            return allRecipesIndex;
×
97
        }
98
        IRecipeIndexModifiable recipeIndex = this.recipeIndexes.get(channel);
×
99
        if (recipeIndex == null) {
×
100
            recipeIndex = new RecipeIndexDefault();
×
101
            this.recipeIndexes.put(channel, recipeIndex);
×
102
        }
103
        return recipeIndex;
×
104
    }
105

106
    @Override
107
    public boolean addCraftingInterface(int channel, ICraftingInterface craftingInterface) {
108
        // Only process deeper indexes if the interface was not yet present
109
        if (getCraftingInterfaces(channel).add(craftingInterface)) {
×
110
            allCraftingInterfaces.add(craftingInterface);
×
111
            IRecipeIndexModifiable recipeIndex = getRecipeIndex(channel);
×
112
            Multimap<IRecipeDefinition, ICraftingInterface> recipeCraftingInterfaces = getRecipeCraftingInterfaces(channel);
×
113
            for (IRecipeDefinition recipe : craftingInterface.getRecipes()) {
×
114
                // Save the recipes in the index
115
                recipeIndex.addRecipe(recipe);
×
116
                allRecipesIndex.addRecipe(recipe);
×
117
                // Save a mapping from each of the recipes to this crafting interface
118
                recipeCraftingInterfaces.put(recipe, craftingInterface);
×
119
                allRecipeCraftingInterfaces.put(recipe, craftingInterface);
×
120
            }
×
121

122
            // Loop over the crafting jobs owned by the interface
123
            Iterator<CraftingJob> craftingJobsIt = craftingInterface.getCraftingJobs();
×
124
            while (craftingJobsIt.hasNext()) {
×
125
                CraftingJob craftingJob = craftingJobsIt.next();
×
126

127
                // Store mapping between interface and job in the network
128
                addCraftingJob(craftingJob.getChannel(), craftingJob, craftingInterface);
×
129

130
                // Add the crafting job dependencies
131
                craftingJobDependencyGraph.addCraftingJobId(craftingJob);
×
132
                IntListIterator dependencyIt = craftingJob.getDependencyCraftingJobs().iterator();
×
133
                while (dependencyIt.hasNext()) {
×
134
                    craftingJobDependencyGraph.addDependency(craftingJob, dependencyIt.nextInt());
×
135
                }
136
            }
×
137

138
            return true;
×
139
        }
140
        return false;
×
141
    }
142

143
    @Override
144
    public boolean removeCraftingInterface(int channel, ICraftingInterface craftingInterface) {
145
        // Only process deeper indexes if the interface was present
146
        if (getCraftingInterfaces(channel).remove(craftingInterface)) {
×
147
            allCraftingInterfaces.remove(craftingInterface);
×
148
            IRecipeIndexModifiable recipeIndex = getRecipeIndex(channel);
×
149
            Multimap<IRecipeDefinition, ICraftingInterface> recipeCraftingInterfaces = getRecipeCraftingInterfaces(channel);
×
150
            for (IRecipeDefinition recipe : craftingInterface.getRecipes()) {
×
151
                // Remove the mappings from each of the recipes to this crafting interface
152
                recipeCraftingInterfaces.remove(recipe, craftingInterface);
×
153
                allRecipeCraftingInterfaces.remove(recipe, craftingInterface);
×
154

155
                // If the mapping from this recipe to crafting interfaces is empty, remove the recipe from the index
156
                if (!recipeCraftingInterfaces.containsKey(recipe)) {
×
157
                    recipeIndex.removeRecipe(recipe);
×
158
                }
159
                if (!allRecipeCraftingInterfaces.containsKey(recipe)) {
×
160
                    allRecipesIndex.removeRecipe(recipe);
×
161
                }
162
            }
×
163

164
            // Try cleaning up the channel
165
            cleanupChannelIfEmpty(channel);
×
166

167
            // Loop over the crafting jobs owned by the interface
168
            Iterator<CraftingJob> craftingJobsIt = craftingInterface.getCraftingJobs();
×
169
            while (craftingJobsIt.hasNext()) {
×
170
                CraftingJob craftingJob = craftingJobsIt.next();
×
171

172
                // Remove the mapping between interface and job in the network
173
                removeCraftingJob(channel, craftingJob);
×
174

175
                // Remove the crafting job dependencies
176
                craftingJobDependencyGraph.removeCraftingJobId(craftingJob);
×
177
            }
×
178

179
            return true;
×
180
        }
181
        return false;
×
182
    }
183

184
    @Override
185
    public boolean addCraftingInterfaceRecipe(int channel, ICraftingInterface craftingInterface, IRecipeDefinition recipe) {
186
        IRecipeIndexModifiable recipeIndex = getRecipeIndex(channel);
×
187
        Multimap<IRecipeDefinition, ICraftingInterface> recipeCraftingInterfaces = getRecipeCraftingInterfaces(channel);
×
188

189
        // Save the recipes in the index
190
        recipeIndex.addRecipe(recipe);
×
191
        allRecipesIndex.addRecipe(recipe);
×
192
        // Save a mapping from each of the recipes to this crafting interface
193
        boolean changed = recipeCraftingInterfaces.put(recipe, craftingInterface);
×
194
        allRecipeCraftingInterfaces.put(recipe, craftingInterface);
×
195

196
        return changed;
×
197
    }
198

199
    @Override
200
    public boolean removeCraftingInterfaceRecipe(int channel, ICraftingInterface craftingInterface, IRecipeDefinition recipe) {
201
        IRecipeIndexModifiable recipeIndex = getRecipeIndex(channel);
×
202
        Multimap<IRecipeDefinition, ICraftingInterface> recipeCraftingInterfaces = getRecipeCraftingInterfaces(channel);
×
203

204
        // Remove the mappings from each of the recipes to this crafting interface
205
        boolean changed = recipeCraftingInterfaces.remove(recipe, craftingInterface);
×
206
        allRecipeCraftingInterfaces.remove(recipe, craftingInterface);
×
207

208
        // If the mapping from this recipe to crafting interfaces is empty, remove the recipe from the index
209
        if (!recipeCraftingInterfaces.containsKey(recipe)) {
×
210
            recipeIndex.removeRecipe(recipe);
×
211
        }
212
        if (!allRecipeCraftingInterfaces.containsKey(recipe)) {
×
213
            allRecipesIndex.removeRecipe(recipe);
×
214
        }
215

216
        return changed;
×
217
    }
218

219
    @Override
220
    public void scheduleCraftingJob(CraftingJob craftingJob, boolean allowDistribution, Function<IngredientComponent<?, ?>, IIngredientComponentStorage> storageGetter)
221
            throws UnavailableCraftingInterfacesException {
222
        Multimap<IRecipeDefinition, ICraftingInterface> recipeInterfaces = getRecipeCraftingInterfaces(craftingJob.getChannel());
×
223
        Collection<ICraftingInterface> craftingInterfaces = recipeInterfaces.get(craftingJob.getRecipe())
×
224
                .stream()
×
225
                .filter(ICraftingInterface::canScheduleCraftingJobs)
×
226
                .collect(Collectors.toList());
×
227

228
        if (craftingInterfaces.size() == 0) {
×
229
            throw new UnavailableCraftingInterfacesException(craftingJob);
×
230
        }
231

232
        // If our crafting job amount is larger than 1,
233
        // and we have multiple crafting interfaces available,
234
        // split our crafting job so we can distribute
235
        if (allowDistribution && craftingInterfaces.size() > 1 && craftingJob.getAmount() > 1) {
×
236
            Collection<CraftingJob> splitCraftingJobs = CraftingHelpers.splitCraftingJobs(craftingJob,
×
237
                    craftingInterfaces.size(), getCraftingJobDependencyGraph(),
×
238
                    CraftingHelpers.getGlobalCraftingJobIdentifier());
×
239
            for (CraftingJob splitCraftingJob : splitCraftingJobs) {
×
240
                scheduleCraftingJob(splitCraftingJob, false, storageGetter);
×
241
            }
×
242
            return;
×
243
        }
244

245
        // Find the crafting interface that has the least number of crafting jobs.
246
        // This will achieve parallelized jobs.
247
        int bestCraftingInterfaceJobCount = 0;
×
248
        ICraftingInterface bestCraftingInterface = null;
×
249
        for (ICraftingInterface craftingInterface : craftingInterfaces) {
×
250
            int jobCount = craftingInterface.getCraftingJobsCount();
×
251
            if (bestCraftingInterface == null || jobCount < bestCraftingInterfaceJobCount) {
×
252
                bestCraftingInterfaceJobCount = jobCount;
×
253
                bestCraftingInterface = craftingInterface;
×
254
                if (bestCraftingInterfaceJobCount == 0) {
×
255
                    break; // Break early, as we won't find a better interface than one with zero jobs
×
256
                }
257
            }
258
        }
×
259

260
        // This should not be null, but let's check to be sure.
261
        if (bestCraftingInterface != null) {
×
262
            // Extract from storage
263
            bestCraftingInterface.fillCraftingJobBufferFromStorage(craftingJob, storageGetter);
×
264

265
            // Schedule the job in the interface
266
            bestCraftingInterface.scheduleCraftingJob(craftingJob);
×
267
            addCraftingJob(craftingJob.getChannel(), craftingJob, bestCraftingInterface);
×
268

269
            // Store the starting tick in the job
270
            craftingJob.setStartTick(getCurrentTick());
×
271
        }
272
    }
×
273

274
    protected long getCurrentTick() {
275
        return ServerLifecycleHooks.getCurrentServer().getLevel(Level.OVERWORLD).getGameTime();
×
276
    }
277

278
    @Override
279
    public void onCraftingJobFinished(CraftingJob craftingJob) {
280
        removeCraftingJob(craftingJob.getChannel(), craftingJob);
×
281
        getCraftingJobDependencyGraph().onCraftingJobFinished(craftingJob);
×
282
    }
×
283

284
    @Override
285
    public boolean cancelCraftingJob(int channel, int craftingJobId) {
286
        CraftingJob craftingJob = getCraftingJob(channel, craftingJobId);
×
287
        if (craftingJob != null) {
×
288
            cancelCraftingJob(craftingJob);
×
289
            return true;
×
290
        }
291
        return false;
×
292
    }
293

294
    protected void cancelCraftingJob(CraftingJob craftingJob) {
295
        // First cancel all dependencies
296
        for (CraftingJob dependency : getCraftingJobDependencyGraph().getDependencies(craftingJob)) {
×
297
            cancelCraftingJob(dependency);
×
298
        }
×
299

300
        // Remove all job references from the interface
301
        ICraftingInterface craftingInterface = getCraftingJobInterface(craftingJob.getChannel(), craftingJob.getId());
×
302
        if (craftingInterface != null) {
×
303
            craftingInterface.cancelCraftingJob(craftingJob.getChannel(), craftingJob.getId());
×
304
        }
305

306
        // Remove all job references from the network
307
        onCraftingJobFinished(craftingJob);
×
308
    }
×
309

310
    @Override
311
    public Iterator<CraftingJob> getCraftingJobs(int channel) {
312
        return new MultitransformIterator<>(getCraftingInterfaces(channel).iterator(),
×
313
                ICraftingInterface::getCraftingJobs);
314
    }
315

316
    @Nullable
317
    @Override
318
    public CraftingJob getCraftingJob(int channel, int craftingJobId) {
319
        if (channel == IPositionedAddonsNetwork.WILDCARD_CHANNEL) {
×
320
            return allIndexedCraftingJobs.getCraftingJob(craftingJobId);
×
321
        }
322

323
        // Check for the channel directly
324
        ICraftingJobIndexModifiable index = indexedCraftingJobs.get(channel);
×
325
        if (index != null) {
×
326
            CraftingJob craftingJob = index.getCraftingJob(craftingJobId);
×
327
            if (craftingJob != null) {
×
328
                return craftingJob;
×
329
            }
330
        }
331

332
        // Check for the case the crafting job was explicitly started on the wildcard channel
333
        ICraftingJobIndexModifiable wildcardIndex = indexedCraftingJobs.get(IPositionedAddonsNetwork.WILDCARD_CHANNEL);
×
334
        if (wildcardIndex != null) {
×
335
            return wildcardIndex.getCraftingJob(craftingJobId);
×
336
        }
337

338
        return null;
×
339
    }
340

341
    protected void addCraftingJob(int channel, CraftingJob craftingJob, ICraftingInterface craftingInterface) {
342
        // Prepare crafting job index
343
        ICraftingJobIndexModifiable craftingJobIndex = indexedCraftingJobs.get(channel);
×
344
        if (craftingJobIndex == null) {
×
345
            craftingJobIndex = new CraftingJobIndexDefault();
×
346
            indexedCraftingJobs.put(channel, craftingJobIndex);
×
347
        }
348

349
        // Prepare crafting job to interface mapping
350
        Int2ObjectMap<ICraftingInterface> craftingJobsToInterface = this.channeledCraftingJobsToInterface.get(channel);
×
351
        if (craftingJobsToInterface == null) {
×
352
            craftingJobsToInterface = new Int2ObjectOpenHashMap<>();
×
353
            this.channeledCraftingJobsToInterface.put(channel, craftingJobsToInterface);
×
354
        }
355

356
        // Insert into crafting job index
357
        allIndexedCraftingJobs.addCraftingJob(craftingJob);
×
358
        craftingJobIndex.addCraftingJob(craftingJob);
×
359

360
        // Insert into crafting job to interface mapping
361
        allCraftingJobsToInterface.put(craftingJob.getId(), craftingInterface);
×
362
        craftingJobsToInterface.put(craftingJob.getId(), craftingInterface);
×
363
    }
×
364

365
    protected void removeCraftingJob(int channel, CraftingJob craftingJob) {
366
        // Prepare crafting job index
367
        ICraftingJobIndexModifiable craftingJobIndex = indexedCraftingJobs.get(channel);
×
368

369
        // Prepare crafting job to interface mapping
370
        Int2ObjectMap<ICraftingInterface> craftingJobsToInterface = this.channeledCraftingJobsToInterface.get(channel);
×
371

372
        // Remove from crafting job index
373
        allIndexedCraftingJobs.removeCraftingJob(craftingJob);
×
374
        if (craftingJobIndex != null) {
×
375
            craftingJobIndex.removeCraftingJob(craftingJob);
×
376
        }
377

378
        // Remove from crafting job to interface mapping
379
        allCraftingJobsToInterface.remove(craftingJob.getId());
×
380
        if (craftingJobsToInterface != null) {
×
381
            craftingJobsToInterface.remove(craftingJob.getId());
×
382
        }
383
    }
×
384

385
    @Override
386
    public <T, M> Iterator<CraftingJob> getCraftingJobs(int channel, IngredientComponent<T, M> ingredientComponent,
387
                                                        T instance, M matchCondition) {
388
        if (channel == IPositionedAddonsNetwork.WILDCARD_CHANNEL) {
×
389
            return allIndexedCraftingJobs.getCraftingJobs(ingredientComponent, instance, matchCondition);
×
390
        }
391

392
        // Check for the specific channel
393
        ICraftingJobIndexModifiable craftingJobIndex = indexedCraftingJobs.get(channel);
×
394
        Iterator<CraftingJob> channelIterator;
395
        if (craftingJobIndex != null) {
×
396
            channelIterator = craftingJobIndex.getCraftingJobs(ingredientComponent, instance, matchCondition);
×
397
        } else {
398
            channelIterator = Iterators.forArray();
×
399
        }
400

401
        // Check for the case the crafting job was explicitly started on the wildcard channel
402
        ICraftingJobIndexModifiable wildcardCraftingJobIndex = indexedCraftingJobs.get(IPositionedAddonsNetwork.WILDCARD_CHANNEL);
×
403
        Iterator<CraftingJob> wildcardChannelIterator;
404
        if (wildcardCraftingJobIndex != null) {
×
405
            wildcardChannelIterator = wildcardCraftingJobIndex.getCraftingJobs(ingredientComponent, instance, matchCondition);
×
406
        } else {
407
            wildcardChannelIterator = Iterators.forArray();
×
408
        }
409

410
        // Concat both iterators
411
        return Iterators.concat(channelIterator, wildcardChannelIterator);
×
412
    }
413

414
    @Override
415
    public CraftingJobDependencyGraph getCraftingJobDependencyGraph() {
416
        return craftingJobDependencyGraph;
×
417
    }
418

419
    @Nullable
420
    @Override
421
    public ICraftingInterface getCraftingJobInterface(int channel, int craftingJobId) {
422
        if (channel == IPositionedAddonsNetwork.WILDCARD_CHANNEL) {
×
423
            return allCraftingJobsToInterface.get(craftingJobId);
×
424
        }
425

426
        // Check for the channel directly
427
        Int2ObjectMap<ICraftingInterface> craftingJobsToInterface = this.channeledCraftingJobsToInterface.get(channel);
×
428
        if (craftingJobsToInterface != null) {
×
429
            ICraftingInterface craftingInterface = craftingJobsToInterface.get(craftingJobId);
×
430
            if (craftingInterface != null) {
×
431
                return craftingInterface;
×
432
            }
433
        }
434

435
        // In case the crafting job was explicitly started on the wildcard channel
436
        Int2ObjectMap<ICraftingInterface> craftingJobsToInterfaceWildcard = this.channeledCraftingJobsToInterface
×
437
                .get(IPositionedAddonsNetwork.WILDCARD_CHANNEL);
×
438
        if (craftingJobsToInterfaceWildcard != null) {
×
439
            return craftingJobsToInterfaceWildcard.get(craftingJobId);
×
440
        }
441

442
        return null;
×
443
    }
444

445
    @Override
446
    public long getRunningTicks(CraftingJob craftingJob) {
447
        return getCurrentTick() - craftingJob.getStartTick();
×
448
    }
449

450
    protected void cleanupChannelIfEmpty(int channel) {
451
        Set<ICraftingInterface> craftingInterfaces = this.craftingInterfaces.get(channel);
×
452
        if (craftingInterfaces != null && craftingInterfaces.isEmpty()) {
×
453
            this.craftingInterfaces.remove(channel);
×
454
            this.recipeIndexes.remove(channel);
×
455
            this.recipeCraftingInterfaces.remove(channel);
×
456
        }
457
    }
×
458
}
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

© 2025 Coveralls, Inc