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

CyclopsMC / IntegratedDynamics / 20655316865

02 Jan 2026 09:52AM UTC coverage: 45.142% (+0.1%) from 45.043%
20655316865

push

github

rubensworks
Bump mod version

2622 of 8584 branches covered (30.55%)

Branch coverage included in aggregate %.

11898 of 23581 relevant lines covered (50.46%)

2.4 hits per line

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

39.22
/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 org.apache.commons.lang3.tuple.Pair;
5
import org.cyclops.commoncapabilities.api.ingredient.IIngredientMatcher;
6
import org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;
7
import org.cyclops.commoncapabilities.api.ingredient.storage.IIngredientComponentStorage;
8
import org.cyclops.cyclopscore.datastructure.Wrapper;
9
import org.cyclops.cyclopscore.ingredient.collection.IIngredientMapMutable;
10
import org.cyclops.cyclopscore.ingredient.collection.IngredientHashMap;
11
import org.cyclops.integrateddynamics.api.network.INetworkIngredientsChannel;
12
import org.cyclops.integrateddynamics.api.network.IPartPosIteratorHandler;
13
import org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetworkIngredients;
14
import org.cyclops.integrateddynamics.api.network.PositionedAddonsNetworkIngredientsFilter;
15
import org.cyclops.integrateddynamics.api.part.PartPos;
16

17
import javax.annotation.Nonnull;
18
import java.util.Iterator;
19
import java.util.List;
20
import java.util.Map;
21
import java.util.function.Supplier;
22

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

31
    private final IPositionedAddonsNetworkIngredients<T, M> network;
32
    private final int channel;
33

34
    private boolean limitsEnabled;
35

36
    public IngredientChannelAdapter(PositionedAddonsNetworkIngredients<T, M> network, int channel) {
2✔
37
        this.network = network;
3✔
38
        this.channel = channel;
3✔
39

40
        this.limitsEnabled = true;
3✔
41
    }
1✔
42

43
    public void enableLimits() {
44
        this.limitsEnabled = true;
×
45
    }
×
46

47
    public void disableLimits() {
48
        this.limitsEnabled = false;
×
49
    }
×
50

51
    public IPositionedAddonsNetworkIngredients<T, M> getNetwork() {
52
        return network;
3✔
53
    }
54

55
    public int getChannel() {
56
        return channel;
3✔
57
    }
58

59
    @Override
60
    public IngredientComponent<T, M> getComponent() {
61
        return network.getComponent();
4✔
62
    }
63

64
    protected abstract Iterator<PartPos> getNonFullPositions();
65
    protected abstract Iterator<PartPos> getAllPositions();
66
    protected abstract Iterator<PartPos> getNonEmptyPositions();
67
    protected abstract Iterator<PartPos> getMatchingPositions(@Nonnull T prototype, M matchFlags);
68

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

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

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

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

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

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

111
        return sum;
2✔
112
    }
113

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

124
    protected void savePartPosIteratorHandler(IPartPosIteratorHandler partPosIteratorHandler) {
125
        network.setPartPosIteratorHandler(partPosIteratorHandler);
4✔
126
    }
1✔
127

128
    protected void markStoragePositionChanged(int channel, PartPos targetPos) {
129
        this.network.scheduleObservationForced(channel, targetPos);
5✔
130
    }
1✔
131

132
    @Override
133
    public T insert(@Nonnull T ingredient, boolean simulate) {
134
        // First run the ingredient instance through the pre-consumers.
135
        for (IIngredientChannelInsertPreConsumer<T> insertPreConsumer : network.getInsertPreConsumers()) {
8!
136
            ingredient = insertPreConsumer.insert(this.channel, ingredient, simulate);
×
137
        }
×
138

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

141
        // Quickly return if the to-be-inserted ingredient was already empty
142
        if (matcher.isEmpty(ingredient)) {
4!
143
            return ingredient;
×
144
        }
145

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

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

165
            // Skip if the position is not loaded or disabled
166
            if (!pos.getPos().isLoaded() || network.isPositionDisabled(pos)) {
9!
167
                continue;
×
168
            }
169

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

176
            this.network.disablePosition(pos);
4✔
177
            long quantityBefore = matcher.getQuantity(ingredient);
4✔
178
            ingredient = this.network.getPositionedStorage(pos).insert(ingredient, simulate);
8✔
179
            long quantityAfter = matcher.getQuantity(ingredient);
4✔
180
            this.network.enablePosition(pos);
4✔
181
            if (!simulate && quantityBefore != quantityAfter) {
6!
182
                markStoragePositionChanged(channel, pos);
5✔
183
            }
184
            if (matcher.isEmpty(ingredient)) {
4!
185
                break;
1✔
186
            }
187
        }
×
188

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

195
        if (!simulate) {
2✔
196
            savePartPosIteratorHandler(partPosIteratorData.getLeft());
5✔
197
        }
198

199
        return ingredient;
2✔
200
    }
201

202
    @Override
203
    public T extract(long maxQuantity, boolean simulate) {
204
        IIngredientMatcher<T, M> matcher = getComponent().getMatcher();
4✔
205

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

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

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

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

227
            // If we do an effective extraction, first simulate to check if it matches the filter
228
            PositionedAddonsNetworkIngredientsFilter<T> filter = this.network.getPositionedStorageFilter(pos);
5✔
229
            if (filter != null && !simulate) {
2!
230
                T extractedSimulated = positionedStorage.extract(maxQuantity, true);
×
231
                if (!filter.testExtraction(extractedSimulated)) {
×
232
                    continue;
×
233
                }
234
            }
235

236
            T extracted = positionedStorage.extract(maxQuantity, simulate);
5✔
237

238
            // If simulating, just check the output
239
            if (filter != null && simulate && !filter.testExtraction(extracted)) {
2!
240
                continue;
×
241
            }
242

243
            this.network.enablePosition(pos);
4✔
244
            if (!matcher.isEmpty(extracted)) {
4!
245
                if (!simulate) {
2✔
246
                    markStoragePositionChanged(channel, pos);
5✔
247
                    savePartPosIteratorHandler(partPosIteratorData.getLeft());
5✔
248
                }
249
                return extracted;
2✔
250
            }
251
        }
×
252

253
        if (!simulate) {
×
254
            savePartPosIteratorHandler(partPosIteratorData.getLeft());
×
255
        }
256

257
        // Schedule an observation if nothing was extracted, because the index may not be initialized yet.
258
        scheduleObservation();
×
259

260
        return matcher.getEmptyInstance();
×
261
    }
262

263
    @Override
264
    public T extract(@Nonnull T prototype, M matchFlags, boolean simulate) {
265
        IIngredientMatcher<T, M> matcher = getComponent().getMatcher();
×
266
        boolean checkQuantity = matcher.hasCondition(matchFlags, getComponent().getPrimaryQuantifier().getMatchCondition());
×
267

268
        // Limit rate
269
        if (this.limitsEnabled) {
×
270
            long limit = network.getRateLimit();
×
271
            if (matcher.getQuantity(prototype) > limit) {
×
272
                // Fail immediately if we require more than the limit
273
                if (checkQuantity) {
×
274
                    return matcher.getEmptyInstance();
×
275
                }
276

277
                // Otherwise, we reduce our requested quantity
278
                prototype = matcher.withQuantity(prototype, limit);
×
279
            }
280
        }
281

282
        final T prototypeFinal = prototype;
×
283
        long requiredQuantity = matcher.getQuantity(prototypeFinal);
×
284

285
        // Modify our match condition that will be used to test each separate interface
286
        if (checkQuantity) {
×
287
            matchFlags = matcher.withoutCondition(matchFlags, getComponent().getPrimaryQuantifier().getMatchCondition());
×
288
        }
289
        M finalMatchFlags = matchFlags;
×
290

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

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

302
            // Skip if the position is not loaded or disabled
303
            if (!pos.getPos().isLoaded() || network.isPositionDisabled(pos)) {
×
304
                continue;
×
305
            }
306

307
            // Do a simulated extraction
308
            this.network.disablePosition(pos);
×
309
            T extractedSimulated = this.network.getPositionedStorage(pos).extract(prototypeFinal, finalMatchFlags, true);
×
310
            this.network.enablePosition(pos);
×
311
            T storagePrototype = getComponent().getMatcher().withQuantity(extractedSimulated, 1);
×
312

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

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

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

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

342
        // If we reach this point, then our effective count is below requiredQuantity
343

344
        // Save the iterator state before returning
345
        if (!simulate) {
×
346
            savePartPosIteratorHandler(partPosIteratorData.getLeft());
×
347
        }
348

349
        // Fail if we required an exact quantity
350
        if (checkQuantity) {
×
351
            return matcher.getEmptyInstance();
×
352
        }
353

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

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

367
        return finalizeExtraction(maxInstance, matchFlags, maxValue, simulate);
×
368
    }
369

370
    protected T finalizeExtraction(T instancePrototype, M matchFlags, Pair<Wrapper<Long>, List<PartPos>> value,
371
                                   boolean simulate) {
372
        IIngredientMatcher<T, M> matcher = getComponent().getMatcher();
×
373
        long extractedCount = value.getLeft().get();
×
374
        if (!simulate && extractedCount > 0) {
×
375
            long toExtract = extractedCount;
×
376
            for (PartPos pos : value.getRight()) {
×
377
                // Update the remaining prototype quantity for this iteration
378
                instancePrototype = matcher.withQuantity(instancePrototype, toExtract);
×
379

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

398
    protected void scheduleObservation() {
399
        this.network.scheduleObservation();
3✔
400
    }
1✔
401

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