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

CyclopsMC / IntegratedCrafting / #479011729

10 Jun 2024 03:02PM UTC coverage: 25.044% (+0.1%) from 24.947%
#479011729

push

github

rubensworks
Update to NeoForge 1.20.4

0 of 52 new or added lines in 14 files covered. (0.0%)

3 existing lines in 3 files now uncovered.

706 of 2819 relevant lines covered (25.04%)

0.25 hits per line

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

0.0
/src/main/java/org/cyclops/integratedcrafting/part/PartTypeInterfaceCrafting.java
1
package org.cyclops.integratedcrafting.part;
2

3
import com.google.common.collect.Lists;
4
import com.google.common.collect.MapMaker;
5
import it.unimi.dsi.fastutil.ints.Int2BooleanArrayMap;
6
import it.unimi.dsi.fastutil.ints.Int2BooleanMap;
7
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
8
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
9
import it.unimi.dsi.fastutil.ints.IntArraySet;
10
import it.unimi.dsi.fastutil.ints.IntSet;
11
import net.minecraft.core.Direction;
12
import net.minecraft.nbt.CompoundTag;
13
import net.minecraft.nbt.ListTag;
14
import net.minecraft.nbt.Tag;
15
import net.minecraft.network.FriendlyByteBuf;
16
import net.minecraft.network.chat.Component;
17
import net.minecraft.network.chat.MutableComponent;
18
import net.minecraft.resources.ResourceLocation;
19
import net.minecraft.server.level.ServerPlayer;
20
import net.minecraft.world.MenuProvider;
21
import net.minecraft.world.SimpleContainer;
22
import net.minecraft.world.entity.player.Inventory;
23
import net.minecraft.world.entity.player.Player;
24
import net.minecraft.world.inventory.AbstractContainerMenu;
25
import net.minecraft.world.item.ItemStack;
26
import net.neoforged.neoforge.common.NeoForge;
27
import org.apache.commons.lang3.tuple.Triple;
28
import org.apache.logging.log4j.Level;
29
import org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition;
30
import org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeHandler;
31
import org.cyclops.commoncapabilities.api.ingredient.IIngredientMatcher;
32
import org.cyclops.commoncapabilities.api.ingredient.IMixedIngredients;
33
import org.cyclops.commoncapabilities.api.ingredient.IPrototypedIngredient;
34
import org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;
35
import org.cyclops.commoncapabilities.api.ingredient.IngredientInstanceWrapper;
36
import org.cyclops.commoncapabilities.api.ingredient.MixedIngredients;
37
import org.cyclops.commoncapabilities.api.ingredient.storage.IIngredientComponentStorage;
38
import org.cyclops.cyclopscore.datastructure.DimPos;
39
import org.cyclops.cyclopscore.helper.BlockEntityHelpers;
40
import org.cyclops.cyclopscore.inventory.SimpleInventory;
41
import org.cyclops.cyclopscore.persist.nbt.NBTClassType;
42
import org.cyclops.integratedcrafting.Capabilities;
43
import org.cyclops.integratedcrafting.GeneralConfig;
44
import org.cyclops.integratedcrafting.IntegratedCrafting;
45
import org.cyclops.integratedcrafting.api.crafting.CraftingJob;
46
import org.cyclops.integratedcrafting.api.crafting.CraftingJobStatus;
47
import org.cyclops.integratedcrafting.api.crafting.ICraftingInterface;
48
import org.cyclops.integratedcrafting.api.crafting.ICraftingResultsSink;
49
import org.cyclops.integratedcrafting.api.network.ICraftingNetwork;
50
import org.cyclops.integratedcrafting.core.CraftingHelpers;
51
import org.cyclops.integratedcrafting.core.CraftingJobHandler;
52
import org.cyclops.integratedcrafting.core.CraftingProcessOverrides;
53
import org.cyclops.integratedcrafting.core.part.PartTypeCraftingBase;
54
import org.cyclops.integratedcrafting.ingredient.storage.IngredientComponentStorageSlottedInsertProxy;
55
import org.cyclops.integratedcrafting.inventory.container.ContainerPartInterfaceCrafting;
56
import org.cyclops.integratedcrafting.inventory.container.ContainerPartInterfaceCraftingSettings;
57
import org.cyclops.integrateddynamics.api.evaluate.EvaluationException;
58
import org.cyclops.integrateddynamics.api.evaluate.variable.IValue;
59
import org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;
60
import org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;
61
import org.cyclops.integrateddynamics.api.network.INetwork;
62
import org.cyclops.integrateddynamics.api.network.INetworkIngredientsChannel;
63
import org.cyclops.integrateddynamics.api.network.IPartNetwork;
64
import org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetworkIngredients;
65
import org.cyclops.integrateddynamics.api.network.NetworkCapability;
66
import org.cyclops.integrateddynamics.api.part.IPartContainer;
67
import org.cyclops.integrateddynamics.api.part.PartCapability;
68
import org.cyclops.integrateddynamics.api.part.PartPos;
69
import org.cyclops.integrateddynamics.api.part.PartTarget;
70
import org.cyclops.integrateddynamics.api.part.PrioritizedPartPos;
71
import org.cyclops.integrateddynamics.core.evaluate.InventoryVariableEvaluator;
72
import org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeRecipe;
73
import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;
74
import org.cyclops.integrateddynamics.core.helper.NetworkHelpers;
75
import org.cyclops.integrateddynamics.core.helper.PartHelpers;
76
import org.cyclops.integrateddynamics.core.part.PartStateBase;
77
import org.cyclops.integrateddynamics.core.part.PartTypeBase;
78
import org.cyclops.integrateddynamics.core.part.event.PartVariableDrivenVariableContentsUpdatedEvent;
79

80
import javax.annotation.Nullable;
81
import java.util.Collection;
82
import java.util.Iterator;
83
import java.util.List;
84
import java.util.ListIterator;
85
import java.util.Map;
86
import java.util.Optional;
87

88
/**
89
 * Interface for item handlers.
90
 * @author rubensworks
91
 */
92
public class PartTypeInterfaceCrafting extends PartTypeCraftingBase<PartTypeInterfaceCrafting, PartTypeInterfaceCrafting.State> {
93

94
    public PartTypeInterfaceCrafting(String name) {
95
        super(name);
×
96
    }
×
97

98
    @Override
99
    public int getConsumptionRate(State state) {
100
        return state.getCraftingJobHandler().getProcessingCraftingJobs().size() * GeneralConfig.interfaceCraftingBaseConsumption;
×
101
    }
102

103
    @Override
104
    public Optional<MenuProvider> getContainerProvider(PartPos pos) {
105
        return Optional.of(new MenuProvider() {
×
106

107
            @Override
108
            public MutableComponent getDisplayName() {
109
                return Component.translatable(getTranslationKey());
×
110
            }
111

112
            @Override
113
            public AbstractContainerMenu createMenu(int id, Inventory playerInventory, Player playerEntity) {
114
                Triple<IPartContainer, PartTypeBase, PartTarget> data = PartHelpers.getContainerPartConstructionData(pos);
×
115
                PartTypeInterfaceCrafting.State partState = (PartTypeInterfaceCrafting.State) data.getLeft().getPartState(data.getRight().getCenter().getSide());
×
116
                return new ContainerPartInterfaceCrafting(id, playerInventory, partState.getInventoryVariables(),
×
117
                        Optional.of(data.getRight()), Optional.of(data.getLeft()), (PartTypeInterfaceCrafting) data.getMiddle());
×
118
            }
119
        });
120
    }
121

122
    @Override
123
    public void writeExtraGuiData(FriendlyByteBuf packetBuffer, PartPos pos, ServerPlayer player) {
124
        // Write inventory size
125
        IPartContainer partContainer = PartHelpers.getPartContainerChecked(pos);
×
126
        PartTypeInterfaceCrafting.State partState = (PartTypeInterfaceCrafting.State) partContainer.getPartState(pos.getSide());
×
127
        packetBuffer.writeInt(partState.getInventoryVariables().getContainerSize());
×
128

129
        super.writeExtraGuiData(packetBuffer, pos, player);
×
130
    }
×
131

132
    @Override
133
    public Optional<MenuProvider> getContainerProviderSettings(PartPos pos) {
134
        return Optional.of(new MenuProvider() {
×
135

136
            @Override
137
            public MutableComponent getDisplayName() {
138
                return Component.translatable(getTranslationKey());
×
139
            }
140

141
            @Override
142
            public AbstractContainerMenu createMenu(int id, Inventory playerInventory, Player playerEntity) {
143
                Triple<IPartContainer, PartTypeBase, PartTarget> data = PartHelpers.getContainerPartConstructionData(pos);
×
144
                return new ContainerPartInterfaceCraftingSettings(id, playerInventory, new SimpleContainer(0),
×
145
                        data.getRight(), Optional.of(data.getLeft()), data.getMiddle());
×
146
            }
147
        });
148
    }
149

150
    @Override
151
    protected PartTypeInterfaceCrafting.State constructDefaultState() {
152
        return new PartTypeInterfaceCrafting.State();
×
153
    }
154

155
    @Override
156
    public void afterNetworkReAlive(INetwork network, IPartNetwork partNetwork, PartTarget target, PartTypeInterfaceCrafting.State state) {
157
        super.afterNetworkReAlive(network, partNetwork, target, state);
×
158
        addTargetToNetwork(network, target, state);
×
159
    }
×
160

161
    @Override
162
    public void onNetworkRemoval(INetwork network, IPartNetwork partNetwork, PartTarget target, PartTypeInterfaceCrafting.State state) {
163
        super.onNetworkRemoval(network, partNetwork, target, state);
×
164
        removeTargetFromNetwork(network, target.getTarget(), state);
×
165
    }
×
166

167
    @Override
168
    public void onNetworkAddition(INetwork network, IPartNetwork partNetwork, PartTarget target, PartTypeInterfaceCrafting.State state) {
169
        super.onNetworkAddition(network, partNetwork, target, state);
×
170
        addTargetToNetwork(network, target, state);
×
171
    }
×
172

173
    @Override
174
    public void setPriorityAndChannel(INetwork network, IPartNetwork partNetwork, PartTarget target, PartTypeInterfaceCrafting.State state, int priority, int channel) {
175
        // We need to do this because the crafting network is not automagically aware of the priority changes,
176
        // so we have to re-add it.
177
        removeTargetFromNetwork(network, target.getTarget(), state);
×
178
        super.setPriorityAndChannel(network, partNetwork, target, state, priority, channel);
×
179
        addTargetToNetwork(network, target, state);
×
180
    }
×
181

182
    protected NetworkCapability<ICraftingNetwork> getNetworkCapability() {
NEW
183
        return Capabilities.CraftingNetwork.NETWORK;
×
184
    }
185

186
    protected void addTargetToNetwork(INetwork network, PartTarget pos, PartTypeInterfaceCrafting.State state) {
187
        network.getCapability(getNetworkCapability())
×
188
                .ifPresent(craftingNetwork -> {
×
189
                    int channelCrafting = state.getChannelCrafting();
×
190
                    state.setTarget(pos);
×
191
                    state.setNetworks(network, craftingNetwork, NetworkHelpers.getPartNetworkChecked(network), channelCrafting, ValueDeseralizationContext.of(pos.getCenter().getPos().getLevel(true)));
×
192
                    state.setShouldAddToCraftingNetwork(true);
×
193
                });
×
194
    }
×
195

196
    protected void removeTargetFromNetwork(INetwork network, PartPos pos, PartTypeInterfaceCrafting.State state) {
197
        ICraftingNetwork craftingNetwork = state.getCraftingNetwork();
×
198
        if (craftingNetwork != null) {
×
199
            network.getCapability(getNetworkCapability())
×
200
                    .ifPresent(n -> n.removeCraftingInterface(state.getChannelCrafting(), state));
×
201
        }
202
        state.setNetworks(null, null, null, -1, null);
×
203
        state.setTarget(null);
×
204
    }
×
205

206
    @Override
207
    public boolean isUpdate(State state) {
208
        return true;
×
209
    }
210

211
    @Override
212
    public int getMinimumUpdateInterval(State state) {
213
        return state.getDefaultUpdateInterval();
×
214
    }
215

216
    @Override
217
    public void update(INetwork network, IPartNetwork partNetwork, PartTarget target, State state) {
218
        super.update(network, partNetwork, target, state);
×
219

220
        // Init network data in part state if it has not been done yet.
221
        // This can occur when the part chunk is being reloaded.
222
        if (state.getCraftingNetwork() == null) {
×
223
            addTargetToNetwork(network, target, state);
×
224
        }
225

226
        int channel = state.getChannelCrafting();
×
227

228
        // Update the network data in the part state
229
        if (state.shouldAddToCraftingNetwork()) {
×
230
            ICraftingNetwork craftingNetwork = network.getCapability(getNetworkCapability()).orElse(null);
×
231
            craftingNetwork.addCraftingInterface(channel, state);
×
232
            state.setShouldAddToCraftingNetwork(false);
×
233
        }
234

235
        // Push any pending output ingredients into the network
236
        state.flushInventoryOutputBuffer(network);
×
237

238
        // Block job ticking if there still are outputs in our crafting result buffer.
239
        if (state.getInventoryOutputBuffer().isEmpty()) {
×
240
            // Tick the job handler
241
            PartPos targetPos = state.getTarget().getTarget();
×
242
            state.getCraftingJobHandler().update(network, channel, targetPos);
×
243
        }
244

245
        // Reload recipes if needed
246
        IntSet slots = state.getDelayedRecipeReloads();
×
247
        if (!slots.isEmpty()) {
×
248
            ICraftingNetwork craftingNetwork = network.getCapability(getNetworkCapability()).orElse(null);
×
249
            if (craftingNetwork != null) {
×
250
                for (Integer slot : slots) {
×
251
                    // Remove the old recipe from the network
252
                    Int2ObjectMap<IRecipeDefinition> recipes = state.getRecipesIndexed();
×
253
                    IRecipeDefinition oldRecipe = recipes.get(slot);
×
254
                    if (oldRecipe != null) {
×
255
                        craftingNetwork.removeCraftingInterfaceRecipe(channel, state, oldRecipe);
×
256
                    }
257

258
                    // Reload the recipe in the slot
259
                    state.reloadRecipe(slot);
×
260

261
                    // Add the new recipe to the network
262
                    IRecipeDefinition newRecipe = recipes.get(slot);
×
263
                    if (newRecipe != null) {
×
264
                        craftingNetwork.addCraftingInterfaceRecipe(channel, state, newRecipe);
×
265
                    }
266
                }
×
267
            }
268
            slots.clear();
×
269
        }
270
    }
×
271

272
    @Nullable
273
    protected static <T, M> IngredientInstanceWrapper<T, M> insertIntoNetwork(IngredientInstanceWrapper<T, M> wrapper,
274
                                                                              INetwork network, int channel) {
275
        IPositionedAddonsNetworkIngredients<T, M> storageNetwork = wrapper.getComponent()
×
NEW
276
                .getCapability(org.cyclops.integrateddynamics.Capabilities.PositionedAddonsNetworkIngredientsHandler.INGREDIENT)
×
277
                .map(n -> (IPositionedAddonsNetworkIngredients<T, M>) n.getStorage(network).orElse(null))
×
278
                .orElse(null);
×
279
        if (storageNetwork != null) {
×
280
            IIngredientComponentStorage<T, M> storage = storageNetwork.getChannel(channel);
×
281
            T remaining = storage.insert(wrapper.getInstance(), false);
×
282
            if (wrapper.getComponent().getMatcher().isEmpty(remaining)) {
×
283
                return null;
×
284
            } else {
285
                return new IngredientInstanceWrapper<>(wrapper.getComponent(), remaining);
×
286
            }
287
        }
288
        return wrapper;
×
289
    }
290

291
    @Override
292
    public void addDrops(PartTarget target, State state, List<ItemStack> itemStacks, boolean dropMainElement, boolean saveState) {
293
        // Drop any remaining output ingredients (only items)
294
        for (IngredientInstanceWrapper<?, ?> ingredientInstanceWrapper : state.getInventoryOutputBuffer()) {
×
295
            if (ingredientInstanceWrapper.getComponent() == IngredientComponent.ITEMSTACK) {
×
296
                itemStacks.add((ItemStack) ingredientInstanceWrapper.getInstance());
×
297
            }
298
        }
×
299
        state.getInventoryOutputBuffer().clear();
×
300

301
        // Drop the stored variables
302
        for(int i = 0; i < state.getInventoryVariables().getContainerSize(); i++) {
×
303
            ItemStack itemStack = state.getInventoryVariables().getItem(i);
×
304
            if(!itemStack.isEmpty()) {
×
305
                itemStacks.add(itemStack);
×
306
            }
307
        }
308
        state.getInventoryVariables().clearContent();
×
309

310
        super.addDrops(target, state, itemStacks, dropMainElement, saveState);
×
311
    }
×
312

313
    public static class State extends PartStateBase<PartTypeInterfaceCrafting>
314
            implements ICraftingInterface, ICraftingResultsSink {
315

316
        private final CraftingJobHandler craftingJobHandler;
317
        private final SimpleInventory inventoryVariables;
318
        private final List<InventoryVariableEvaluator<ValueObjectTypeRecipe.ValueRecipe>> variableEvaluators;
319
        private final List<IngredientInstanceWrapper<?, ?>> inventoryOutputBuffer;
320
        private final Int2ObjectMap<MutableComponent> recipeSlotMessages;
321
        private final Int2BooleanMap recipeSlotValidated;
322
        private final IntSet delayedRecipeReloads;
323
        private final Map<IVariable, Boolean> variableListeners;
324
        private int channelCrafting = 0;
×
325
        private boolean disableCraftingCheck = false;
×
326

327
        private final Int2ObjectMap<IRecipeDefinition> currentRecipes;
328
        private PartTarget target = null;
×
329
        private INetwork network = null;
×
330
        private ICraftingNetwork craftingNetwork = null;
×
331
        private IPartNetwork partNetwork = null;
×
332
        private int channel = -1;
×
333
        private ValueDeseralizationContext valueDeseralizationContext;
334
        private boolean shouldAddToCraftingNetwork = false;
×
335
        private Player lastPlayer;
336

337
        public State() {
×
338
            this.craftingJobHandler = new CraftingJobHandler(1, true,
×
339
                    CraftingProcessOverrides.REGISTRY.getCraftingProcessOverrides(), this);
×
340
            this.inventoryVariables = new SimpleInventory(9, 1);
×
341
            this.inventoryVariables.addDirtyMarkListener(this);
×
342
            this.variableEvaluators = Lists.newArrayList();
×
343
            this.inventoryOutputBuffer = Lists.newArrayList();
×
344
            this.recipeSlotMessages = new Int2ObjectArrayMap<>();
×
345
            this.recipeSlotValidated = new Int2BooleanArrayMap();
×
346
            this.delayedRecipeReloads = new IntArraySet();
×
347
            this.variableListeners = new MapMaker().weakKeys().makeMap();
×
348
            this.currentRecipes = new Int2ObjectArrayMap<>();
×
349
        }
×
350

351
        @Override
352
        protected int getDefaultUpdateInterval() {
353
            return GeneralConfig.minCraftingInterfaceUpdateFreq;
×
354
        }
355

356
        /**
357
         * @return The inner variables inventory
358
         */
359
        public SimpleInventory getInventoryVariables() {
360
            return this.inventoryVariables;
×
361
        }
362

363
        @Override
364
        public void writeToNBT(CompoundTag tag) {
365
            super.writeToNBT(tag);
×
366
            inventoryVariables.writeToNBT(tag, "variables");
×
367

368
            ListTag instanceTags = new ListTag();
×
369
            for (IngredientInstanceWrapper instanceWrapper : inventoryOutputBuffer) {
×
370
                CompoundTag instanceTag = new CompoundTag();
×
371
                instanceTag.putString("component", IngredientComponent.REGISTRY.getKey(instanceWrapper.getComponent()).toString());
×
372
                instanceTag.put("instance", instanceWrapper.getComponent().getSerializer().serializeInstance(instanceWrapper.getInstance()));
×
373
                instanceTags.add(instanceTag);
×
374
            }
×
375
            tag.put("inventoryOutputBuffer", instanceTags);
×
376

377
            this.craftingJobHandler.writeToNBT(tag);
×
378
            tag.putInt("channelCrafting", channelCrafting);
×
379

380
            CompoundTag recipeSlotErrorsTag = new CompoundTag();
×
381
            for (Int2ObjectMap.Entry<MutableComponent> entry : this.recipeSlotMessages.int2ObjectEntrySet()) {
×
382
                NBTClassType.writeNbt(MutableComponent.class, String.valueOf(entry.getIntKey()), entry.getValue(), recipeSlotErrorsTag);
×
383
            }
×
384
            tag.put("recipeSlotMessages", recipeSlotErrorsTag);
×
385

386
            CompoundTag recipeSlotValidatedTag = new CompoundTag();
×
387
            for (Int2BooleanMap.Entry entry : this.recipeSlotValidated.int2BooleanEntrySet()) {
×
388
                recipeSlotValidatedTag.putBoolean(String.valueOf(entry.getIntKey()), entry.getBooleanValue());
×
389
            }
×
390
            tag.put("recipeSlotValidated", recipeSlotValidatedTag);
×
391

392
            tag.putBoolean("disableCraftingCheck", disableCraftingCheck);
×
393
        }
×
394

395
        @Override
396
        public void readFromNBT(ValueDeseralizationContext valueDeseralizationContext, CompoundTag tag) {
397
            super.readFromNBT(valueDeseralizationContext, tag);
×
398
            inventoryVariables.readFromNBT(tag, "variables");
×
399

400
            this.inventoryOutputBuffer.clear();
×
401
            for (Tag instanceTagRaw : tag.getList("inventoryOutputBuffer", Tag.TAG_COMPOUND)) {
×
402
                CompoundTag instanceTag = (CompoundTag) instanceTagRaw;
×
403
                String componentName = instanceTag.getString("component");
×
NEW
404
                IngredientComponent<?, ?> component = IngredientComponent.REGISTRY.get(new ResourceLocation(componentName));
×
405
                this.inventoryOutputBuffer.add(new IngredientInstanceWrapper(component,
×
406
                        component.getSerializer().deserializeInstance(instanceTag.get("instance"))));
×
407
            }
×
408

409
            this.craftingJobHandler.readFromNBT(tag);
×
410
            this.channelCrafting = tag.getInt("channelCrafting");
×
411

412
            this.recipeSlotMessages.clear();
×
413
            CompoundTag recipeSlotErrorsTag = tag.getCompound("recipeSlotMessages");
×
414
            for (String slot : recipeSlotErrorsTag.getAllKeys()) {
×
415
                MutableComponent unlocalizedString = NBTClassType.readNbt(MutableComponent.class, slot, recipeSlotErrorsTag);
×
416
                this.recipeSlotMessages.put(Integer.parseInt(slot), unlocalizedString);
×
417
            }
×
418

419
            this.recipeSlotValidated.clear();
×
420
            CompoundTag recipeSlotValidatedTag = tag.getCompound("recipeSlotValidated");
×
421
            for (String slot : recipeSlotValidatedTag.getAllKeys()) {
×
422
                this.recipeSlotValidated.put(Integer.parseInt(slot), recipeSlotValidatedTag.getBoolean(slot));
×
423
            }
×
424

425
            this.disableCraftingCheck = tag.getBoolean("disableCraftingCheck");
×
426
        }
×
427

428
        public void setChannelCrafting(int channelCrafting) {
429
            if (this.channelCrafting != channelCrafting) {
×
430
                // Unregister from the network
431
                if (craftingNetwork != null) {
×
432
                    craftingNetwork.removeCraftingInterface(this.channelCrafting, this);
×
433
                }
434

435
                // Update the channel
436
                this.channelCrafting = channelCrafting;
×
437

438
                // Re-register to the network
439
                if (craftingNetwork != null) {
×
440
                    craftingNetwork.addCraftingInterface(this.channelCrafting, this);
×
441
                }
442

443
                sendUpdate();
×
444
            }
445
        }
×
446

447
        public int getChannelCrafting() {
448
            return channelCrafting;
×
449
        }
450

451
        public void reloadRecipes() {
452
            this.currentRecipes.clear();
×
453
            this.recipeSlotMessages.clear();
×
454
            this.recipeSlotValidated.clear();
×
455
            variableEvaluators.clear();
×
456
            for (int i = 0; i < getInventoryVariables().getContainerSize(); i++) {
×
457
                int slot = i;
×
458
                variableEvaluators.add(new InventoryVariableEvaluator<ValueObjectTypeRecipe.ValueRecipe>(
×
459
                        getInventoryVariables(), slot, valueDeseralizationContext, ValueTypes.OBJECT_RECIPE) {
×
460
                    @Override
461
                    public void onErrorsChanged() {
462
                        super.onErrorsChanged();
×
463
                        setLocalErrors(slot, getErrors());
×
464
                    }
×
465
                });
466
            }
467
            if (this.partNetwork != null) {
×
468
                for (int i = 0; i < getInventoryVariables().getContainerSize(); i++) {
×
469
                    reloadRecipe(i);
×
470
                }
471
            }
472
        }
×
473

474
        private void setLocalErrors(int slot, List<MutableComponent> errors) {
475
            if (errors.isEmpty()) {
×
476
                if (this.recipeSlotMessages.size() > slot) {
×
477
                    this.recipeSlotMessages.remove(slot);
×
478
                }
479
            } else {
480
                this.recipeSlotMessages.put(slot, errors.get(0));
×
481
            }
482
        }
×
483

484
        protected void reloadRecipe(int slot) {
485
            this.currentRecipes.remove(slot);
×
486
            if (this.recipeSlotMessages.size() > slot) {
×
487
                this.recipeSlotMessages.remove(slot);
×
488
            }
489
            if (this.recipeSlotValidated.size() > slot) {
×
490
                this.recipeSlotValidated.remove(slot);
×
491
            }
492
            if (this.partNetwork != null) {
×
493
                InventoryVariableEvaluator<ValueObjectTypeRecipe.ValueRecipe> evaluator = variableEvaluators.get(slot);
×
494
                evaluator.refreshVariable(network, false);
×
495
                IVariable<ValueObjectTypeRecipe.ValueRecipe> variable = evaluator.getVariable(network);
×
496
                if (variable != null) {
×
497
                    try {
498
                        // Refresh the recipe if variable is changed
499
                        // The map is needed because we only want to register the listener once for each variable
500
                        if (!this.variableListeners.containsKey(variable)) {
×
501
                            variable.addInvalidationListener(() -> {
×
502
                                this.variableListeners.remove(variable);
×
503
                                delayedReloadRecipe(slot);
×
504
                            });
×
505
                            this.variableListeners.put(variable, true);
×
506
                        }
507

508
                        IValue value = variable.getValue();
×
509
                        if (value.getType() == ValueTypes.OBJECT_RECIPE) {
×
510
                            Optional<IRecipeDefinition> recipeWrapper = ((ValueObjectTypeRecipe.ValueRecipe) value).getRawValue();
×
511
                            if (recipeWrapper.isPresent()) {
×
512
                                IRecipeDefinition recipe = recipeWrapper.get();
×
513
                                if (!GeneralConfig.validateRecipesCraftingInterface || this.disableCraftingCheck || isValid(recipe)) {
×
514
                                    this.currentRecipes.put(slot, recipe);
×
515
                                    this.recipeSlotValidated.put(slot, true);
×
516
                                    this.recipeSlotMessages.put(slot, Component.translatable("gui.integratedcrafting.partinterface.slot.message.valid"));
×
517
                                } else {
518
                                    this.recipeSlotMessages.put(slot, Component.translatable("gui.integratedcrafting.partinterface.slot.message.invalid"));
×
519
                                }
520
                            }
521
                        } else {
×
522
                            this.recipeSlotMessages.put(slot, Component.translatable("gui.integratedcrafting.partinterface.slot.message.norecipe"));
×
523
                        }
524
                    } catch (EvaluationException e) {
×
525
                        this.recipeSlotMessages.put(slot, e.getErrorMessage());
×
526
                    }
×
527
                } else {
528
                    this.recipeSlotMessages.put(slot, Component.translatable("gui.integratedcrafting.partinterface.slot.message.norecipe"));
×
529
                }
530

531
                try {
532
                    IPartNetwork partNetwork = NetworkHelpers.getPartNetworkChecked(network);
×
NEW
533
                    NeoForge.EVENT_BUS.post(new PartVariableDrivenVariableContentsUpdatedEvent<>(network,
×
534
                            partNetwork, getTarget(),
×
535
                            PartTypes.INTERFACE_CRAFTING, this, lastPlayer, variable,
536
                            variable != null ? variable.getValue() : null));
×
537
                } catch (EvaluationException e) {
×
538
                    // Ignore error
539
                }
×
540
            }
541
            sendUpdate();
×
542
        }
×
543

544
        public void setLastPlayer(Player lastPlayer) {
545
            this.lastPlayer = lastPlayer;
×
546
        }
×
547

548
        private void delayedReloadRecipe(int slot) {
549
            this.delayedRecipeReloads.add(slot);
×
550
        }
×
551

552

553
        private boolean isValid(IRecipeDefinition recipe) {
554
            DimPos dimPos = getTarget().getTarget().getPos();
×
555
            Direction side = getTarget().getTarget().getSide();
×
NEW
556
            IRecipeHandler recipeHandler = BlockEntityHelpers.getCapability(dimPos.getLevel(true), dimPos.getBlockPos(), side, org.cyclops.commoncapabilities.api.capability.Capabilities.RecipeHandler.BLOCK).orElse(null);
×
557
            if (recipeHandler != null) {
×
558
                IMixedIngredients simulatedOutput = recipeHandler.simulate(MixedIngredients.fromRecipeInput(recipe));
×
559
                if (simulatedOutput != null && !simulatedOutput.isEmpty()) {
×
560
                    if (recipe.getOutput().containsAll(simulatedOutput)) {
×
561
                        return true;
×
562
                    } else {
563
                        if (GeneralConfig.logRecipeValidationFailures) {
×
564
                            IntegratedCrafting.clog(Level.INFO, "Recipe validation failure: incompatible recipe output and simulated output:\nRecipe output: " + recipe.getOutput() + "\nSimulated output: " + simulatedOutput);
×
565
                        }
566
                        return false;
×
567
                    }
568
                }
569
                if (GeneralConfig.logRecipeValidationFailures) {
×
570
                    IntegratedCrafting.clog(Level.INFO, "Recipe validation failure: No output was obtained when simulating a recipe\n" + recipe);
×
571
                }
572
                return false;
×
573
            }
574
            return true; // No recipe handler capability is present, so we can't confirm that the recipe will work.
×
575
        }
576

577
        @Override
578
        public void onDirty() {
579
            super.onDirty();
×
580

581
            // Unregister from the network, when all old recipes are still in place
582
            if (craftingNetwork != null) {
×
583
                craftingNetwork.removeCraftingInterface(channelCrafting, this);
×
584
            }
585

586
            // Recalculate recipes
587
            if (getTarget() != null && !getTarget().getCenter().getPos().getLevel(true).isClientSide) {
×
588
                reloadRecipes();
×
589
            }
590

591
            // Re-register to the network, to force an update for all new recipes
592
            if (craftingNetwork != null) {
×
593
                craftingNetwork.addCraftingInterface(channelCrafting, this);
×
594
            }
595
        }
×
596

597
        public void setTarget(PartTarget target) {
598
            this.target = target;
×
599
        }
×
600

601
        public PartTarget getTarget() {
602
            return target;
×
603
        }
604

605
        public void setNetworks(@Nullable INetwork network, @Nullable ICraftingNetwork craftingNetwork,
606
                                @Nullable IPartNetwork partNetwork, int channel,
607
                                @Nullable ValueDeseralizationContext valueDeseralizationContext) {
608
            this.network = network;
×
609
            this.craftingNetwork = craftingNetwork;
×
610
            this.partNetwork = partNetwork;
×
611
            this.channel = channel;
×
612
            this.valueDeseralizationContext = valueDeseralizationContext;
×
613
            reloadRecipes();
×
614
            if (network != null) {
×
615
                this.getCraftingJobHandler().reRegisterObservers(network);
×
616
            }
617
        }
×
618

619
        public ICraftingNetwork getCraftingNetwork() {
620
            return craftingNetwork;
×
621
        }
622

623
        @Override
624
        public int getChannel() {
625
            return channel;
×
626
        }
627

628
        @Override
629
        public Collection<IRecipeDefinition> getRecipes() {
630
            return this.currentRecipes.values();
×
631
        }
632

633
        public Int2ObjectMap<IRecipeDefinition> getRecipesIndexed() {
634
            return currentRecipes;
×
635
        }
636

637
        @Override
638
        public boolean canScheduleCraftingJobs() {
639
            return getCraftingJobHandler().canScheduleCraftingJobs();
×
640
        }
641

642
        @Override
643
        public void scheduleCraftingJob(CraftingJob craftingJob) {
644
            getCraftingJobHandler().scheduleCraftingJob(craftingJob);
×
645
        }
×
646

647
        @Override
648
        public int getCraftingJobsCount() {
649
            return this.craftingJobHandler.getAllCraftingJobs().size();
×
650
        }
651

652
        @Override
653
        public Iterator<CraftingJob> getCraftingJobs() {
654
            return this.craftingJobHandler.getAllCraftingJobs().values().iterator();
×
655
        }
656

657
        @Override
658
        public List<Map<IngredientComponent<?, ?>, List<IPrototypedIngredient<?, ?>>>> getPendingCraftingJobOutputs(int craftingJobId) {
659
            List<Map<IngredientComponent<?, ?>, List<IPrototypedIngredient<?, ?>>>> pending = this.craftingJobHandler.getProcessingCraftingJobsPendingIngredients().get(craftingJobId);
×
660
            if (pending == null) {
×
661
                pending = Lists.newArrayList();
×
662
            }
663
            return pending;
×
664
        }
665

666
        @Override
667
        public CraftingJobStatus getCraftingJobStatus(ICraftingNetwork network, int channel, int craftingJobId) {
668
            return craftingJobHandler.getCraftingJobStatus(network, channel, craftingJobId);
×
669
        }
670

671
        @Override
672
        public void cancelCraftingJob(int channel, int craftingJobId) {
673
            craftingJobHandler.markCraftingJobFinished(craftingJobId);
×
674
        }
×
675

676
        @Override
677
        public PrioritizedPartPos getPosition() {
678
            return PrioritizedPartPos.of(getTarget().getCenter(), getPriority());
×
679
        }
680

681
        public CraftingJobHandler getCraftingJobHandler() {
682
            return craftingJobHandler;
×
683
        }
684

685
        public boolean shouldAddToCraftingNetwork() {
686
            return shouldAddToCraftingNetwork;
×
687
        }
688

689
        public void setShouldAddToCraftingNetwork(boolean shouldAddToCraftingNetwork) {
690
            this.shouldAddToCraftingNetwork = shouldAddToCraftingNetwork;
×
691
        }
×
692

693
        public List<IngredientInstanceWrapper<?, ?>> getInventoryOutputBuffer() {
694
            return inventoryOutputBuffer;
×
695
        }
696

697
        @Override
698
        public <T> Optional<T> getCapability(PartTypeInterfaceCrafting partType, PartCapability<T> capability, INetwork network, IPartNetwork partNetwork, PartTarget target) {
NEW
699
            if (capability == Capabilities.CraftingInterface.PART) {
×
NEW
700
                return Optional.of((T) this);
×
701
            }
702

703
            // Expose the whole storage
704
            if (this.network != null) {
×
705
                IngredientComponent<?, ?> ingredientComponent = IngredientComponent.getIngredientComponentForStorageCapability(capability);
×
706
                if (ingredientComponent != null) {
×
707
                    T cap = wrapStorageCapability(capability, ingredientComponent);
×
708
                    if (cap != null) {
×
NEW
709
                        return Optional.of(cap);
×
710
                    }
711
                }
712
            }
713

NEW
714
            return super.getCapability(partType, capability, network, partNetwork, target);
×
715
        }
716

717
        protected <C, T, M> C wrapStorageCapability(PartCapability<C> capability, IngredientComponent<T, M> ingredientComponent) {
718
            IIngredientComponentStorage<T, M> storage = CraftingHelpers.getNetworkStorage(this.network, this.channelCrafting,
×
719
                    ingredientComponent, false);
720

721
            // Don't allow extraction, only insertion
722
            storage = new IngredientComponentStorageSlottedInsertProxy<>(storage);
×
723

724
            return ingredientComponent.getStorageWrapperHandler(capability).wrapStorage(storage);
×
725
        }
726

727
        @Override
728
        public <T, M> void addResult(IngredientComponent<T, M> ingredientComponent, T instance) {
729
            this.getInventoryOutputBuffer().add(new IngredientInstanceWrapper<>(ingredientComponent, instance));
×
730

731
            // Try to flush buffer immediately
732
            if (this.network != null) {
×
733
                this.flushInventoryOutputBuffer(this.network);
×
734
            }
735
        }
×
736

737
        public void setIngredientComponentTargetSideOverride(IngredientComponent<?, ?> ingredientComponent, Direction side) {
738
            if (getTarget().getTarget().getSide() == side) {
×
739
                craftingJobHandler.setIngredientComponentTarget(ingredientComponent, null);
×
740
            } else {
741
                craftingJobHandler.setIngredientComponentTarget(ingredientComponent, side);
×
742
            }
743
            sendUpdate();
×
744
        }
×
745

746
        public Direction getIngredientComponentTargetSideOverride(IngredientComponent<?, ?> ingredientComponent) {
747
            Direction side = craftingJobHandler.getIngredientComponentTarget(ingredientComponent);
×
748
            if (side == null) {
×
749
                side = getTarget().getTarget().getSide();
×
750
            }
751
            return side;
×
752
        }
753

754
        public boolean isRecipeSlotValid(int slot) {
755
            return this.recipeSlotValidated.containsKey(slot);
×
756
        }
757

758
        @Nullable
759
        public MutableComponent getRecipeSlotUnlocalizedMessage(int slot) {
760
            return this.recipeSlotMessages.get(slot);
×
761
        }
762

763
        public IntSet getDelayedRecipeReloads() {
764
            return delayedRecipeReloads;
×
765
        }
766

767
        public void setDisableCraftingCheck(boolean disableCraftingCheck) {
768
            if (disableCraftingCheck != this.disableCraftingCheck) {
×
769
                this.disableCraftingCheck = disableCraftingCheck;
×
770

771
                this.sendUpdate();
×
772
            }
773
        }
×
774

775
        public boolean isDisableCraftingCheck() {
776
            return disableCraftingCheck;
×
777
        }
778

779
        public void flushInventoryOutputBuffer(INetwork network) {
780
            // Try to insert each ingredient in the buffer into the network.
781
            boolean changed = false;
×
782
            ListIterator<IngredientInstanceWrapper<?, ?>> outputBufferIt = this.getInventoryOutputBuffer().listIterator();
×
783
            while (outputBufferIt.hasNext()) {
×
784
                IngredientInstanceWrapper<?, ?> oldWrapper = outputBufferIt.next();
×
785

786
                // Force observation before insertion (see #98 on why this is necessary)
787
                this.forceObservationOnInsertable(oldWrapper);
×
788

789
                IngredientInstanceWrapper<?, ?> newWrapper = insertIntoNetwork(oldWrapper,
×
790
                        network, this.getChannelCrafting());
×
791
                if (newWrapper != oldWrapper) {
×
792
                    changed = true;
×
793
                }
794
                if (newWrapper == null) {
×
795
                    outputBufferIt.remove();
×
796
                } else {
797
                    outputBufferIt.set(newWrapper);
×
798
                }
799
            }
×
800

801
            // If at least one ingredient was inserted, force a sync observer update in the network.
802
            if (changed) {
×
803
                CraftingHelpers.beforeCalculateCraftingJobs(network, getChannelCrafting());
×
804
            }
805
        }
×
806

807
        /**
808
         * Iterate over all positions that *could* accept the given instance,
809
         * and force an observation over them.
810
         *
811
         * This is necessary to ensure that we have the latest state indexed right before insertion.
812
         * This allows us to force another observation right after the insertion,
813
         * which will guarantee that we will track the expected diff events as result.
814
         *
815
         * @param oldWrapper The ingredient to attempt to insert (simulated).
816
         * @param <T> Ingredient type.
817
         * @param <M> Match flags.
818
         */
819
        protected <T, M> void forceObservationOnInsertable(IngredientInstanceWrapper<T, M> oldWrapper) {
820
            IIngredientMatcher<T, M> matcher = oldWrapper.getComponent().getMatcher();
×
821
            IPositionedAddonsNetworkIngredients<T, M> ingredientsNetwork = CraftingHelpers.getIngredientsNetwork(network, oldWrapper.getComponent()).orElse(null);
×
822
            if (ingredientsNetwork != null) {
×
823
                boolean marked = false;
×
NEW
824
                INetworkIngredientsChannel<?, ?> ingredientsNetworkChannel = ingredientsNetwork.getChannel(this.getChannelCrafting());
×
825
                T instance = oldWrapper.getInstance();
×
826
                for (PartPos position : ingredientsNetworkChannel.findNonFullPositions()) {
×
827
                    T instanceOut = ingredientsNetwork.getPositionedStorage(position).insert(instance, true);
×
828
                    if (!matcher.matchesExactly(instanceOut, instance)) {
×
829
                        marked = true;
×
830
                        instance = instanceOut;
×
831
                        ingredientsNetwork.scheduleObservationForced(this.getChannelCrafting(), position);
×
832
                        if (matcher.isEmpty(instance)) {
×
833
                            break;
×
834
                        }
835
                    }
836
                }
×
837

838
                if (marked || ingredientsNetwork.isObservationForcedPending(channel)) {
×
839
                    ingredientsNetwork.runObserverSync();
×
840
                }
841
            }
842
        }
×
843
    }
844
}
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

© 2025 Coveralls, Inc