• 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/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;
×
UNCOV
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.
UNCOV
261
        if (bestCraftingInterface != null) {
×
262
            // Extract from storage
263
            bestCraftingInterface.fillCraftingJobBufferFromStorage(craftingJob, storageGetter);
×
264

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

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

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

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

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

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

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

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

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

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

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

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

UNCOV
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);
×
UNCOV
344
        if (craftingJobIndex == null) {
×
UNCOV
345
            craftingJobIndex = new CraftingJobIndexDefault();
×
UNCOV
346
            indexedCraftingJobs.put(channel, craftingJobIndex);
×
347
        }
348

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

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

360
        // Insert into crafting job to interface mapping
UNCOV
361
        allCraftingJobsToInterface.put(craftingJob.getId(), craftingInterface);
×
UNCOV
362
        craftingJobsToInterface.put(craftingJob.getId(), craftingInterface);
×
UNCOV
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
UNCOV
373
        allIndexedCraftingJobs.removeCraftingJob(craftingJob);
×
UNCOV
374
        if (craftingJobIndex != null) {
×
UNCOV
375
            craftingJobIndex.removeCraftingJob(craftingJob);
×
376
        }
377

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

385
    @Override
386
    public <T, M> Iterator<CraftingJob> getCraftingJobs(int channel, IngredientComponent<T, M> ingredientComponent,
387
                                                        T instance, M matchCondition) {
UNCOV
388
        if (channel == IPositionedAddonsNetwork.WILDCARD_CHANNEL) {
×
UNCOV
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) {
×
UNCOV
396
            channelIterator = craftingJobIndex.getCraftingJobs(ingredientComponent, instance, matchCondition);
×
397
        } else {
UNCOV
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) {
×
UNCOV
405
            wildcardChannelIterator = wildcardCraftingJobIndex.getCraftingJobs(ingredientComponent, instance, matchCondition);
×
406
        } else {
UNCOV
407
            wildcardChannelIterator = Iterators.forArray();
×
408
        }
409

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

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

419
    @Nullable
420
    @Override
421
    public ICraftingInterface getCraftingJobInterface(int channel, int craftingJobId) {
UNCOV
422
        if (channel == IPositionedAddonsNetwork.WILDCARD_CHANNEL) {
×
UNCOV
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) {
×
UNCOV
429
            ICraftingInterface craftingInterface = craftingJobsToInterface.get(craftingJobId);
×
UNCOV
430
            if (craftingInterface != null) {
×
UNCOV
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
×
UNCOV
437
                .get(IPositionedAddonsNetwork.WILDCARD_CHANNEL);
×
UNCOV
438
        if (craftingJobsToInterfaceWildcard != null) {
×
439
            return craftingJobsToInterfaceWildcard.get(craftingJobId);
×
440
        }
441

UNCOV
442
        return null;
×
443
    }
444

445
    @Override
446
    public long getRunningTicks(CraftingJob craftingJob) {
UNCOV
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()) {
×
UNCOV
453
            this.craftingInterfaces.remove(channel);
×
454
            this.recipeIndexes.remove(channel);
×
UNCOV
455
            this.recipeCraftingInterfaces.remove(channel);
×
456
        }
UNCOV
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

© 2026 Coveralls, Inc