-
-
Notifications
You must be signed in to change notification settings - Fork 10
/
IngredientComponentStorageWrapperHandlerItemStack.java
392 lines (330 loc) · 16.6 KB
/
IngredientComponentStorageWrapperHandlerItemStack.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
package org.cyclops.commoncapabilities.ingredient.storage;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import net.minecraft.world.item.ItemStack;
import net.minecraft.core.Direction;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
import org.apache.commons.lang3.tuple.Pair;
import org.cyclops.commoncapabilities.api.capability.itemhandler.ISlotlessItemHandler;
import org.cyclops.commoncapabilities.api.capability.itemhandler.ItemHandlerItemStackIterator;
import org.cyclops.commoncapabilities.api.capability.itemhandler.ItemMatch;
import org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;
import org.cyclops.commoncapabilities.api.ingredient.storage.IIngredientComponentStorage;
import org.cyclops.commoncapabilities.api.ingredient.storage.IIngredientComponentStorageSlotted;
import org.cyclops.commoncapabilities.api.ingredient.storage.IIngredientComponentStorageWrapperHandler;
import org.cyclops.commoncapabilities.api.ingredient.storage.IngredientComponentStorageEmpty;
import org.cyclops.commoncapabilities.capability.itemhandler.SlotlessItemHandlerConfig;
import org.cyclops.cyclopscore.datastructure.Wrapper;
import org.cyclops.cyclopscore.helper.Helpers;
import org.cyclops.cyclopscore.ingredient.collection.FilteredIngredientCollectionIterator;
import org.cyclops.cyclopscore.ingredient.collection.IIngredientMapMutable;
import org.cyclops.cyclopscore.ingredient.collection.IngredientHashMap;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* Item storage wrapper handler for {@link IItemHandler}.
* @author rubensworks
*/
public class IngredientComponentStorageWrapperHandlerItemStack
implements IIngredientComponentStorageWrapperHandler<ItemStack, Integer, IItemHandler> {
private final IngredientComponent<ItemStack, Integer> ingredientComponent;
public IngredientComponentStorageWrapperHandlerItemStack(IngredientComponent<ItemStack, Integer> ingredientComponent) {
this.ingredientComponent = Objects.requireNonNull(ingredientComponent);
}
@Override
public IIngredientComponentStorage<ItemStack, Integer> wrapComponentStorage(IItemHandler storage) {
return new ComponentStorageWrapper(getComponent(), storage);
}
public IIngredientComponentStorage<ItemStack, Integer> wrapComponentStorage(IItemHandler storage,
ISlotlessItemHandler slotlessStorage) {
return new ComponentStorageWrapperCombined(getComponent(), storage, slotlessStorage);
}
@Override
public IItemHandler wrapStorage(IIngredientComponentStorage<ItemStack, Integer> componentStorage) {
if (componentStorage instanceof IIngredientComponentStorageSlotted) {
return new ItemStorageWrapperSlotted(getComponent(),
(IIngredientComponentStorageSlotted<ItemStack, Integer>) componentStorage);
}
return new ItemStorageWrapper(getComponent(), componentStorage);
}
@Override
public LazyOptional<IItemHandler> getStorage(ICapabilityProvider capabilityProvider, @Nullable Direction facing) {
return capabilityProvider.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, facing);
}
@Override
public IIngredientComponentStorage<ItemStack, Integer> getComponentStorage(ICapabilityProvider capabilityProvider,
@Nullable Direction facing) {
LazyOptional<IItemHandler> storageSlotted = getStorage(capabilityProvider, facing);
LazyOptional<ISlotlessItemHandler> storageSlotless = capabilityProvider.getCapability(SlotlessItemHandlerConfig.CAPABILITY, facing);
if (storageSlotted.isPresent()) {
if (storageSlotless.isPresent()) {
return wrapComponentStorage(storageSlotted.orElse(null), storageSlotless.orElse(null));
} else {
return wrapComponentStorage(storageSlotted.orElse(null));
}
}
return new IngredientComponentStorageEmpty<>(getComponent());
}
@Override
public IngredientComponent<ItemStack, Integer> getComponent() {
return this.ingredientComponent;
}
public static class ComponentStorageWrapper implements IIngredientComponentStorageSlotted<ItemStack, Integer> {
private final IngredientComponent<ItemStack, Integer> ingredientComponent;
private final IItemHandler storage;
public ComponentStorageWrapper(IngredientComponent<ItemStack, Integer> ingredientComponent,
IItemHandler storage) {
this.ingredientComponent = ingredientComponent;
this.storage = storage;
}
@Override
public IngredientComponent<ItemStack, Integer> getComponent() {
return this.ingredientComponent;
}
@Override
public Iterator<ItemStack> iterator() {
return new ItemHandlerItemStackIterator(storage);
}
@Override
public Iterator<ItemStack> iterator(@Nonnull ItemStack prototype, Integer matchFlags) {
if (getComponent().getMatcher().getAnyMatchCondition().equals(matchFlags)) {
return iterator();
}
return new FilteredIngredientCollectionIterator<>(iterator(), getComponent().getMatcher(),
prototype, matchFlags);
}
@Override
public long getMaxQuantity() {
long sum = 0;
int slots = storage.getSlots();
for (int slot = 0; slot < slots; slot++) {
sum = Math.addExact(sum, storage.getSlotLimit(slot));
}
return sum;
}
@Override
public ItemStack insert(@Nonnull ItemStack ingredient, boolean simulate) {
return ItemHandlerHelper.insertItem(storage, ingredient, simulate);
}
@Override
public ItemStack extract(@Nonnull ItemStack prototype, Integer matchFlags, boolean simulate) {
int slots = storage.getSlots();
boolean checkStackSize = (matchFlags & ItemMatch.STACKSIZE) > 0;
int requiredStackSize = prototype.getCount();
// Maintain a temporary mapping of prototype items to their total count over all slots,
// plus the list of slots in which they are present.
IIngredientMapMutable<ItemStack, Integer, Pair<Wrapper<Integer>, List<Integer>>> validInstancesCollapsed = new IngredientHashMap<>(getComponent());
int subMatchFlags = matchFlags & ~ItemMatch.STACKSIZE;
for (int slot = 0; slot < slots; slot++) {
ItemStack extractedSimulated = storage.extractItem(slot, requiredStackSize, true);
if (!extractedSimulated.isEmpty()
&& getComponent().getMatcher().matches(prototype, extractedSimulated, subMatchFlags)) {
ItemStack storagePrototype = getComponent().getMatcher().withQuantity(extractedSimulated, 1);
// Get existing value from temporary mapping
Pair<Wrapper<Integer>, List<Integer>> existingValue = validInstancesCollapsed.get(storagePrototype);
if (existingValue == null) {
existingValue = Pair.of(new Wrapper<>(0), Lists.newLinkedList());
validInstancesCollapsed.put(storagePrototype, existingValue);
}
// Update the counter and slot-list for our prototype
int newCount = existingValue.getLeft().get() + extractedSimulated.getCount();
existingValue.getLeft().set(newCount);
existingValue.getRight().add(slot);
// If the count is sufficient for our query, return
if (newCount >= requiredStackSize) {
// Actually extract if we are not simulating the extraction
// We assume that the simulated extraction resulted in the same output
// as the non-simulated output, so we ignore its output
existingValue.getLeft().set(requiredStackSize);
return finalizeExtraction(storagePrototype, existingValue, requiredStackSize, simulate);
}
}
}
// If we reach this point, then our effective count is below requiredStackSize
// Fail if we required an exact quantity
if (checkStackSize) {
return ItemStack.EMPTY;
}
// Extract for the instance that had the most matches if we didn't require an exact quantity
Pair<Wrapper<Integer>, List<Integer>> maxValue = Pair.of(new Wrapper<>(0), Lists.newArrayList());
ItemStack maxInstance = ItemStack.EMPTY;
for (Map.Entry<ItemStack, Pair<Wrapper<Integer>, List<Integer>>> entry : validInstancesCollapsed) {
if (entry.getValue().getLeft().get() > maxValue.getLeft().get()) {
maxInstance = entry.getKey();
maxValue = entry.getValue();
}
}
return finalizeExtraction(maxInstance, maxValue, requiredStackSize, simulate);
}
protected ItemStack finalizeExtraction(ItemStack instancePrototype, Pair<Wrapper<Integer>, List<Integer>> value,
int requiredQuantity, boolean simulate) {
long extractedCount = value.getLeft().get();
if (!simulate && extractedCount > 0) {
int toExtract = requiredQuantity;
for (Integer finalSlot : value.getRight()) {
ItemStack extractedActual = storage.extractItem(finalSlot, toExtract, false);
toExtract -= extractedActual.getCount();
}
// Quick heuristic check to see if 'storage' did not lie during its simulation
if (toExtract != requiredQuantity - extractedCount) {
throw new IllegalStateException("An item storage resulted in inconsistent simulated and non-simulated output.");
}
}
return getComponent().getMatcher().withQuantity(instancePrototype, extractedCount);
}
@Override
public ItemStack extract(long maxQuantity, boolean simulate) {
int slots = storage.getSlots();
int amount = Helpers.castSafe(maxQuantity);
for (int slot = 0; slot < slots; slot++) {
ItemStack extractedSimulated = storage.extractItem(slot, amount, true);
if (!extractedSimulated.isEmpty()) {
return simulate ? extractedSimulated : storage.extractItem(slot, amount, false);
}
}
return ItemStack.EMPTY;
}
@Override
public int getSlots() {
return storage.getSlots();
}
@Override
public ItemStack getSlotContents(int slot) {
return storage.getStackInSlot(slot);
}
@Override
public long getMaxQuantity(int slot) {
return storage.getSlotLimit(slot);
}
@Override
public ItemStack insert(int slot, @Nonnull ItemStack ingredient, boolean simulate) {
return storage.insertItem(slot, ingredient, simulate);
}
@Override
public ItemStack extract(int slot, long maxQuantity, boolean simulate) {
return storage.extractItem(slot, Helpers.castSafe(maxQuantity), simulate);
}
}
public static class ComponentStorageWrapperCombined extends ComponentStorageWrapper {
private final ISlotlessItemHandler storageSlotless;
public ComponentStorageWrapperCombined(IngredientComponent<ItemStack, Integer> ingredientComponent,
IItemHandler storage, ISlotlessItemHandler storageSlotless) {
super(ingredientComponent, storage);
this.storageSlotless = storageSlotless;
}
@Override
public Iterator<ItemStack> iterator() {
return storageSlotless.getItems();
}
@Override
public Iterator<ItemStack> iterator(@Nonnull ItemStack prototype, Integer matchFlags) {
return storageSlotless.findItems(prototype, matchFlags);
}
@Override
public long getMaxQuantity() {
return storageSlotless.getLimit();
}
@Override
public ItemStack insert(@Nonnull ItemStack ingredient, boolean simulate) {
return storageSlotless.insertItem(ingredient, simulate);
}
@Override
public ItemStack extract(long maxQuantity, boolean simulate) {
return storageSlotless.extractItem(Helpers.castSafe(maxQuantity), simulate);
}
@Override
public ItemStack extract(@Nonnull ItemStack prototype, Integer matchFlags, boolean simulate) {
return storageSlotless.extractItem(prototype, matchFlags, simulate);
}
}
public static class ItemStorageWrapper implements IItemHandler {
private final IngredientComponent<ItemStack, Integer> ingredientComponent;
private final IIngredientComponentStorage<ItemStack, Integer> storage;
public ItemStorageWrapper(IngredientComponent<ItemStack, Integer> ingredientComponent,
IIngredientComponentStorage<ItemStack, Integer> storage) {
this.ingredientComponent = ingredientComponent;
this.storage = storage;
}
@Override
public int getSlots() {
// +1 so that at least one slot appears empty, for when others want to insert
return Iterators.size(storage.iterator()) + 1;
}
@Nonnull
@Override
public ItemStack getStackInSlot(int slot) {
try {
return Iterators.get(storage.iterator(), slot);
} catch (IndexOutOfBoundsException e) {
return ItemStack.EMPTY;
}
}
@Nonnull
@Override
public ItemStack insertItem(int slot, @Nonnull ItemStack stack, boolean simulate) {
return storage.insert(stack, simulate);
}
@Nonnull
@Override
public ItemStack extractItem(int slot, int amount, boolean simulate) {
ItemStack slotItem = Iterators.get(storage.iterator(), slot, ItemStack.EMPTY);
if (slotItem.isEmpty()) {
return slotItem;
}
return storage.extract(ingredientComponent.getMatcher().withQuantity(slotItem, amount),
ingredientComponent.getMatcher().getExactMatchNoQuantityCondition(), simulate);
}
@Override
public int getSlotLimit(int slot) {
return Helpers.castSafe(ingredientComponent.getMatcher().getMaximumQuantity());
}
@Override
public boolean isItemValid(int slot, @Nonnull ItemStack stack) {
return true;
}
}
public static class ItemStorageWrapperSlotted implements IItemHandler {
private final IngredientComponent<ItemStack, Integer> ingredientComponent;
private final IIngredientComponentStorageSlotted<ItemStack, Integer> storage;
public ItemStorageWrapperSlotted(IngredientComponent<ItemStack, Integer> ingredientComponent,
IIngredientComponentStorageSlotted<ItemStack, Integer> storage) {
this.ingredientComponent = ingredientComponent;
this.storage = storage;
}
@Override
public int getSlots() {
return storage.getSlots();
}
@Nonnull
@Override
public ItemStack getStackInSlot(int slot) {
return storage.getSlotContents(slot);
}
@Nonnull
@Override
public ItemStack insertItem(int slot, @Nonnull ItemStack stack, boolean simulate) {
return storage.insert(slot, stack, simulate);
}
@Nonnull
@Override
public ItemStack extractItem(int slot, int amount, boolean simulate) {
return storage.extract(slot, amount, simulate);
}
@Override
public int getSlotLimit(int slot) {
return Helpers.castSafe(storage.getMaxQuantity(slot));
}
@Override
public boolean isItemValid(int slot, @Nonnull ItemStack stack) {
return true;
}
}
}