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

CyclopsMC / IntegratedCrafting / #479011779

08 Feb 2025 03:17PM UTC coverage: 25.34% (+0.05%) from 25.288%
#479011779

push

github

rubensworks
Bump mod version

726 of 2865 relevant lines covered (25.34%)

0.25 hits per line

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

66.67
/src/main/java/org/cyclops/integratedcrafting/api/crafting/CraftingJobDependencyGraph.java
1
package org.cyclops.integratedcrafting.api.crafting;
2

3
import com.google.common.collect.Maps;
4
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
5
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
6
import it.unimi.dsi.fastutil.ints.IntArrayList;
7
import it.unimi.dsi.fastutil.ints.IntCollection;
8
import it.unimi.dsi.fastutil.ints.IntIterator;
9
import net.minecraft.nbt.CompoundTag;
10
import net.minecraft.nbt.IntArrayTag;
11
import net.minecraft.nbt.ListTag;
12
import net.minecraft.nbt.Tag;
13
import org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition;
14
import org.cyclops.integratedcrafting.core.CraftingHelpers;
15

16
import javax.annotation.Nullable;
17
import java.util.Collection;
18
import java.util.Map;
19
import java.util.Objects;
20
import java.util.stream.Collectors;
21

22
/**
23
 * A CraftingJobDependencyGraph stores dependencies between crafting jobs based on their unique ID.
24
 * @author rubensworks
25
 */
26
public class CraftingJobDependencyGraph {
27

28
    private final Int2ObjectMap<CraftingJob> craftingJobs;
29
    private final Int2ObjectMap<IntCollection> dependencies;
30
    private final Int2ObjectMap<IntCollection> dependents;
31

32
    public CraftingJobDependencyGraph() {
33
        this(new Int2ObjectOpenHashMap<>(), new Int2ObjectOpenHashMap<>(), new Int2ObjectOpenHashMap<>());
1✔
34
    }
1✔
35

36
    public CraftingJobDependencyGraph(Int2ObjectMap<CraftingJob> craftingJobs,
37
                                      Int2ObjectMap<IntCollection> dependencies,
38
                                      Int2ObjectMap<IntCollection> dependents) {
1✔
39
        this.craftingJobs = craftingJobs;
1✔
40
        this.dependencies = dependencies;
1✔
41
        this.dependents = dependents;
1✔
42
    }
1✔
43

44
    public Collection<CraftingJob> getCraftingJobs() {
45
        return craftingJobs.values();
1✔
46
    }
47

48
    @Nullable
49
    public CraftingJob getCraftingJob(int id) {
50
        return craftingJobs.get(id);
1✔
51
    }
52

53
    public Collection<CraftingJob> getDependencies(CraftingJob craftingJob) {
54
        return dependencies.getOrDefault(craftingJob.getId(), new IntArrayList())
1✔
55
                .stream()
1✔
56
                .map(craftingJobs::get)
1✔
57
                .filter(Objects::nonNull)
1✔
58
                .collect(Collectors.toList());
1✔
59
    }
60

61
    public boolean hasDependencies(CraftingJob craftingJob) {
62
        return hasDependencies(craftingJob.getId());
×
63
    }
64

65
    public boolean hasDependencies(int craftingJobId) {
66
        IntCollection deps = dependencies.get(craftingJobId);
×
67
        if (deps != null) {
×
68
            IntIterator it = deps.iterator();
×
69
            while (it.hasNext()) {
×
70
                if (craftingJobs.get(it.next()) != null) {
×
71
                    return true;
×
72
                }
73
            }
74
        }
75
        return false;
×
76
    }
77

78
    public Collection<CraftingJob> getDependents(CraftingJob craftingJob) {
79
        return dependents.getOrDefault(craftingJob.getId(), new IntArrayList())
1✔
80
                .stream()
1✔
81
                .map(craftingJobs::get)
1✔
82
                .filter(Objects::nonNull)
1✔
83
                .collect(Collectors.toList());
1✔
84
    }
85

86
    public void addCraftingJobId(CraftingJob craftingJob) {
87
        craftingJobs.put(craftingJob.getId(), craftingJob);
1✔
88
    }
1✔
89

90
    public void removeCraftingJobId(CraftingJob craftingJob) {
91
        craftingJobs.remove(craftingJob.getId());
1✔
92
    }
1✔
93

94
    public void onCraftingJobFinished(CraftingJob craftingJob) {
95
        this.onCraftingJobFinished(craftingJob, false);
1✔
96
    }
1✔
97

98
    public void onCraftingJobFinished(CraftingJob craftingJob, boolean finishInvalidDependencies) {
99
        // Remove the job instance reference
100
        removeCraftingJobId(craftingJob);
1✔
101

102
        // Remove the dependents
103
        IntCollection removed = dependents.remove(craftingJob.getId());
1✔
104
        craftingJob.getDependentCraftingJobs().clear();
1✔
105

106
        // Remove all backwards dependency links
107
        if (removed != null) {
1✔
108
            IntIterator removedIt = removed.iterator();
1✔
109
            while (removedIt.hasNext()) {
1✔
110
                int dependent = removedIt.nextInt();
1✔
111
                IntCollection dependentDependencies = dependencies.get(dependent);
1✔
112
                dependentDependencies.rem(craftingJob.getId());
1✔
113
                CraftingJob dependentJob = craftingJobs.get(dependent);
1✔
114
                if (dependentJob != null) {
1✔
115
                    dependentJob.getDependencyCraftingJobs().rem(craftingJob.getId());
1✔
116
                    if (dependentDependencies.isEmpty()) {
1✔
117
                        dependencies.remove(dependent);
1✔
118
                        if (!dependents.containsKey(dependent)) {
1✔
119
                            craftingJobs.remove(dependent);
1✔
120
                        }
121
                    }
122
                }
123
            }
1✔
124
        }
125

126
        // Remove invalid dependencies that are not present in craftingJobs
127
        IntCollection removedDependencies = dependencies.remove(craftingJob.getId());
1✔
128
        if (removedDependencies != null) {
1✔
129
            IntIterator removedDependenciesIt = removedDependencies.iterator();
1✔
130
            while (removedDependenciesIt.hasNext()) {
1✔
131
                int dependency = removedDependenciesIt.nextInt();
1✔
132
                dependents.remove(dependency);
1✔
133
                if (finishInvalidDependencies) {
1✔
134
                    onCraftingJobFinished(craftingJobs.get(dependency), true);
1✔
135
                }
136
            }
1✔
137
        }
138
    }
1✔
139

140
    public void addDependency(CraftingJob craftingJob, CraftingJob dependency) {
141
        // Store id's of the edge
142
        addCraftingJobId(dependency);
1✔
143
        addDependency(craftingJob, dependency.getId());
1✔
144
    }
1✔
145

146
    public void addDependency(CraftingJob craftingJob, int dependency) {
147
        // Store id's of the edge
148
        addCraftingJobId(craftingJob);
1✔
149

150
        // Save dependency link
151
        IntCollection jobDependencies = dependencies.get(craftingJob.getId());
1✔
152
        if (jobDependencies == null) {
1✔
153
            jobDependencies = new IntArrayList();
1✔
154
            dependencies.put(craftingJob.getId(), jobDependencies);
1✔
155
        }
156
        jobDependencies.add(dependency);
1✔
157

158
        // Save reverse link
159
        IntCollection jobDependents = dependents.get(dependency);
1✔
160
        if (jobDependents == null) {
1✔
161
            jobDependents = new IntArrayList();
1✔
162
            dependents.put(dependency, jobDependents);
1✔
163
        }
164
        jobDependents.add(craftingJob.getId());
1✔
165
    }
1✔
166

167
    public void removeDependency(int craftingJob, int dependency) {
168
        // Remove dependency link
169
        IntCollection jobDependencies = dependencies.get(craftingJob);
1✔
170
        if (jobDependencies != null) {
1✔
171
            jobDependencies.rem(dependency);
1✔
172
            if (jobDependencies.isEmpty()) {
1✔
173
                dependencies.remove(craftingJob);
1✔
174
                if (!dependents.containsKey(craftingJob)) {
1✔
175
                    craftingJobs.remove(craftingJob);
1✔
176
                }
177
            }
178
        }
179

180
        // Remove reverse link
181
        IntCollection jobDependents = dependents.get(dependency);
1✔
182
        if (jobDependents != null) {
1✔
183
            jobDependents.rem(craftingJob);
1✔
184
            if (jobDependents.isEmpty()) {
1✔
185
                dependents.remove(dependency);
1✔
186
                if (!dependencies.containsKey(dependency)) {
1✔
187
                    craftingJobs.remove(dependency);
1✔
188
                }
189
            }
190
        }
191
    }
1✔
192

193
    public void importDependencies(CraftingJobDependencyGraph craftingJobsGraph) {
194
        for (CraftingJob craftingJob : craftingJobsGraph.getCraftingJobs()) {
1✔
195
            for (CraftingJob dependency : craftingJobsGraph.getDependencies(craftingJob)) {
1✔
196
                this.addDependency(craftingJob, dependency);
1✔
197
            }
1✔
198
        }
1✔
199
    }
1✔
200

201
    /**
202
     * Merge the two crafting jobs by adding the second job's amount into the first job's amount.
203
     * Furthermore, all dependencies of the second job will be merged into the dependencies of the first job as well.
204
     * @param target The job that should be merged into.
205
     * @param mergee The job that should be removed and merged into the target job.
206
     * @param markMergeeAsFinished If the mergee job should be marked as finished.
207
     */
208
    public void mergeCraftingJobs(CraftingJob target, CraftingJob mergee, boolean markMergeeAsFinished) {
209
        target.setAmount(target.getAmount() + mergee.getAmount());
1✔
210
        target.setIngredientsStorage(CraftingHelpers.mergeMixedIngredients(
1✔
211
                target.getIngredientsStorage(), mergee.getIngredientsStorage()));
1✔
212

213
        // If the existing job had dependencies, batch the dependencies as well
214
        // First, collect all dependency crafting jobs for the target job
215
        Map<IRecipeDefinition, CraftingJob> dependencyRecipeJobs = Maps.newHashMap();
1✔
216
        for (Integer dependencyCraftingJobId : target.getDependencyCraftingJobs()) {
1✔
217
            CraftingJob dependencyCraftingJob = this.getCraftingJob(dependencyCraftingJobId);
1✔
218
            dependencyRecipeJobs.put(dependencyCraftingJob.getRecipe(), dependencyCraftingJob);
1✔
219
        }
1✔
220
        // Next, try merging the mergee's jobs into the target dependency jobs
221
        // If no corresponding target dependency job exists, just add the dependency directly to target as dependency.
222
        for (Integer dependencyCraftingJobId : mergee.getDependencyCraftingJobs()) {
1✔
223
            CraftingJob dependencyCraftingJob = this.getCraftingJob(dependencyCraftingJobId);
1✔
224
            CraftingJob existingDependencyJob = dependencyRecipeJobs.get(dependencyCraftingJob.getRecipe());
1✔
225
            if (existingDependencyJob != null) {
1✔
226
                mergeCraftingJobs(existingDependencyJob, dependencyCraftingJob, false);
1✔
227
            } else {
228
                // Update dependency links
229
                mergee.removeDependency(dependencyCraftingJob);
1✔
230
                target.addDependency(dependencyCraftingJob);
1✔
231
                this.removeDependency(mergee.getId(), dependencyCraftingJobId);
1✔
232
                this.addDependency(target, dependencyCraftingJob);
1✔
233

234
                // Add to our available jobs
235
                dependencyRecipeJobs.put(dependencyCraftingJob.getRecipe(), dependencyCraftingJob);
1✔
236
            }
237
        }
1✔
238

239
        if (markMergeeAsFinished) {
1✔
240
            // Remove the crafting job from the graph
241
            this.onCraftingJobFinished(mergee, true);
1✔
242
        }
243
    }
1✔
244

245
    public static CompoundTag serialize(CraftingJobDependencyGraph graph) {
246
        CompoundTag tag = new CompoundTag();
×
247

248
        ListTag craftingJobs = new ListTag();
×
249
        for (CraftingJob craftingJob : graph.getCraftingJobs()) {
×
250
            craftingJobs.add(CraftingJob.serialize(craftingJob));
×
251
        }
×
252
        tag.put("craftingJobs", craftingJobs);
×
253

254
        CompoundTag dependencies = new CompoundTag();
×
255
        for (CraftingJob craftingJob : graph.getCraftingJobs()) {
×
256
            IntCollection intCollection = graph.dependencies.get(craftingJob.getId());
×
257
            if (intCollection != null) {
×
258
                dependencies.put(Integer.toString(craftingJob.getId()), new IntArrayTag(intCollection.toIntArray()));
×
259
            }
260
        }
×
261
        tag.put("dependencies", dependencies);
×
262

263
        CompoundTag dependents = new CompoundTag();
×
264
        for (CraftingJob craftingJob : graph.getCraftingJobs()) {
×
265
            IntCollection intCollection = graph.dependents.get(craftingJob.getId());
×
266
            if (intCollection != null) {
×
267
                dependents.put(Integer.toString(craftingJob.getId()), new IntArrayTag(intCollection.toIntArray()));
×
268
            }
269
        }
×
270
        tag.put("dependents", dependents);
×
271

272
        return tag;
×
273
    }
274

275
    public static CraftingJobDependencyGraph deserialize(CompoundTag tag) {
276
        if (!tag.contains("craftingJobs", Tag.TAG_LIST)) {
×
277
            throw new IllegalArgumentException("Could not find a craftingJobs entry in the given tag");
×
278
        }
279
        if (!tag.contains("dependencies", Tag.TAG_COMPOUND)) {
×
280
            throw new IllegalArgumentException("Could not find a dependencies entry in the given tag");
×
281
        }
282
        if (!tag.contains("dependents", Tag.TAG_COMPOUND)) {
×
283
            throw new IllegalArgumentException("Could not find a dependents entry in the given tag");
×
284
        }
285

286
        Int2ObjectMap<CraftingJob> craftingJobs = new Int2ObjectOpenHashMap<>();
×
287
        ListTag craftingJobsTag = tag.getList("craftingJobs", Tag.TAG_COMPOUND);
×
288
        for (int i = 0; i < craftingJobsTag.size(); i++) {
×
289
            CraftingJob craftingJob = CraftingJob.deserialize(craftingJobsTag.getCompound(i));
×
290
            craftingJobs.put(craftingJob.getId(), craftingJob);
×
291
        }
292

293
        Int2ObjectMap<IntCollection> dependencies = new Int2ObjectOpenHashMap<>();
×
294
        CompoundTag dependenciesTag = tag.getCompound("dependencies");
×
295
        for (String key : dependenciesTag.getAllKeys()) {
×
296
            int id = Integer.parseInt(key);
×
297
            int[] value = dependenciesTag.getIntArray(key);
×
298
            dependencies.put(id, new IntArrayList(value));
×
299
        }
×
300

301
        Int2ObjectMap<IntCollection> dependents = new Int2ObjectOpenHashMap<>();
×
302
        CompoundTag dependentsTag = tag.getCompound("dependencies");
×
303
        for (String key : dependentsTag.getAllKeys()) {
×
304
            int id = Integer.parseInt(key);
×
305
            int[] value = dependentsTag.getIntArray(key);
×
306
            dependents.put(id, new IntArrayList(value));
×
307
        }
×
308

309
        return new CraftingJobDependencyGraph(craftingJobs, dependencies, dependents);
×
310
    }
311

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