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

CyclopsMC / IntegratedDynamics / 20655861540

02 Jan 2026 10:23AM UTC coverage: 53.088% (+0.03%) from 53.056%
20655861540

push

github

rubensworks
Merge remote-tracking branch 'origin/master-1.21-lts' into master-1.21

2864 of 8758 branches covered (32.7%)

Branch coverage included in aggregate %.

17440 of 29488 relevant lines covered (59.14%)

3.07 hits per line

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

43.96
/src/main/java/org/cyclops/integrateddynamics/core/network/IngredientChannelAdapter.java
1
package org.cyclops.integrateddynamics.core.network;
2

3
import com.google.common.collect.Lists;
4
import net.neoforged.neoforge.transfer.transaction.SnapshotJournal;
5
import net.neoforged.neoforge.transfer.transaction.Transaction;
6
import net.neoforged.neoforge.transfer.transaction.TransactionContext;
7
import org.apache.commons.lang3.tuple.Pair;
8
import org.cyclops.commoncapabilities.api.ingredient.IIngredientMatcher;
9
import org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;
10
import org.cyclops.commoncapabilities.api.ingredient.storage.IIngredientComponentStorage;
11
import org.cyclops.cyclopscore.datastructure.Wrapper;
12
import org.cyclops.cyclopscore.ingredient.collection.IIngredientMapMutable;
13
import org.cyclops.cyclopscore.ingredient.collection.IngredientHashMap;
14
import org.cyclops.integrateddynamics.api.network.INetworkIngredientsChannel;
15
import org.cyclops.integrateddynamics.api.network.IPartPosIteratorHandler;
16
import org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetworkIngredients;
17
import org.cyclops.integrateddynamics.api.network.PositionedAddonsNetworkIngredientsFilter;
18
import org.cyclops.integrateddynamics.api.part.PartPos;
19

20
import javax.annotation.Nonnull;
21
import java.util.Iterator;
22
import java.util.List;
23
import java.util.Map;
24
import java.util.function.Supplier;
25

26
/**
27
 * An abstract {@link IIngredientComponentStorage} that wraps over a {@link IPositionedAddonsNetworkIngredients}.
28
 *
29
 * @param <T> The instance type.
30
 * @param <M> The matching condition parameter.
31
 */
32
public abstract class IngredientChannelAdapter<T, M> implements INetworkIngredientsChannel<T, M> {
33

34
    private final IPositionedAddonsNetworkIngredients<T, M> network;
35
    private final int channel;
36

37
    private boolean limitsEnabled;
38

39
    public IngredientChannelAdapter(PositionedAddonsNetworkIngredients<T, M> network, int channel) {
2✔
40
        this.network = network;
3✔
41
        this.channel = channel;
3✔
42

43
        this.limitsEnabled = true;
3✔
44
    }
1✔
45

46
    public void enableLimits() {
47
        this.limitsEnabled = true;
×
48
    }
×
49

50
    public void disableLimits() {
51
        this.limitsEnabled = false;
×
52
    }
×
53

54
    public IPositionedAddonsNetworkIngredients<T, M> getNetwork() {
55
        return network;
3✔
56
    }
57

58
    public int getChannel() {
59
        return channel;
3✔
60
    }
61

62
    @Override
63
    public IngredientComponent<T, M> getComponent() {
64
        return network.getComponent();
4✔
65
    }
66

67
    protected abstract Iterator<PartPos> getNonFullPositions();
68
    protected abstract Iterator<PartPos> getAllPositions();
69
    protected abstract Iterator<PartPos> getNonEmptyPositions();
70
    protected abstract Iterator<PartPos> getMatchingPositions(@Nonnull T prototype, M matchFlags);
71

72
    @Override
73
    public Iterable<PartPos> findNonFullPositions() {
74
        return () -> getPartPosIteratorData(this::getNonFullPositions, channel).getRight();
×
75
    }
76

77
    @Override
78
    public Iterable<PartPos> findAllPositions() {
79
        return () -> getPartPosIteratorData(this::getAllPositions, channel).getRight();
×
80
    }
81

82
    @Override
83
    public Iterable<PartPos> findNonEmptyPositions() {
84
        return () -> getPartPosIteratorData(this::getNonEmptyPositions, channel).getRight();
×
85
    }
86

87
    @Override
88
    public Iterable<PartPos> findMatchingPositions(@Nonnull T prototype, M matchFlags) {
89
        return () -> getPartPosIteratorData(() -> this.getMatchingPositions(prototype, matchFlags), channel).getRight();
×
90
    }
91

92
    @Override
93
    public long getMaxQuantity() {
94
        long sum = 0;
2✔
95
        Iterator<PartPos> it = getAllPositions();
3✔
96
        while (it.hasNext() && sum < Long.MAX_VALUE) {
7!
97
            PartPos pos = it.next();
4✔
98
            // Skip if the position is not loaded
99
            if (!pos.getPos().isLoaded() || network.isPositionDisabled(pos)) {
9!
100
                continue;
×
101
            }
102
            this.network.disablePosition(pos);
4✔
103
            try {
104
                sum = Math.addExact(sum, this.network.getPositionedStorage(pos).getMaxQuantity());
8✔
105
            } catch (ArithmeticException e) {
×
106
                sum = Long.MAX_VALUE; // If we had an overflow, we're already at max quantity.
×
107
            }
1✔
108
            this.network.enablePosition(pos);
4✔
109
        }
1✔
110

111
        // Schedule an observation, as since this method is called, there may be a need for changes later on.
112
        scheduleObservation();
2✔
113

114
        return sum;
2✔
115
    }
116

117
    protected Pair<IPartPosIteratorHandler, Iterator<PartPos>> getPartPosIteratorData(Supplier<Iterator<PartPos>> iteratorSupplier, int channel) {
118
        IPartPosIteratorHandler handler = network.getPartPosIteratorHandler();
4✔
119
        if (handler == null) {
2✔
120
            handler = PartPosIteratorHandlerDummy.INSTANCE;
3✔
121
        } else {
122
            handler = handler.clone();
3✔
123
        }
124
        return Pair.of(handler, handler.handleIterator(iteratorSupplier, channel));
7✔
125
    }
126

127
    protected void savePartPosIteratorHandler(IPartPosIteratorHandler partPosIteratorHandler, TransactionContext transaction) {
128
        new PartPosIteratorHandledJournal(partPosIteratorHandler.clone()).updateSnapshots(transaction);
8✔
129
        network.setPartPosIteratorHandler(partPosIteratorHandler);
4✔
130
    }
1✔
131

132
    protected void markStoragePositionChanged(int channel, PartPos targetPos) {
133
        this.network.scheduleObservationForced(channel, targetPos);
5✔
134
    }
1✔
135

136
    @Override
137
    public T insert(@Nonnull T ingredient, TransactionContext transaction) {
138
        // First run the ingredient instance through the pre-consumers.
139
        for (IIngredientChannelInsertPreConsumer<T> insertPreConsumer : network.getInsertPreConsumers()) {
8!
140
            ingredient = insertPreConsumer.insert(this.channel, ingredient, transaction);
×
141
        }
×
142

143
        IIngredientMatcher<T, M> matcher = getComponent().getMatcher();
4✔
144

145
        // Quickly return if the to-be-inserted ingredient was already empty
146
        if (matcher.isEmpty(ingredient)) {
4!
147
            return ingredient;
×
148
        }
149

150
        // Limit rate
151
        long skippedQuantity = 0;
2✔
152
        T ingredientOriginal = ingredient;
2✔
153
        if (this.limitsEnabled) {
3!
154
            long limit = network.getRateLimit();
4✔
155
            long currentQuantity = matcher.getQuantity(ingredient);
4✔
156
            if (currentQuantity > limit) {
4!
157
                ingredient = matcher.withQuantity(ingredient, limit);
×
158
                skippedQuantity = currentQuantity - limit;
×
159
            }
160
        }
161

162
        // Try inserting the ingredient at all positions that are not full,
163
        // until the ingredient becomes completely empty.
164
        Pair<IPartPosIteratorHandler, Iterator<PartPos>> partPosIteratorData = getPartPosIteratorData(this::getNonFullPositions, channel);
7✔
165
        Iterator<PartPos> it = partPosIteratorData.getRight();
4✔
166
        while (it.hasNext()) {
3!
167
            PartPos pos = it.next();
4✔
168

169
            // Skip if the position is not loaded or disabled
170
            if (!pos.getPos().isLoaded() || network.isPositionDisabled(pos)) {
9!
171
                continue;
×
172
            }
173

174
            // Skip if a filter was set that doesn't match the ingredient
175
            PositionedAddonsNetworkIngredientsFilter<T> filter = this.network.getPositionedStorageFilter(pos);
5✔
176
            if (filter != null && !filter.testInsertion(ingredient)) {
2!
177
                continue;
×
178
            }
179

180
            this.network.disablePosition(pos);
4✔
181
            long quantityBefore = matcher.getQuantity(ingredient);
4✔
182
            ingredient = this.network.getPositionedStorage(pos).insert(ingredient, transaction);
8✔
183
            long quantityAfter = matcher.getQuantity(ingredient);
4✔
184
            this.network.enablePosition(pos);
4✔
185
            if (quantityBefore != quantityAfter) {
4!
186
                new MarkStoragePositionChangedOnRootCommitJournal(pos).updateSnapshots(transaction);
7✔
187
            }
188
            if (matcher.isEmpty(ingredient)) {
4!
189
                break;
1✔
190
            }
191
        }
×
192

193
        // Re-add skipped quantity to response if applicable
194
        if (skippedQuantity > 0) {
4!
195
            // Modify ingredientOriginal instead of ingredient, because ingredient may be EMPTY.
196
            ingredient = matcher.withQuantity(ingredientOriginal, skippedQuantity + matcher.getQuantity(ingredient));
×
197
        }
198

199
        savePartPosIteratorHandler(partPosIteratorData.getLeft(), transaction);
6✔
200

201
        return ingredient;
2✔
202
    }
203

204
    @Override
205
    public T extract(long maxQuantity, TransactionContext transaction) {
206
        IIngredientMatcher<T, M> matcher = getComponent().getMatcher();
4✔
207

208
        // Limit rate
209
        if (this.limitsEnabled) {
3!
210
            maxQuantity = (int) Math.min(maxQuantity, network.getRateLimit());
8✔
211
        }
212

213
        // Try extracting from all non-empty positions
214
        // until one succeeds.
215
        Pair<IPartPosIteratorHandler, Iterator<PartPos>> partPosIteratorData = getPartPosIteratorData(this::getNonEmptyPositions, channel);
7✔
216
        Iterator<PartPos> it = partPosIteratorData.getRight();
4✔
217
        while (it.hasNext()) {
3✔
218
            PartPos pos = it.next();
4✔
219

220
            // Skip if the position is not loaded or disabled
221
            if (!pos.getPos().isLoaded() || network.isPositionDisabled(pos)) {
9!
222
                continue;
×
223
            }
224

225
            // Obtain storage
226
            this.network.disablePosition(pos);
4✔
227
            IIngredientComponentStorage<T, M> positionedStorage = this.network.getPositionedStorage(pos);
5✔
228

229
            // If we do an effective extraction, first simulate to check if it matches the filter
230
            PositionedAddonsNetworkIngredientsFilter<T> filter = this.network.getPositionedStorageFilter(pos);
5✔
231
            if (filter != null) {
2!
232
                try (var tx = Transaction.open(transaction)) {
×
233
                    T extractedSimulated = positionedStorage.extract(maxQuantity, tx);
×
234
                    if (!filter.testExtraction(extractedSimulated)) {
×
235
                        continue;
236
                    }
237
                }
×
238
            }
239

240
            T extracted = positionedStorage.extract(maxQuantity, transaction);
5✔
241

242
            this.network.enablePosition(pos);
4✔
243
            if (!matcher.isEmpty(extracted)) {
4!
244
                new MarkStoragePositionChangedOnRootCommitJournal(pos).updateSnapshots(transaction);
7✔
245
                savePartPosIteratorHandler(partPosIteratorData.getLeft(), transaction);
6✔
246
                return extracted;
2✔
247
            }
248
        }
×
249

250
        savePartPosIteratorHandler(partPosIteratorData.getLeft(), transaction);
6✔
251

252
        // Schedule an observation if nothing was extracted, because the index may not be initialized yet.
253
        scheduleObservation();
2✔
254

255
        return matcher.getEmptyInstance();
3✔
256
    }
257

258
    @Override
259
    public T extract(@Nonnull T prototype, M matchFlags, TransactionContext transaction) {
260
        IIngredientMatcher<T, M> matcher = getComponent().getMatcher();
×
261
        boolean checkQuantity = matcher.hasCondition(matchFlags, getComponent().getPrimaryQuantifier().getMatchCondition());
×
262

263
        // Limit rate
264
        if (this.limitsEnabled) {
×
265
            long limit = network.getRateLimit();
×
266
            if (matcher.getQuantity(prototype) > limit) {
×
267
                // Fail immediately if we require more than the limit
268
                if (checkQuantity) {
×
269
                    return matcher.getEmptyInstance();
×
270
                }
271

272
                // Otherwise, we reduce our requested quantity
273
                prototype = matcher.withQuantity(prototype, limit);
×
274
            }
275
        }
276

277
        final T prototypeFinal = prototype;
×
278
        long requiredQuantity = matcher.getQuantity(prototypeFinal);
×
279

280
        // Modify our match condition that will be used to test each separate interface
281
        if (checkQuantity) {
×
282
            matchFlags = matcher.withoutCondition(matchFlags, getComponent().getPrimaryQuantifier().getMatchCondition());
×
283
        }
284
        M finalMatchFlags = matchFlags;
×
285

286
        // Maintain a temporary mapping of prototype items to their total count over all positions,
287
        // plus the list of positions in which they are present.
288
        IIngredientMapMutable<T, M, Pair<Wrapper<Long>, List<PartPos>>> validInstancesCollapsed = new IngredientHashMap<>(getComponent());
×
289

290
        // Try extracting from all positions that match with the given conditions
291
        // until one succeeds.
292
        Pair<IPartPosIteratorHandler, Iterator<PartPos>> partPosIteratorData = getPartPosIteratorData(() -> this.getMatchingPositions(prototypeFinal, finalMatchFlags), channel);
×
293
        Iterator<PartPos> it = partPosIteratorData.getRight();
×
294
        while (it.hasNext()) {
×
295
            PartPos pos = it.next();
×
296

297
            // Skip if the position is not loaded or disabled
298
            if (!pos.getPos().isLoaded() || network.isPositionDisabled(pos)) {
×
299
                continue;
×
300
            }
301

302
            // Do a simulated extraction
303
            this.network.disablePosition(pos);
×
304
            T extractedSimulated;
305
            try (var tx = Transaction.open(transaction)) {
×
306
                extractedSimulated = this.network.getPositionedStorage(pos).extract(prototypeFinal, finalMatchFlags, tx);
×
307
            }
308
            this.network.enablePosition(pos);
×
309
            T storagePrototype = getComponent().getMatcher().withQuantity(extractedSimulated, 1);
×
310

311
            // Skip if a filter was set that doesn't match the simulated extraction
312
            PositionedAddonsNetworkIngredientsFilter<T> filter = this.network.getPositionedStorageFilter(pos);
×
313
            if (filter != null && !filter.testExtraction(extractedSimulated)) {
×
314
                continue;
×
315
            }
316

317
            // Get existing value from temporary mapping
318
            Pair<Wrapper<Long>, List<PartPos>> existingValue = validInstancesCollapsed.get(storagePrototype);
×
319
            if (existingValue == null) {
×
320
                existingValue = Pair.of(new Wrapper<>(0L), Lists.newLinkedList());
×
321
                validInstancesCollapsed.put(storagePrototype, existingValue);
×
322
            }
323

324
            // Update the counter and pos-list for our prototype
325
            long newCount = existingValue.getLeft().get() + matcher.getQuantity(extractedSimulated);
×
326
            existingValue.getLeft().set(newCount);
×
327
            existingValue.getRight().add(pos);
×
328

329
            // If the count is sufficient for our query, return
330
            if (newCount >= requiredQuantity) {
×
331
                // Save the iterator state before returning
332
                savePartPosIteratorHandler(partPosIteratorData.getLeft(), transaction);
×
333
                existingValue.getLeft().set(requiredQuantity);
×
334
                return finalizeExtraction(storagePrototype, matchFlags, existingValue, transaction);
×
335
            }
336
        }
×
337

338
        // If we reach this point, then our effective count is below requiredQuantity
339

340
        // Save the iterator state before returning
341
        savePartPosIteratorHandler(partPosIteratorData.getLeft(), transaction);
×
342

343
        // Fail if we required an exact quantity
344
        if (checkQuantity) {
×
345
            return matcher.getEmptyInstance();
×
346
        }
347

348
        // Extract for the instance that had the most matches if we didn't require an exact quantity
349
        Pair<Wrapper<Long>, List<PartPos>> maxValue = Pair.of(new Wrapper<>(0L), Lists.newArrayList());
×
350
        T maxInstance = matcher.getEmptyInstance();
×
351
        for (Map.Entry<T, Pair<Wrapper<Long>, List<PartPos>>> entry : validInstancesCollapsed) {
×
352
            if (entry.getValue().getLeft().get() > maxValue.getLeft().get()) {
×
353
                maxInstance = entry.getKey();
×
354
                maxValue = entry.getValue();
×
355
            }
356
        }
×
357

358
        // Schedule an observation, as since this method is called, there may be a need for changes later on.
359
        scheduleObservation();
×
360

361
        return finalizeExtraction(maxInstance, matchFlags, maxValue, transaction);
×
362
    }
363

364
    protected T finalizeExtraction(T instancePrototype, M matchFlags, Pair<Wrapper<Long>, List<PartPos>> value,
365
                                   TransactionContext transaction) {
366
        IIngredientMatcher<T, M> matcher = getComponent().getMatcher();
×
367
        long extractedCount = value.getLeft().get();
×
368
        if (extractedCount > 0) {
×
369
            long toExtract = extractedCount;
×
370
            for (PartPos pos : value.getRight()) {
×
371
                // Update the remaining prototype quantity for this iteration
372
                instancePrototype = matcher.withQuantity(instancePrototype, toExtract);
×
373

374
                this.network.disablePosition(pos);
×
375
                T extracted = this.network.getPositionedStorage(pos).extract(instancePrototype, matchFlags, transaction);
×
376
                this.network.enablePosition(pos);
×
377
                markStoragePositionChanged(channel, pos);
×
378
                long thisExtractedAmount = matcher.getQuantity(extracted);
×
379
                toExtract -= thisExtractedAmount;
×
380
            }
×
381
            // Quick heuristic check to see if 'storage' did not lie during its simulation
382
            if (toExtract != 0) {
×
383
                /*IntegratedDynamics.clog(org.apache.logging.log4j.Level.WARN, String.format(
384
                        "A storage resulted in inconsistent simulated and non-simulated output. Storages: %s", value.getRight()));*/
385
                // This is not such a huge problem actually, so just make sure our output is correct.
386
                extractedCount -= toExtract;
×
387
            }
388
        }
389
        return getComponent().getMatcher().withQuantity(instancePrototype, extractedCount);
×
390
    }
391

392
    protected void scheduleObservation() {
393
        this.network.scheduleObservation();
3✔
394
    }
1✔
395

396
    class MarkStoragePositionChangedOnRootCommitJournal extends SnapshotJournal<Void> {
397
        private final PartPos pos;
398

399
        MarkStoragePositionChangedOnRootCommitJournal(PartPos pos) {
5✔
400
            this.pos = pos;
3✔
401
        }
1✔
402

403
        @Override
404
        protected Void createSnapshot() {
405
            return null;
2✔
406
        }
407

408
        @Override
409
        protected void revertToSnapshot(Void unused) {
410

411
        }
1✔
412

413
        @Override
414
        protected void onRootCommit(Void originalState) {
415
            super.onRootCommit(originalState);
3✔
416
            markStoragePositionChanged(channel, pos);
8✔
417
        }
1✔
418
    }
419

420
    class PartPosIteratorHandledJournal extends SnapshotJournal<Void> {
421
        private final IPartPosIteratorHandler iteratorHandler;
422

423
        PartPosIteratorHandledJournal(IPartPosIteratorHandler iteratorHandler) {
5✔
424
            this.iteratorHandler = iteratorHandler;
3✔
425
        }
1✔
426

427
        @Override
428
        protected Void createSnapshot() {
429
            return null;
2✔
430
        }
431

432
        @Override
433
        protected void revertToSnapshot(Void unused) {
434
            network.setPartPosIteratorHandler(iteratorHandler);
6✔
435
        }
1✔
436
    }
437

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