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

CyclopsMC / IntegratedDynamics / 16552051255

27 Jul 2025 01:58PM UTC coverage: 53.206% (+8.0%) from 45.161%
16552051255

push

github

rubensworks
Resolve minor TODOs

2888 of 8740 branches covered (33.04%)

Branch coverage included in aggregate %.

17341 of 29280 relevant lines covered (59.22%)

3.08 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.fluids.capability.IFluidHandler;
26
import net.neoforged.neoforge.fluids.capability.IFluidHandlerItem;
27
import org.cyclops.commoncapabilities.api.capability.fluidhandler.FluidMatch;
28
import org.cyclops.commoncapabilities.api.capability.recipehandler.IPrototypedIngredientAlternatives;
29
import org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition;
30
import org.cyclops.commoncapabilities.api.capability.recipehandler.PrototypedIngredientAlternativesList;
31
import org.cyclops.commoncapabilities.api.capability.recipehandler.RecipeDefinition;
32
import org.cyclops.commoncapabilities.api.ingredient.IPrototypedIngredient;
33
import org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;
34
import org.cyclops.commoncapabilities.api.ingredient.MixedIngredients;
35
import org.cyclops.commoncapabilities.api.ingredient.PrototypedIngredient;
36
import org.cyclops.cyclopscore.helper.IModHelpers;
37
import org.cyclops.cyclopscore.helper.IModHelpersNeoForge;
38
import org.cyclops.cyclopscore.inventory.slot.SlotExtended;
39
import org.cyclops.integrateddynamics.IntegratedDynamics;
40
import org.cyclops.integrateddynamics.api.evaluate.variable.IValue;
41
import org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;
42
import org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementType;
43
import org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeRecipe;
44
import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;
45
import org.cyclops.integrateddynamics.core.helper.Helpers;
46
import org.cyclops.integrateddynamics.core.helper.L10NValues;
47
import org.cyclops.integrateddynamics.core.ingredient.ItemMatchProperties;
48
import org.cyclops.integrateddynamics.core.logicprogrammer.client.ValueTypeRecipeLPElementClient;
49
import org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;
50
import org.cyclops.integrateddynamics.network.packet.LogicProgrammerValueTypeRecipeSlotPropertiesChangedPacket;
51

52
import java.util.Collections;
53
import java.util.List;
54
import java.util.Map;
55
import java.util.Optional;
56
import java.util.stream.Collectors;
57

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

65
    public static final int SLOT_OFFSET = 4;
66
    public static final int TICK_DELAY = 30;
67

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

86
    public ValueTypeRecipeLPElement() {
87
        super(ValueTypes.OBJECT_RECIPE);
×
88
    }
×
89

90
    @Override
91
    public ValueTypeRecipeLPElementClient constructClient() {
92
        return new ValueTypeRecipeLPElementClient(this);
×
93
    }
94

95
    @Override
96
    public ILogicProgrammerElementType getType() {
97
        return LogicProgrammerElementTypes.VALUETYPE;
×
98
    }
99

100
    @Override
101
    public IConfigRenderPattern getRenderPattern() {
102
        return IConfigRenderPattern.RECIPE;
×
103
    }
104

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

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

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

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

158
    protected void putItemPropertiesInContainer(ContainerLogicProgrammerBase container, int slot, ItemMatchProperties props) {
159
        putStackInContainer(container, slot, props.getItemStack());
×
160
        getInputStacks().set(slot, props);
×
161
    }
×
162

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

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

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

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

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

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

220
    protected ItemStack getFluidBucket(FluidStack fluidStack) {
221
        ItemStack itemStack = new ItemStack(Items.BUCKET);
×
222
        IFluidHandlerItem fluidHandler = itemStack.getCapability(Capabilities.FluidHandler.ITEM);
×
223
        fluidHandler.fill(new FluidStack(fluidStack.getFluid(), IModHelpersNeoForge.get().getFluidHelpers().getBucketVolume()), IFluidHandler.FluidAction.EXECUTE);
×
224
        return fluidHandler.getContainer();
×
225
    }
226

227
    protected boolean isInputValid() {
228
        return inputStacks.stream().anyMatch(ItemMatchProperties::isValid)
×
229
                || !inputFluid.isEmpty() || !inputFluidAmount.equalsIgnoreCase("0")
×
230
                || !inputEnergy.equalsIgnoreCase("0");
×
231
    }
232

233
    protected boolean isOutputValid() {
234
        return outputStacks.stream().anyMatch(stack -> !stack.isEmpty())
×
235
                || !outputFluid.isEmpty() || !outputFluidAmount.equalsIgnoreCase("0")
×
236
                || !outputEnergy.equalsIgnoreCase("0");
×
237
    }
238

239
    @Override
240
    public boolean canWriteElementPre() {
241
        return isInputValid() == isOutputValid(); // Not &&, because we also allow fully blank recipes
×
242
    }
243

244
    @Override
245
    public void activate() {
246
        inputStacks = NonNullList.withSize(9, new ItemMatchProperties(ItemStack.EMPTY));
×
247
        for (int i = 0; i < 9; i++) {
×
248
            inputStacks.set(i, new ItemMatchProperties(ItemStack.EMPTY));
×
249
        }
250
        inputFluid = ItemStack.EMPTY;
×
251
        inputFluidAmount = "0";
×
252
        inputEnergy = "0";
×
253
        outputStacks = NonNullList.withSize(3, ItemStack.EMPTY);
×
254
        outputFluid = ItemStack.EMPTY;
×
255
        outputFluidAmount = "0";
×
256
        outputEnergy = "0";
×
257
    }
×
258

259
    @Override
260
    public void deactivate() {
261

262
    }
×
263

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

305
    @Override
306
    public boolean isItemValidForSlot(int slotId, ItemStack itemStack) {
307
        return true;
×
308
    }
309

310
    @Override
311
    public boolean slotClick(int slotId, Slot slot, int mouseButton, ClickType clickType, Player player) {
312
        if (slotId >= SLOT_OFFSET && slotId < 9 + SLOT_OFFSET) {
×
313
            if (clickType == ClickType.QUICK_MOVE && mouseButton == 0) {
×
314
                if (player.level().isClientSide()) {
×
315
                    int id = slotId - SLOT_OFFSET;
×
316
                    getClient().setPropertySubGui(id);
×
317
                }
318
                return true;
×
319
            } else {
320
                // Similar logic as ContainerExtended.adjustPhantomSlot
321
                ItemMatchProperties props = getInputStacks().get(slotId - SLOT_OFFSET);
×
322
                int quantityCurrent = props.getTagQuantity();
×
323
                int quantityNew;
324
                if (clickType == ClickType.QUICK_MOVE) {
×
325
                    quantityNew = mouseButton == 0 ? (quantityCurrent + 1) / 2 : quantityCurrent * 2;
×
326
                } else {
327
                    quantityNew = mouseButton == 0 ? quantityCurrent - 1 : quantityCurrent + 1;
×
328
                }
329

330
                if (quantityNew > slot.getMaxStackSize()) {
×
331
                    quantityNew = slot.getMaxStackSize();
×
332
                }
333

334
                props.setTagQuantity(quantityNew);
×
335
                if (!props.getItemStack().isEmpty()) {
×
336
                    props.getItemStack().setCount(quantityNew);
×
337
                }
338

339
                if (quantityNew <= 0) {
×
340
                    props.setItemTag(null);
×
341
                    props.setTagQuantity(1);
×
342
                    if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {
×
343
                        getClient().refreshPropertiesGui(slotId - SLOT_OFFSET);
×
344
                    }
345
                }
346
            }
347
        }
348

349
        return super.slotClick(slotId, slot, mouseButton, clickType, player);
×
350
    }
351

352
    @Override
353
    public Slot createSlot(Container temporaryInputSlots, int slotId, int x, int y) {
354
        SlotExtended slot = new SlotExtended(temporaryInputSlots, slotId, x, y) {
×
355
            @Override
356
            public boolean mayPlace(ItemStack itemStack) {
357
                return ValueTypeRecipeLPElement.this.isItemValidForSlot(slotId, itemStack);
×
358
            }
359

360
            @Override
361
            public ItemStack getItem() {
362
                if (IModHelpers.get().getMinecraftHelpers().isClientSideThread() && slotId < 9) {
×
363
                    ItemMatchProperties props = getInputStacks().get(slotId);
×
364
                    String tagName = props.getItemTag();
×
365
                    if (tagName != null) {
×
366
                        try {
367
                            Optional<HolderSet.Named<Item>> tag = BuiltInRegistries.ITEM.get(TagKey.create(Registries.ITEM, ResourceLocation.parse(tagName)));
×
368
                            if (!tag.isEmpty()) {
×
369
                                List<Item> items = tag.stream()
×
370
                                        .flatMap(holders -> holders.stream())
×
371
                                        .map(holder -> holder.value()).toList();
×
372
                                int tick = ((int) Minecraft.getInstance().level.getGameTime()) / TICK_DELAY;
×
373
                                Item item = items.get(tick % items.size());
×
374
                                return new ItemStack(item, props.getTagQuantity());
×
375
                            }
376
                        } catch (ResourceLocationException e) {
×
377
                            // Ignore invalid tags
378
                        }
×
379
                    }
380
                }
381
                return super.getItem();
×
382
            }
383
        };
384
        slot.setPhantom(true);
×
385
        return slot;
×
386
    }
387

388
    @Override
389
    public int getItemStackSizeLimit() {
390
        return 64;
×
391
    }
392

393
    protected Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> getInputs(List<ItemMatchProperties> itemStacks,
394
                                                                                                      ItemStack fluid, int fluidAmount,
395
                                                                                                      long energy) {
396
        // Cut of itemStacks list until last non-empty stack
397
        int lastNonEmpty = 0;
×
398
        for (int i = 0; i < itemStacks.size(); i++) {
×
399
            if (itemStacks.get(i).isValid()) {
×
400
                lastNonEmpty = i + 1;
×
401
            }
402
        }
403
        itemStacks = itemStacks.subList(0, lastNonEmpty);
×
404

405
        // Override fluid amount
406
        FluidStack fluidStack = Helpers.getFluidStack(fluid);
×
407
        if (!fluidStack.isEmpty()) {
×
408
            fluidStack.setAmount(fluidAmount);
×
409
        }
410

411
        Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
×
412
        List<IPrototypedIngredientAlternatives<ItemStack, Integer>> items = itemStacks.stream()
×
413
                .map(ItemMatchProperties::createPrototypedIngredient)
×
414
                .collect(Collectors.toList());
×
415
        List<IPrototypedIngredientAlternatives<FluidStack, Integer>> fluids = !fluidStack.isEmpty()
×
416
                ? Collections.singletonList(new PrototypedIngredientAlternativesList<>(
×
417
                        Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.FLUIDSTACK, fluidStack, FluidMatch.FLUID | FluidMatch.DATA))))
×
418
                : Collections.emptyList();
×
419
        List<IPrototypedIngredientAlternatives<Long, Boolean>> energies = energy > 0 ?
×
420
                Collections.singletonList(new PrototypedIngredientAlternativesList<>(
×
421
                        Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ENERGY, energy, false))))
×
422
                : Collections.emptyList();
×
423
        if (!items.isEmpty()) {
×
424
            inputs.put(IngredientComponent.ITEMSTACK, (List) items);
×
425
        }
426
        if (!fluids.isEmpty()) {
×
427
            inputs.put(IngredientComponent.FLUIDSTACK, (List) fluids);
×
428
        }
429
        if (!energies.isEmpty()) {
×
430
            inputs.put(IngredientComponent.ENERGY, (List) energies);
×
431
        }
432

433
        return inputs;
×
434
    }
435

436
    protected Map<IngredientComponent<?, ?>, List<Boolean>> getInputsReusable(List<ItemMatchProperties> itemStacks) {
437
        Map<IngredientComponent<?, ?>, List<Boolean>> inputs = Maps.newIdentityHashMap();
×
438

439
        List<Boolean> items = itemStacks.stream()
×
440
                .map(ItemMatchProperties::isReusable)
×
441
                .collect(Collectors.toList());
×
442
        if (!items.isEmpty()) {
×
443
            inputs.put(IngredientComponent.ITEMSTACK, (List) items);
×
444
        }
445

446
        return inputs;
×
447
    }
448

449
    protected Map<IngredientComponent<?, ?>, List<?>> getOutputs(List<ItemStack> itemStacksIn,
450
                                                                 ItemStack fluid, int fluidAmount,
451
                                                                 long energy) {
452
        // Cut of itemStacks list until last non-empty stack
453
        List<ItemStack> itemStacks = Lists.newArrayList();
×
454
        for (int i = 0; i < itemStacksIn.size(); i++) {
×
455
            if (!itemStacksIn.get(i).isEmpty()) {
×
456
                itemStacks.add(itemStacksIn.get(i));
×
457
            }
458
        }
459

460
        // Override fluid amount
461
        FluidStack fluidStack = Helpers.getFluidStack(fluid);
×
462
        if (!fluidStack.isEmpty()) {
×
463
            fluidStack.setAmount(fluidAmount);
×
464
        }
465

466
        Map<IngredientComponent<?, ?>, List<?>> outputs = Maps.newIdentityHashMap();
×
467
        if (!itemStacks.isEmpty()) {
×
468
            outputs.put(IngredientComponent.ITEMSTACK, itemStacks);
×
469
        }
470
        if (!fluidStack.isEmpty()) {
×
471
            outputs.put(IngredientComponent.FLUIDSTACK, Collections.singletonList(fluidStack));
×
472
        }
473
        if (energy > 0) {
×
474
            outputs.put(IngredientComponent.ENERGY, Collections.singletonList(energy));
×
475
        }
476

477
        return outputs;
×
478
    }
479

480
    @Override
481
    public IValue getValue() {
482
        if (!isInputValid() && !isOutputValid()) {
×
483
            return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
484
        }
485
        return ValueObjectTypeRecipe.ValueRecipe.of(
×
486
                new RecipeDefinition(getInputs(this.inputStacks, this.inputFluid,
×
487
                        Integer.parseInt(this.inputFluidAmount), Long.parseLong(this.inputEnergy)),
×
488
                getInputsReusable(this.inputStacks),
×
489
                new MixedIngredients(getOutputs(this.outputStacks, this.outputFluid,
×
490
                        Integer.parseInt(this.outputFluidAmount), Long.parseLong(this.outputEnergy)))));
×
491
    }
492

493
    @Override
494
    public void setValue(IValue value) {
495
        ValueObjectTypeRecipe.ValueRecipe valueRecipe = (ValueObjectTypeRecipe.ValueRecipe) value;
×
496
        valueRecipe.getRawValue().ifPresent(recipe -> {
×
497
            loadInputStacks(recipe);
×
498
            loadInputFluid(recipe);
×
499
            loadInputEnergy(recipe);
×
500

501
            loadOutputStacks(recipe);
×
502
            loadOutputFluid(recipe);
×
503
            loadOutputEnergy(recipe);
×
504
        });
×
505
    }
×
506

507
    private void loadInputStacks(IRecipeDefinition recipe) {
508
        List<IPrototypedIngredientAlternatives<ItemStack, Integer>> listAlternatives = recipe.getInputs(IngredientComponent.ITEMSTACK);
×
509
        for (int i = 0; i < listAlternatives.size(); i++) {
×
510
            IPrototypedIngredientAlternatives<ItemStack, Integer> prototypes = listAlternatives.get(i);
×
511
            boolean reusable = recipe.isInputReusable(IngredientComponent.ITEMSTACK, i);
×
512
            ItemMatchProperties itemMatchProperties = ItemMatchProperties.fromPrototypedIngredient(prototypes, reusable);
×
513
            this.inputStacks.set(i, itemMatchProperties);
×
514
        }
515
    }
×
516

517
    private <T, M> Optional<T> loadFirstInput(IRecipeDefinition recipe, IngredientComponent<T, M> ingredientComponent) {
518
        List<IPrototypedIngredientAlternatives<T, M>> listAlternatives = recipe.getInputs(ingredientComponent);
×
519
        if (listAlternatives.size() > 0) {
×
520
            IPrototypedIngredientAlternatives<T, M> prototypes = listAlternatives.get(0);
×
521
            if (prototypes.getAlternatives().size() > 0) {
×
522
                IPrototypedIngredient<T, M> prototype = prototypes.getAlternatives().stream().findFirst().get();
×
523
                return Optional.of(prototype.getPrototype());
×
524
            }
525
        }
526
        return Optional.empty();
×
527
    }
528

529
    private void loadInputFluid(IRecipeDefinition recipe) {
530
        loadFirstInput(recipe, IngredientComponent.FLUIDSTACK).ifPresent(fluidStack -> {
×
531
            this.inputFluid = fluidStack.getFluid().getFluidType().getBucket(fluidStack);
×
532
            this.inputFluidAmount = Integer.toString(fluidStack.getAmount());
×
533
        });
×
534
    }
×
535

536
    private void loadInputEnergy(IRecipeDefinition recipe) {
537
        loadFirstInput(recipe, IngredientComponent.ENERGY).ifPresent(energy -> this.inputEnergy = Long.toString(energy));
×
538
    }
×
539

540
    private void loadOutputStacks(IRecipeDefinition recipe) {
541
        List<ItemStack> instances = recipe.getOutput().getInstances(IngredientComponent.ITEMSTACK);
×
542
        if (instances.size() > 0) {
×
543
            outputStacks.set(0, instances.get(0));
×
544
        }
545
        if (instances.size() > 1) {
×
546
            outputStacks.set(1, instances.get(1));
×
547
        }
548
        if (instances.size() > 2) {
×
549
            outputStacks.set(2, instances.get(2));
×
550
        }
551
    }
×
552

553
    private <T, M> Optional<T> loadFirstOutput(IRecipeDefinition recipe, IngredientComponent<T, M> ingredientComponent) {
554
        List<T> instances = recipe.getOutput().getInstances(ingredientComponent);
×
555
        if (instances.size() > 0) {
×
556
            return Optional.of(instances.get(0));
×
557
        }
558
        return Optional.empty();
×
559
    }
560

561
    private void loadOutputFluid(IRecipeDefinition recipe) {
562
        loadFirstOutput(recipe, IngredientComponent.FLUIDSTACK).ifPresent(fluidStack -> {
×
563
            this.outputFluid = fluidStack.getFluid().getFluidType().getBucket(fluidStack);
×
564
            this.outputFluidAmount = Integer.toString(fluidStack.getAmount());
×
565
        });
×
566
    }
×
567

568
    private void loadOutputEnergy(IRecipeDefinition recipe) {
569
        loadFirstOutput(recipe, IngredientComponent.ENERGY).ifPresent(energy -> this.outputEnergy = Long.toString(energy));
×
570
    }
×
571

572
    @Override
573
    public void setValueInContainer(ContainerLogicProgrammerBase container) {
574
        Container slots = container.getTemporaryInputSlots();
×
575

576
        // Input slots
577
        for (int i = 0; i < this.inputStacks.size(); i++) {
×
578
            ItemMatchProperties entry = this.inputStacks.get(i);
×
579
            slots.setItem(i, entry.getItemStack());
×
580
        }
581
        slots.setItem(9, this.inputFluid);
×
582

583
        // Output slots
584
        for (int i = 0; i < this.outputStacks.size(); i++) {
×
585
            slots.setItem(10 + i, this.outputStacks.get(i));
×
586
            // No need to set slot type, as this can't be changed for output stacks
587
        }
588
        slots.setItem(13, this.outputFluid);
×
589
    }
×
590

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