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

CyclopsMC / IntegratedDynamics / 20210191346

14 Dec 2025 03:32PM UTC coverage: 19.514% (-33.5%) from 53.061%
20210191346

push

github

rubensworks
Remove deprecations

663 of 8728 branches covered (7.6%)

Branch coverage included in aggregate %.

6786 of 29445 relevant lines covered (23.05%)

1.09 hits per line

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

0.0
/src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/ValueTypeRecipeLPElement.java
1
package org.cyclops.integrateddynamics.core.logicprogrammer;
2

3
import com.google.common.collect.Lists;
4
import com.google.common.collect.Maps;
5
import lombok.Getter;
6
import lombok.Setter;
7
import net.minecraft.ResourceLocationException;
8
import net.minecraft.client.Minecraft;
9
import net.minecraft.core.HolderSet;
10
import net.minecraft.core.NonNullList;
11
import net.minecraft.core.registries.BuiltInRegistries;
12
import net.minecraft.core.registries.Registries;
13
import net.minecraft.network.chat.Component;
14
import net.minecraft.resources.ResourceLocation;
15
import net.minecraft.tags.TagKey;
16
import net.minecraft.world.Container;
17
import net.minecraft.world.entity.player.Player;
18
import net.minecraft.world.inventory.ClickType;
19
import net.minecraft.world.inventory.Slot;
20
import net.minecraft.world.item.Item;
21
import net.minecraft.world.item.ItemStack;
22
import net.minecraft.world.item.Items;
23
import net.neoforged.neoforge.capabilities.Capabilities;
24
import net.neoforged.neoforge.fluids.FluidStack;
25
import net.neoforged.neoforge.transfer.ResourceHandler;
26
import net.neoforged.neoforge.transfer.access.ItemAccess;
27
import net.neoforged.neoforge.transfer.fluid.FluidResource;
28
import net.neoforged.neoforge.transfer.transaction.Transaction;
29
import org.cyclops.commoncapabilities.api.capability.fluidhandler.FluidMatch;
30
import org.cyclops.commoncapabilities.api.capability.recipehandler.IPrototypedIngredientAlternatives;
31
import org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition;
32
import org.cyclops.commoncapabilities.api.capability.recipehandler.PrototypedIngredientAlternativesList;
33
import org.cyclops.commoncapabilities.api.capability.recipehandler.RecipeDefinition;
34
import org.cyclops.commoncapabilities.api.ingredient.IPrototypedIngredient;
35
import org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;
36
import org.cyclops.commoncapabilities.api.ingredient.MixedIngredients;
37
import org.cyclops.commoncapabilities.api.ingredient.PrototypedIngredient;
38
import org.cyclops.cyclopscore.helper.IModHelpers;
39
import org.cyclops.cyclopscore.helper.IModHelpersNeoForge;
40
import org.cyclops.cyclopscore.inventory.slot.SlotExtended;
41
import org.cyclops.integrateddynamics.IntegratedDynamics;
42
import org.cyclops.integrateddynamics.api.evaluate.variable.IValue;
43
import org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;
44
import org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementType;
45
import org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeRecipe;
46
import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;
47
import org.cyclops.integrateddynamics.core.helper.Helpers;
48
import org.cyclops.integrateddynamics.core.helper.L10NValues;
49
import org.cyclops.integrateddynamics.core.ingredient.ItemMatchProperties;
50
import org.cyclops.integrateddynamics.core.logicprogrammer.client.ValueTypeRecipeLPElementClient;
51
import org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;
52
import org.cyclops.integrateddynamics.network.packet.LogicProgrammerValueTypeRecipeSlotPropertiesChangedPacket;
53

54
import java.util.Collections;
55
import java.util.List;
56
import java.util.Map;
57
import java.util.Optional;
58
import java.util.function.Consumer;
59
import java.util.stream.Collectors;
60

61
/**
62
 * Element for recipes.
63
 * This is hardcoded to only support items, fluids and energy
64
 * @author rubensworks
65
 */
66
public class ValueTypeRecipeLPElement extends ValueTypeLPElementBase<ValueTypeRecipeLPElementClient> {
67

68
    public static final int SLOT_OFFSET = 4;
69
    public static final int TICK_DELAY = 30;
70

71
    @Getter
×
72
    private NonNullList<ItemMatchProperties> inputStacks;
73
    private ItemStack inputFluid;
74
    @Getter
×
75
    @Setter
×
76
    private String inputFluidAmount = "0";
77
    @Getter
×
78
    @Setter
×
79
    private String inputEnergy = "0";
80
    private NonNullList<ItemStack> outputStacks;
81
    private ItemStack outputFluid;
82
    @Getter
×
83
    @Setter
×
84
    private String outputFluidAmount = "0";
85
    @Getter
×
86
    @Setter
×
87
    private String outputEnergy = "0";
88

89
    public ValueTypeRecipeLPElement() {
90
        super(ValueTypes.OBJECT_RECIPE);
×
91
    }
×
92

93
    @Override
94
    public ValueTypeRecipeLPElementClient constructClient() {
95
        return new ValueTypeRecipeLPElementClient(this);
×
96
    }
97

98
    @Override
99
    public ILogicProgrammerElementType getType() {
100
        return LogicProgrammerElementTypes.VALUETYPE;
×
101
    }
102

103
    @Override
104
    public IConfigRenderPattern getRenderPattern() {
105
        return IConfigRenderPattern.RECIPE;
×
106
    }
107

108
    @Override
109
    public void onInputSlotUpdated(Player player, int slotId, ItemStack itemStack) {
110
        if (inputStacks == null) {
×
111
            return;
×
112
        }
113

114
        if (slotId >= 0 && slotId < 9) {
×
115
            ItemStack itemStackOld = inputStacks.get(slotId).getItemStack();
×
116
            if (itemStackOld.getItem() != itemStack.getItem()) {
×
117
                inputStacks.set(slotId, new ItemMatchProperties(itemStack.copy()));
×
118
                if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {
×
119
                    getClient().refreshPropertiesGui(slotId);
×
120
                }
121
            }
122
        }
123
        if (slotId == 9) {
×
124
            inputFluid = itemStack.copy();
×
125
            if (inputFluidAmount.equalsIgnoreCase("0")) {
×
126
                int amount = IModHelpersNeoForge.get().getFluidHelpers().getAmount(Helpers.getFluidStack(inputFluid));
×
127
                inputFluidAmount = Integer.toString(amount);
×
128
                if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {
×
129
                    getClient().refreshInputFluidAmountBox();
×
130
                }
131
            }
132
        }
133
        if (slotId > 9 && slotId < 13) {
×
134
            outputStacks.set(slotId - 10, itemStack.copy());
×
135
        }
136
        if (slotId == 13) {
×
137
            outputFluid = itemStack.copy();
×
138
            if (outputFluidAmount.equalsIgnoreCase("0")) {
×
139
                int amount = IModHelpersNeoForge.get().getFluidHelpers().getAmount(Helpers.getFluidStack(outputFluid));
×
140
                outputFluidAmount = Integer.toString(amount);
×
141
                if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {
×
142
                    getClient().refreshOutputFluidAmountBox();
×
143
                }
144
            }
145
        }
146
    }
×
147

148
    public void sendSlotPropertiesToServer(int slotId, ItemMatchProperties props) {
149
        IntegratedDynamics._instance.getPacketHandler().sendToServer(
×
150
                new LogicProgrammerValueTypeRecipeSlotPropertiesChangedPacket(
151
                        slotId, props.isNbt(), props.getItemTag() == null ? "" : props.getItemTag(), props.getTagQuantity(), props.isReusable()));
×
152
    }
×
153

154
    // Used by ID-Compat for JEI recipe transfer handler
155
    public boolean isValidForRecipeGrid(List<ItemMatchProperties> itemInputs, List<FluidStack> fluidInputs,
156
                                        List<ItemStack> itemOutputs, List<FluidStack> fluidOutputs) {
157
        return itemInputs.size() <= 9 && itemOutputs.size() <= 3
×
158
                && fluidInputs.size() <= 1 && fluidOutputs.size() <= 1;
×
159
    }
160

161
    protected void putItemPropertiesInContainer(ContainerLogicProgrammerBase container, int slot, ItemMatchProperties props) {
162
        putStackInContainer(container, slot, props.getItemStack());
×
163
        getInputStacks().set(slot, props);
×
164
    }
×
165

166
    protected void putStackInContainer(ContainerLogicProgrammerBase container, int slot, ItemStack itemStack) {
167
        // Offset: Player inventory, recipe grid slots
168
        container.setItem(container.getItems().size() - (36 + 14) + slot, 0, itemStack);
×
169
    }
×
170

171
    // Used by ID-Compat for JEI recipe transfer handler
172
    public void setRecipeGrid(ContainerLogicProgrammerBase container,
173
                              List<ItemMatchProperties> itemInputs, List<FluidStack> fluidInputs,
174
                              List<ItemStack> itemOutputs, List<FluidStack> fluidOutputs) {
175
        int slot = 0;
×
176

177
        // Fill input item slots
178
        for (ItemMatchProperties itemInput : itemInputs) {
×
179
            putItemPropertiesInContainer(container, slot, itemInput);
×
180
            slot++;
×
181
        }
×
182
        while (slot < 9) {
×
183
            putItemPropertiesInContainer(container, slot, new ItemMatchProperties(ItemStack.EMPTY));
×
184
            slot++;
×
185
        }
186

187
        // Fill input fluid slot
188
        slot = 9;
×
189
        FluidStack fluidStackInput = FluidStack.EMPTY;
×
190
        if (fluidInputs.size() > 0) {
×
191
            fluidStackInput = fluidInputs.get(0);
×
192
        }
193
        putStackInContainer(container, slot, fluidStackInput.isEmpty() ? ItemStack.EMPTY : getFluidBucket(fluidStackInput));
×
194
        inputFluidAmount = String.valueOf(IModHelpersNeoForge.get().getFluidHelpers().getAmount(fluidStackInput));
×
195
        if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {
×
196
            getClient().refreshInputFluidAmountBox();
×
197
        }
198

199
        // Fill input output slots
200
        slot = 10;
×
201
        for (ItemStack itemOutput : itemOutputs) {
×
202
            putStackInContainer(container, slot, itemOutput);
×
203
            slot++;
×
204
        }
×
205
        while (slot < 13) {
×
206
            putStackInContainer(container, slot, ItemStack.EMPTY);
×
207
            slot++;
×
208
        }
209

210
        // Fill output fluid slot
211
        slot = 13;
×
212
        FluidStack fluidStackOutput = FluidStack.EMPTY;
×
213
        if (fluidOutputs.size() > 0) {
×
214
            fluidStackOutput = fluidOutputs.get(0);
×
215
        }
216
        putStackInContainer(container, slot, fluidStackOutput.isEmpty() ? ItemStack.EMPTY : getFluidBucket(fluidStackOutput));
×
217
        outputFluidAmount = String.valueOf(IModHelpersNeoForge.get().getFluidHelpers().getAmount(fluidStackOutput));
×
218
        if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {
×
219
            getClient().refreshOutputFluidAmountBox();
×
220
        }
221
    }
×
222

223
    public static ItemStack getFluidBucket(FluidStack fluidStack) {
224
        ItemStack itemStack = new ItemStack(Items.BUCKET);
×
225
        ItemAccess itemAccess = ItemAccess.forStack(itemStack);
×
226
        ResourceHandler<FluidResource> fluidHandler = itemAccess.getCapability(Capabilities.Fluid.ITEM);
×
227
        try (var tx = Transaction.openRoot()) {
×
228
            fluidHandler.insert(FluidResource.of(fluidStack.getFluid()), IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume(), tx);
×
229
            tx.commit();
×
230
        }
231
        return itemAccess.getResource().toStack(itemAccess.getAmount());
×
232
    }
233

234
    protected boolean isInputValid() {
235
        return inputStacks.stream().anyMatch(ItemMatchProperties::isValid)
×
236
                || !inputFluid.isEmpty() || !inputFluidAmount.equalsIgnoreCase("0")
×
237
                || !inputEnergy.equalsIgnoreCase("0");
×
238
    }
239

240
    protected boolean isOutputValid() {
241
        return outputStacks.stream().anyMatch(stack -> !stack.isEmpty())
×
242
                || !outputFluid.isEmpty() || !outputFluidAmount.equalsIgnoreCase("0")
×
243
                || !outputEnergy.equalsIgnoreCase("0");
×
244
    }
245

246
    @Override
247
    public boolean canWriteElementPre() {
248
        return isInputValid() == isOutputValid(); // Not &&, because we also allow fully blank recipes
×
249
    }
250

251
    @Override
252
    public void activate() {
253
        inputStacks = NonNullList.withSize(9, new ItemMatchProperties(ItemStack.EMPTY));
×
254
        for (int i = 0; i < 9; i++) {
×
255
            inputStacks.set(i, new ItemMatchProperties(ItemStack.EMPTY));
×
256
        }
257
        inputFluid = ItemStack.EMPTY;
×
258
        inputFluidAmount = "0";
×
259
        inputEnergy = "0";
×
260
        outputStacks = NonNullList.withSize(3, ItemStack.EMPTY);
×
261
        outputFluid = ItemStack.EMPTY;
×
262
        outputFluidAmount = "0";
×
263
        outputEnergy = "0";
×
264
    }
×
265

266
    @Override
267
    public void deactivate() {
268

269
    }
×
270

271
    @Override
272
    public Component validate() {
273
        if (!inputFluid.isEmpty() && Helpers.getFluidStack(inputFluid).isEmpty()) {
×
274
            return Component.translatable(L10NValues.VALUETYPE_OBJECT_FLUID_ERROR_NOFLUID);
×
275
        }
276
        if (!outputFluid.isEmpty() && Helpers.getFluidStack(outputFluid).isEmpty()) {
×
277
            return Component.translatable(L10NValues.VALUETYPE_OBJECT_FLUID_ERROR_NOFLUID);
×
278
        }
279
        try {
280
            Integer.parseInt(inputFluidAmount);
×
281
        } catch (NumberFormatException e) {
×
282
            return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDINPUT, inputFluidAmount);
×
283
        }
×
284
        try {
285
            Integer.parseInt(outputFluidAmount);
×
286
        } catch (NumberFormatException e) {
×
287
            return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDINPUT, outputFluidAmount);
×
288
        }
×
289
        try {
290
            Long.parseLong(inputEnergy);
×
291
        } catch (NumberFormatException e) {
×
292
            return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDINPUT, inputEnergy);
×
293
        }
×
294
        try {
295
            Long.parseLong(outputEnergy);
×
296
        } catch (NumberFormatException e) {
×
297
            return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDINPUT, outputEnergy);
×
298
        }
×
299
        // Validate input item tag strings if they are defined
300
        for (ItemMatchProperties inputStack : inputStacks) {
×
301
            if (inputStack.getItemTag() != null) {
×
302
                try {
303
                    ResourceLocation.parse(inputStack.getItemTag());
×
304
                } catch (ResourceLocationException e) {
×
305
                    return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDINPUT, inputStack.getItemTag());
×
306
                }
×
307
            }
308
        }
×
309
        return null;
×
310
    }
311

312
    @Override
313
    public boolean isItemValidForSlot(int slotId, ItemStack itemStack) {
314
        return true;
×
315
    }
316

317
    @Override
318
    public boolean slotClick(int slotId, Slot slot, int mouseButton, ClickType clickType, Player player) {
319
        return slotClickCommon(slotId, slot, mouseButton, clickType, player, getInputStacks(), 9, (i) -> {
×
320
            if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {
×
321
                getClient().setPropertySubGui(i);
×
322
            }
323
        }, (i) -> {
×
324
            if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {
×
325
                getClient().refreshPropertiesGui(i);
×
326
            }
327
        }) || super.slotClick(slotId, slot, mouseButton, clickType, player);
×
328
    }
329

330
    public static boolean slotClickCommon(int slotId, Slot slot, int mouseButton, ClickType clickType, Player player,
331
                                          List<ItemMatchProperties> inputStacks, int propertySlotCount,
332
                                          Consumer<Integer> setPropertySubGui,
333
                                          Consumer<Integer> refreshPropertiesGui) {
334
        if (slotId >= SLOT_OFFSET && slotId < propertySlotCount + SLOT_OFFSET) {
×
335
            if (clickType == ClickType.QUICK_MOVE && mouseButton == 0) {
×
336
                if (player.level().isClientSide()) {
×
337
                    int id = slotId - SLOT_OFFSET;
×
338
                    setPropertySubGui.accept(id);
×
339
                }
340
                return true;
×
341
            } else {
342
                // Similar logic as ContainerExtended.adjustPhantomSlot
343
                ItemMatchProperties props = inputStacks.get(slotId - SLOT_OFFSET);
×
344
                int quantityCurrent = props.getTagQuantity();
×
345
                int quantityNew;
346
                if (clickType == ClickType.QUICK_MOVE) {
×
347
                    quantityNew = mouseButton == 0 ? (quantityCurrent + 1) / 2 : quantityCurrent * 2;
×
348
                } else {
349
                    quantityNew = mouseButton == 0 ? quantityCurrent - 1 : quantityCurrent + 1;
×
350
                }
351

352
                if (quantityNew > slot.getMaxStackSize()) {
×
353
                    quantityNew = slot.getMaxStackSize();
×
354
                }
355

356
                props.setTagQuantity(quantityNew);
×
357
                if (!props.getItemStack().isEmpty()) {
×
358
                    props.getItemStack().setCount(quantityNew);
×
359
                }
360

361
                if (quantityNew <= 0) {
×
362
                    props.setItemTag(null);
×
363
                    props.setTagQuantity(1);
×
364
                    if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {
×
365
                        refreshPropertiesGui.accept(slotId - SLOT_OFFSET);
×
366
                    }
367
                }
368
            }
369
        }
370

371
        return false;
×
372
    }
373

374
    @Override
375
    public Slot createSlot(Container temporaryInputSlots, int slotId, int x, int y) {
376
        SlotExtended slot = new SlotExtended(temporaryInputSlots, slotId, x, y) {
×
377
            @Override
378
            public boolean mayPlace(ItemStack itemStack) {
379
                return ValueTypeRecipeLPElement.this.isItemValidForSlot(slotId, itemStack);
×
380
            }
381

382
            @Override
383
            public ItemStack getItem() {
384
                if (IModHelpers.get().getMinecraftHelpers().isClientSideThread() && slotId < 9) {
×
385
                    return getRotatingItemFromTag(getInputStacks().get(slotId))
×
386
                            .orElseGet(super::getItem);
×
387
                }
388
                return super.getItem();
×
389
            }
390
        };
391
        slot.setPhantom(true);
×
392
        return slot;
×
393
    }
394

395
    public static Optional<ItemStack> getRotatingItemFromTag(ItemMatchProperties props) {
396
        String tagName = props.getItemTag();
×
397
        if (tagName != null) {
×
398
            try {
399
                Optional<HolderSet.Named<Item>> tag = BuiltInRegistries.ITEM.get(TagKey.create(Registries.ITEM, ResourceLocation.parse(tagName)));
×
400
                if (!tag.isEmpty()) {
×
401
                    List<Item> items = tag.stream()
×
402
                            .flatMap(holders -> holders.stream())
×
403
                            .map(holder -> holder.value()).toList();
×
404
                    int tick = ((int) Minecraft.getInstance().level.getGameTime()) / TICK_DELAY;
×
405
                    Item item = items.get(tick % items.size());
×
406
                    return Optional.of(new ItemStack(item, props.getTagQuantity()));
×
407
                }
408
            } catch (ResourceLocationException e) {
×
409
                // Ignore invalid tags
410
            }
×
411
        }
412
        return Optional.empty();
×
413
    }
414

415
    @Override
416
    public int getItemStackSizeLimit() {
417
        return 64;
×
418
    }
419

420
    protected Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> getInputs(List<ItemMatchProperties> itemStacks,
421
                                                                                                      ItemStack fluid, int fluidAmount,
422
                                                                                                      long energy) {
423
        // Cut of itemStacks list until last non-empty stack
424
        int lastNonEmpty = 0;
×
425
        for (int i = 0; i < itemStacks.size(); i++) {
×
426
            if (itemStacks.get(i).isValid()) {
×
427
                lastNonEmpty = i + 1;
×
428
            }
429
        }
430
        itemStacks = itemStacks.subList(0, lastNonEmpty);
×
431

432
        // Override fluid amount
433
        FluidStack fluidStack = Helpers.getFluidStack(fluid);
×
434
        if (!fluidStack.isEmpty()) {
×
435
            fluidStack.setAmount(fluidAmount);
×
436
        }
437

438
        Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
×
439
        List<IPrototypedIngredientAlternatives<ItemStack, Integer>> items = itemStacks.stream()
×
440
                .map(ItemMatchProperties::createPrototypedIngredient)
×
441
                .collect(Collectors.toList());
×
442
        List<IPrototypedIngredientAlternatives<FluidStack, Integer>> fluids = !fluidStack.isEmpty()
×
443
                ? Collections.singletonList(new PrototypedIngredientAlternativesList<>(
×
444
                        Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.FLUIDSTACK, fluidStack, FluidMatch.FLUID | FluidMatch.DATA))))
×
445
                : Collections.emptyList();
×
446
        List<IPrototypedIngredientAlternatives<Long, Boolean>> energies = energy > 0 ?
×
447
                Collections.singletonList(new PrototypedIngredientAlternativesList<>(
×
448
                        Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ENERGY, energy, false))))
×
449
                : Collections.emptyList();
×
450
        if (!items.isEmpty()) {
×
451
            inputs.put(IngredientComponent.ITEMSTACK, (List) items);
×
452
        }
453
        if (!fluids.isEmpty()) {
×
454
            inputs.put(IngredientComponent.FLUIDSTACK, (List) fluids);
×
455
        }
456
        if (!energies.isEmpty()) {
×
457
            inputs.put(IngredientComponent.ENERGY, (List) energies);
×
458
        }
459

460
        return inputs;
×
461
    }
462

463
    public static Map<IngredientComponent<?, ?>, List<Boolean>> getInputsReusable(List<ItemMatchProperties> itemStacks) {
464
        Map<IngredientComponent<?, ?>, List<Boolean>> inputs = Maps.newIdentityHashMap();
×
465

466
        List<Boolean> items = itemStacks.stream()
×
467
                .map(ItemMatchProperties::isReusable)
×
468
                .collect(Collectors.toList());
×
469
        if (!items.isEmpty()) {
×
470
            inputs.put(IngredientComponent.ITEMSTACK, (List) items);
×
471
        }
472

473
        return inputs;
×
474
    }
475

476
    protected Map<IngredientComponent<?, ?>, List<?>> getOutputs(List<ItemStack> itemStacksIn,
477
                                                                 ItemStack fluid, int fluidAmount,
478
                                                                 long energy) {
479
        // Cut of itemStacks list until last non-empty stack
480
        List<ItemStack> itemStacks = Lists.newArrayList();
×
481
        for (int i = 0; i < itemStacksIn.size(); i++) {
×
482
            if (!itemStacksIn.get(i).isEmpty()) {
×
483
                itemStacks.add(itemStacksIn.get(i));
×
484
            }
485
        }
486

487
        // Override fluid amount
488
        FluidStack fluidStack = Helpers.getFluidStack(fluid);
×
489
        if (!fluidStack.isEmpty()) {
×
490
            fluidStack.setAmount(fluidAmount);
×
491
        }
492

493
        Map<IngredientComponent<?, ?>, List<?>> outputs = Maps.newIdentityHashMap();
×
494
        if (!itemStacks.isEmpty()) {
×
495
            outputs.put(IngredientComponent.ITEMSTACK, itemStacks);
×
496
        }
497
        if (!fluidStack.isEmpty()) {
×
498
            outputs.put(IngredientComponent.FLUIDSTACK, Collections.singletonList(fluidStack));
×
499
        }
500
        if (energy > 0) {
×
501
            outputs.put(IngredientComponent.ENERGY, Collections.singletonList(energy));
×
502
        }
503

504
        return outputs;
×
505
    }
506

507
    @Override
508
    public IValue getValue() {
509
        if (!isInputValid() && !isOutputValid()) {
×
510
            return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
511
        }
512
        return ValueObjectTypeRecipe.ValueRecipe.of(
×
513
                new RecipeDefinition(getInputs(this.inputStacks, this.inputFluid,
×
514
                        Integer.parseInt(this.inputFluidAmount), Long.parseLong(this.inputEnergy)),
×
515
                getInputsReusable(this.inputStacks),
×
516
                new MixedIngredients(getOutputs(this.outputStacks, this.outputFluid,
×
517
                        Integer.parseInt(this.outputFluidAmount), Long.parseLong(this.outputEnergy)))));
×
518
    }
519

520
    @Override
521
    public void setValue(IValue value) {
522
        ValueObjectTypeRecipe.ValueRecipe valueRecipe = (ValueObjectTypeRecipe.ValueRecipe) value;
×
523
        valueRecipe.getRawValue().ifPresent(recipe -> {
×
524
            loadInputStacks(recipe);
×
525
            loadInputFluid(recipe);
×
526
            loadInputEnergy(recipe);
×
527

528
            loadOutputStacks(recipe);
×
529
            loadOutputFluid(recipe);
×
530
            loadOutputEnergy(recipe);
×
531
        });
×
532
    }
×
533

534
    private void loadInputStacks(IRecipeDefinition recipe) {
535
        List<IPrototypedIngredientAlternatives<ItemStack, Integer>> listAlternatives = recipe.getInputs(IngredientComponent.ITEMSTACK);
×
536
        for (int i = 0; i < listAlternatives.size(); i++) {
×
537
            IPrototypedIngredientAlternatives<ItemStack, Integer> prototypes = listAlternatives.get(i);
×
538
            boolean reusable = recipe.isInputReusable(IngredientComponent.ITEMSTACK, i);
×
539
            ItemMatchProperties itemMatchProperties = ItemMatchProperties.fromPrototypedIngredient(prototypes, reusable);
×
540
            this.inputStacks.set(i, itemMatchProperties);
×
541
        }
542
    }
×
543

544
    private <T, M> Optional<T> loadFirstInput(IRecipeDefinition recipe, IngredientComponent<T, M> ingredientComponent) {
545
        List<IPrototypedIngredientAlternatives<T, M>> listAlternatives = recipe.getInputs(ingredientComponent);
×
546
        if (listAlternatives.size() > 0) {
×
547
            IPrototypedIngredientAlternatives<T, M> prototypes = listAlternatives.get(0);
×
548
            if (prototypes.getAlternatives().size() > 0) {
×
549
                IPrototypedIngredient<T, M> prototype = prototypes.getAlternatives().stream().findFirst().get();
×
550
                return Optional.of(prototype.getPrototype());
×
551
            }
552
        }
553
        return Optional.empty();
×
554
    }
555

556
    private void loadInputFluid(IRecipeDefinition recipe) {
557
        loadFirstInput(recipe, IngredientComponent.FLUIDSTACK).ifPresent(fluidStack -> {
×
558
            this.inputFluid = fluidStack.getFluid().getFluidType().getBucket(fluidStack);
×
559
            this.inputFluidAmount = Integer.toString(fluidStack.getAmount());
×
560
        });
×
561
    }
×
562

563
    private void loadInputEnergy(IRecipeDefinition recipe) {
564
        loadFirstInput(recipe, IngredientComponent.ENERGY).ifPresent(energy -> this.inputEnergy = Long.toString(energy));
×
565
    }
×
566

567
    private void loadOutputStacks(IRecipeDefinition recipe) {
568
        List<ItemStack> instances = recipe.getOutput().getInstances(IngredientComponent.ITEMSTACK);
×
569
        if (instances.size() > 0) {
×
570
            outputStacks.set(0, instances.get(0));
×
571
        }
572
        if (instances.size() > 1) {
×
573
            outputStacks.set(1, instances.get(1));
×
574
        }
575
        if (instances.size() > 2) {
×
576
            outputStacks.set(2, instances.get(2));
×
577
        }
578
    }
×
579

580
    private <T, M> Optional<T> loadFirstOutput(IRecipeDefinition recipe, IngredientComponent<T, M> ingredientComponent) {
581
        List<T> instances = recipe.getOutput().getInstances(ingredientComponent);
×
582
        if (instances.size() > 0) {
×
583
            return Optional.of(instances.get(0));
×
584
        }
585
        return Optional.empty();
×
586
    }
587

588
    private void loadOutputFluid(IRecipeDefinition recipe) {
589
        loadFirstOutput(recipe, IngredientComponent.FLUIDSTACK).ifPresent(fluidStack -> {
×
590
            this.outputFluid = fluidStack.getFluid().getFluidType().getBucket(fluidStack);
×
591
            this.outputFluidAmount = Integer.toString(fluidStack.getAmount());
×
592
        });
×
593
    }
×
594

595
    private void loadOutputEnergy(IRecipeDefinition recipe) {
596
        loadFirstOutput(recipe, IngredientComponent.ENERGY).ifPresent(energy -> this.outputEnergy = Long.toString(energy));
×
597
    }
×
598

599
    @Override
600
    public void setValueInContainer(ContainerLogicProgrammerBase container) {
601
        Container slots = container.getTemporaryInputSlots();
×
602

603
        // Input slots
604
        for (int i = 0; i < this.inputStacks.size(); i++) {
×
605
            ItemMatchProperties entry = this.inputStacks.get(i);
×
606
            slots.setItem(i, entry.getItemStack());
×
607
        }
608
        slots.setItem(9, this.inputFluid);
×
609

610
        // Output slots
611
        for (int i = 0; i < this.outputStacks.size(); i++) {
×
612
            slots.setItem(10 + i, this.outputStacks.get(i));
×
613
            // No need to set slot type, as this can't be changed for output stacks
614
        }
615
        slots.setItem(13, this.outputFluid);
×
616
    }
×
617

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