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

CyclopsMC / IntegratedCrafting / #479011789

10 May 2025 06:36AM UTC coverage: 25.162% (-0.5%) from 25.626%
#479011789

push

github

rubensworks
Add smithing table and stonecutter support, Closes #118

0 of 64 new or added lines in 6 files covered. (0.0%)

1 existing line in 1 file now uncovered.

737 of 2929 relevant lines covered (25.16%)

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.IntOpenHashSet;
11
import it.unimi.dsi.fastutil.ints.IntSet;
12
import net.minecraft.core.Direction;
13
import net.minecraft.nbt.CompoundTag;
14
import net.minecraft.nbt.ListTag;
15
import net.minecraft.nbt.Tag;
16
import net.minecraft.network.FriendlyByteBuf;
17
import net.minecraft.network.chat.Component;
18
import net.minecraft.network.chat.MutableComponent;
19
import net.minecraft.resources.ResourceLocation;
20
import net.minecraft.server.level.ServerPlayer;
21
import net.minecraft.world.MenuProvider;
22
import net.minecraft.world.SimpleContainer;
23
import net.minecraft.world.entity.player.Inventory;
24
import net.minecraft.world.entity.player.Player;
25
import net.minecraft.world.inventory.AbstractContainerMenu;
26
import net.minecraft.world.item.ItemStack;
27
import net.minecraft.world.level.block.state.BlockState;
28
import net.minecraftforge.common.MinecraftForge;
29
import net.minecraftforge.common.capabilities.Capability;
30
import net.minecraftforge.common.util.LazyOptional;
31
import org.apache.commons.lang3.tuple.Triple;
32
import org.apache.logging.log4j.Level;
33
import org.cyclops.commoncapabilities.api.capability.block.BlockCapabilities;
34
import org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition;
35
import org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeHandler;
36
import org.cyclops.commoncapabilities.api.ingredient.IIngredientMatcher;
37
import org.cyclops.commoncapabilities.api.ingredient.IMixedIngredients;
38
import org.cyclops.commoncapabilities.api.ingredient.IPrototypedIngredient;
39
import org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;
40
import org.cyclops.commoncapabilities.api.ingredient.IngredientInstanceWrapper;
41
import org.cyclops.commoncapabilities.api.ingredient.storage.IIngredientComponentStorage;
42
import org.cyclops.cyclopscore.datastructure.DimPos;
43
import org.cyclops.cyclopscore.helper.BlockEntityHelpers;
44
import org.cyclops.cyclopscore.inventory.SimpleInventory;
45
import org.cyclops.cyclopscore.persist.nbt.NBTClassType;
46
import org.cyclops.integratedcrafting.Capabilities;
47
import org.cyclops.integratedcrafting.GeneralConfig;
48
import org.cyclops.integratedcrafting.IntegratedCrafting;
49
import org.cyclops.integratedcrafting.api.crafting.CraftingJob;
50
import org.cyclops.integratedcrafting.api.crafting.CraftingJobStatus;
51
import org.cyclops.integratedcrafting.api.crafting.ICraftingInterface;
52
import org.cyclops.integratedcrafting.api.crafting.ICraftingResultsSink;
53
import org.cyclops.integratedcrafting.api.network.ICraftingNetwork;
54
import org.cyclops.integratedcrafting.capability.network.CraftingInterfaceConfig;
55
import org.cyclops.integratedcrafting.capability.network.CraftingNetworkConfig;
56
import org.cyclops.integratedcrafting.core.CraftingHelpers;
57
import org.cyclops.integratedcrafting.core.CraftingJobHandler;
58
import org.cyclops.integratedcrafting.core.CraftingProcessOverrides;
59
import org.cyclops.integratedcrafting.core.part.PartTypeCraftingBase;
60
import org.cyclops.integratedcrafting.ingredient.storage.IngredientComponentStorageSlottedInsertProxy;
61
import org.cyclops.integratedcrafting.inventory.container.ContainerPartInterfaceCrafting;
62
import org.cyclops.integratedcrafting.inventory.container.ContainerPartInterfaceCraftingSettings;
63
import org.cyclops.integrateddynamics.api.evaluate.EvaluationException;
64
import org.cyclops.integrateddynamics.api.evaluate.variable.IValue;
65
import org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;
66
import org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;
67
import org.cyclops.integrateddynamics.api.network.INetwork;
68
import org.cyclops.integrateddynamics.api.network.INetworkIngredientsChannel;
69
import org.cyclops.integrateddynamics.api.network.IPartNetwork;
70
import org.cyclops.integrateddynamics.api.network.IPositionedAddonsNetworkIngredients;
71
import org.cyclops.integrateddynamics.api.part.IPartContainer;
72
import org.cyclops.integrateddynamics.api.part.PartPos;
73
import org.cyclops.integrateddynamics.api.part.PartTarget;
74
import org.cyclops.integrateddynamics.api.part.PrioritizedPartPos;
75
import org.cyclops.integrateddynamics.capability.network.PositionedAddonsNetworkIngredientsHandlerConfig;
76
import org.cyclops.integrateddynamics.core.evaluate.InventoryVariableEvaluator;
77
import org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeRecipe;
78
import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;
79
import org.cyclops.integrateddynamics.core.helper.NetworkHelpers;
80
import org.cyclops.integrateddynamics.core.helper.PartHelpers;
81
import org.cyclops.integrateddynamics.core.part.PartStateBase;
82
import org.cyclops.integrateddynamics.core.part.PartTypeBase;
83
import org.cyclops.integrateddynamics.core.part.event.PartVariableDrivenVariableContentsUpdatedEvent;
84

85
import javax.annotation.Nullable;
86
import java.util.Collection;
87
import java.util.Iterator;
88
import java.util.List;
89
import java.util.ListIterator;
90
import java.util.Map;
91
import java.util.Optional;
92

93
/**
94
 * Interface for item handlers.
95
 * @author rubensworks
96
 */
97
public class PartTypeInterfaceCrafting extends PartTypeCraftingBase<PartTypeInterfaceCrafting, PartTypeInterfaceCrafting.State> {
98

99
    public PartTypeInterfaceCrafting(String name) {
100
        super(name);
×
101
    }
×
102

103
    @Override
104
    public int getConsumptionRate(State state) {
105
        return state.getCraftingJobHandler().getProcessingCraftingJobs().size() * GeneralConfig.interfaceCraftingBaseConsumption;
×
106
    }
107

108
    @Override
109
    public Optional<MenuProvider> getContainerProvider(PartPos pos) {
110
        return Optional.of(new MenuProvider() {
×
111

112
            @Override
113
            public MutableComponent getDisplayName() {
114
                return Component.translatable(getTranslationKey());
×
115
            }
116

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

127
    @Override
128
    public void writeExtraGuiData(FriendlyByteBuf packetBuffer, PartPos pos, ServerPlayer player) {
129
        // Write inventory size
130
        IPartContainer partContainer = PartHelpers.getPartContainerChecked(pos);
×
131
        PartTypeInterfaceCrafting.State partState = (PartTypeInterfaceCrafting.State) partContainer.getPartState(pos.getSide());
×
132
        packetBuffer.writeInt(partState.getInventoryVariables().getContainerSize());
×
133

134
        super.writeExtraGuiData(packetBuffer, pos, player);
×
135
    }
×
136

137
    @Override
138
    public Optional<MenuProvider> getContainerProviderSettings(PartPos pos) {
139
        return Optional.of(new MenuProvider() {
×
140

141
            @Override
142
            public MutableComponent getDisplayName() {
143
                return Component.translatable(getTranslationKey());
×
144
            }
145

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

155
    @Override
156
    protected PartTypeInterfaceCrafting.State constructDefaultState() {
157
        return new PartTypeInterfaceCrafting.State();
×
158
    }
159

160
    @Override
161
    public void afterNetworkReAlive(INetwork network, IPartNetwork partNetwork, PartTarget target, PartTypeInterfaceCrafting.State state) {
162
        super.afterNetworkReAlive(network, partNetwork, target, state);
×
163
        addTargetToNetwork(network, target, state, true);
×
164
    }
×
165

166
    @Override
167
    public void onNetworkRemoval(INetwork network, IPartNetwork partNetwork, PartTarget target, PartTypeInterfaceCrafting.State state) {
168
        super.onNetworkRemoval(network, partNetwork, target, state);
×
169
        removeTargetFromNetwork(network, target.getTarget(), state);
×
170
    }
×
171

172
    @Override
173
    public void onNetworkAddition(INetwork network, IPartNetwork partNetwork, PartTarget target, PartTypeInterfaceCrafting.State state) {
174
        super.onNetworkAddition(network, partNetwork, target, state);
×
175
        addTargetToNetwork(network, target, state, true);
×
176
    }
×
177

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

187
    protected Capability<ICraftingNetwork> getNetworkCapability() {
188
        return CraftingNetworkConfig.CAPABILITY;
×
189
    }
190

191
    protected void addTargetToNetwork(INetwork network, PartTarget pos, PartTypeInterfaceCrafting.State state, boolean initialize) {
192
        network.getCapability(getNetworkCapability())
×
193
                .ifPresent(craftingNetwork -> {
×
194
                    int channelCrafting = state.getChannelCrafting();
×
195
                    state.setTarget(pos);
×
196
                    state.setNetworks(network, craftingNetwork, NetworkHelpers.getPartNetworkChecked(network), channelCrafting, ValueDeseralizationContext.of(pos.getCenter().getPos().getLevel(true)), initialize);
×
197
                    state.setShouldAddToCraftingNetwork(true);
×
198
                });
×
199
    }
×
200

201
    protected void removeTargetFromNetwork(INetwork network, PartPos pos, PartTypeInterfaceCrafting.State state) {
202
        ICraftingNetwork craftingNetwork = state.getCraftingNetwork();
×
203
        if (craftingNetwork != null) {
×
204
            network.getCapability(getNetworkCapability())
×
205
                    .ifPresent(n -> n.removeCraftingInterface(state.getChannelCrafting(), state));
×
206
        }
207
        state.setNetworks(null, null, null, -1, null, false);
×
208
        state.setTarget(null);
×
209
    }
×
210

211
    @Override
212
    public boolean isUpdate(State state) {
213
        return true;
×
214
    }
215

216
    @Override
217
    public int getMinimumUpdateInterval(State state) {
218
        return state.getDefaultUpdateInterval();
×
219
    }
220

221
    @Override
222
    public void update(INetwork network, IPartNetwork partNetwork, PartTarget target, State state) {
223
        super.update(network, partNetwork, target, state);
×
224

225
        // Init network data in part state if it has not been done yet.
226
        // This can occur when the part chunk is being reloaded.
227
        if (state.getCraftingNetwork() == null) {
×
228
            addTargetToNetwork(network, target, state, false);
×
229
        }
230

231
        int channel = state.getChannelCrafting();
×
232

233
        // Update the network data in the part state
234
        if (state.shouldAddToCraftingNetwork()) {
×
235
            ICraftingNetwork craftingNetwork = network.getCapability(getNetworkCapability()).orElse(null);
×
236
            craftingNetwork.addCraftingInterface(channel, state);
×
237
            state.setShouldAddToCraftingNetwork(false);
×
238
        }
239

240
        // Push any pending output ingredients into the network
241
        state.flushInventoryOutputBuffer(network);
×
242

243
        // Block job ticking if there still are outputs in our crafting result buffer.
244
        if (state.getInventoryOutputBuffer().isEmpty()) {
×
245
            // Tick the job handler
246
            PartPos targetPos = state.getTarget().getTarget();
×
247
            state.getCraftingJobHandler().update(network, channel, targetPos);
×
248
        }
249

250
        // Reload recipes if needed
251
        IntSet slots = state.getDelayedRecipeReloads();
×
252
        if (!slots.isEmpty()) {
×
253
            ICraftingNetwork craftingNetwork = network.getCapability(getNetworkCapability()).orElse(null);
×
254
            if (craftingNetwork != null) {
×
255
                IntSet slotsCopy = new IntOpenHashSet(slots); // Create a copy, to allow insertion into slots during this loop
×
256
                slots.clear();
×
257
                for (Integer slot : slotsCopy) {
×
258
                    // Remove the old recipe from the network
259
                    Int2ObjectMap<IRecipeDefinition> recipes = state.getRecipesIndexed();
×
260
                    IRecipeDefinition oldRecipe = recipes.get(slot);
×
261
                    if (oldRecipe != null) {
×
262
                        craftingNetwork.removeCraftingInterfaceRecipe(channel, state, oldRecipe);
×
263
                    }
264

265
                    // Reload the recipe in the slot
266
                    // We simulate initialization for the first two ticks, as dependency variables may still be loading,
267
                    // and errored may only go away after these dependencies are fully loaded.
268
                    // Related to CyclopsMC/IntegratedCrafting#110
269
                    state.reloadRecipe(slot, state.ticksAfterReload <= 1);
×
270

271
                    // Add the new recipe to the network
272
                    IRecipeDefinition newRecipe = recipes.get(slot);
×
273
                    if (newRecipe != null) {
×
274
                        craftingNetwork.addCraftingInterfaceRecipe(channel, state, newRecipe);
×
275
                    }
276
                }
×
277
            }
278
        }
279

280
        // Internal tick counter
281
        state.ticksAfterReload++;
×
282
    }
×
283

284
    @Nullable
285
    protected static <T, M> IngredientInstanceWrapper<T, M> insertIntoNetwork(IngredientInstanceWrapper<T, M> wrapper,
286
                                                                              INetwork network, int channel) {
287
        IPositionedAddonsNetworkIngredients<T, M> storageNetwork = wrapper.getComponent()
×
288
                .getCapability(PositionedAddonsNetworkIngredientsHandlerConfig.CAPABILITY)
×
289
                .map(n -> (IPositionedAddonsNetworkIngredients<T, M>) n.getStorage(network).orElse(null))
×
290
                .orElse(null);
×
291
        if (storageNetwork != null) {
×
292
            IIngredientComponentStorage<T, M> storage = storageNetwork.getChannel(channel);
×
293
            T remaining = storage.insert(wrapper.getInstance(), false);
×
294
            if (wrapper.getComponent().getMatcher().isEmpty(remaining)) {
×
295
                return null;
×
296
            } else {
297
                return new IngredientInstanceWrapper<>(wrapper.getComponent(), remaining);
×
298
            }
299
        }
300
        return wrapper;
×
301
    }
302

303
    @Override
304
    public void addDrops(PartTarget target, State state, List<ItemStack> itemStacks, boolean dropMainElement, boolean saveState) {
305
        // Drop any remaining output ingredients (only items)
306
        for (IngredientInstanceWrapper<?, ?> ingredientInstanceWrapper : state.getInventoryOutputBuffer()) {
×
307
            if (ingredientInstanceWrapper.getComponent() == IngredientComponent.ITEMSTACK) {
×
308
                itemStacks.add((ItemStack) ingredientInstanceWrapper.getInstance());
×
309
            }
310
        }
×
311
        state.getInventoryOutputBuffer().clear();
×
312

313
        // Drop the stored variables
314
        for(int i = 0; i < state.getInventoryVariables().getContainerSize(); i++) {
×
315
            ItemStack itemStack = state.getInventoryVariables().getItem(i);
×
316
            if(!itemStack.isEmpty()) {
×
317
                itemStacks.add(itemStack);
×
318
            }
319
        }
320
        state.getInventoryVariables().clearContent();
×
321

322
        super.addDrops(target, state, itemStacks, dropMainElement, saveState);
×
323
    }
×
324

325
    public static class State extends PartStateBase<PartTypeInterfaceCrafting>
326
            implements ICraftingInterface, ICraftingResultsSink {
327

328
        protected int ticksAfterReload = 0;
×
329

330
        private final CraftingJobHandler craftingJobHandler;
331
        private final SimpleInventory inventoryVariables;
332
        private final List<InventoryVariableEvaluator<ValueObjectTypeRecipe.ValueRecipe>> variableEvaluators;
333
        private final List<IngredientInstanceWrapper<?, ?>> inventoryOutputBuffer;
334
        private final Int2ObjectMap<MutableComponent> recipeSlotMessages;
335
        private final Int2BooleanMap recipeSlotValidated;
336
        private final IntSet delayedRecipeReloads;
337
        private final Map<IVariable, Boolean> variableListeners;
338
        private int channelCrafting = 0;
×
339
        private boolean disableCraftingCheck = false;
×
340

341
        private final Int2ObjectMap<IRecipeDefinition> currentRecipes;
342
        private PartTarget target = null;
×
343
        private INetwork network = null;
×
344
        private ICraftingNetwork craftingNetwork = null;
×
345
        private IPartNetwork partNetwork = null;
×
346
        private int channel = -1;
×
347
        private ValueDeseralizationContext valueDeseralizationContext;
348
        private boolean shouldAddToCraftingNetwork = false;
×
349
        private Player lastPlayer;
350

351
        public State() {
×
352
            this.craftingJobHandler = new CraftingJobHandler(1, true,
×
353
                    CraftingProcessOverrides.REGISTRY.getCraftingProcessOverrides(), this);
×
354
            this.inventoryVariables = new SimpleInventory(9, 1);
×
355
            this.inventoryVariables.addDirtyMarkListener(this);
×
356
            this.variableEvaluators = Lists.newArrayList();
×
357
            this.inventoryOutputBuffer = Lists.newArrayList();
×
358
            this.recipeSlotMessages = new Int2ObjectArrayMap<>();
×
359
            this.recipeSlotValidated = new Int2BooleanArrayMap();
×
360
            this.delayedRecipeReloads = new IntArraySet();
×
361
            this.variableListeners = new MapMaker().weakKeys().makeMap();
×
362
            this.currentRecipes = new Int2ObjectArrayMap<>();
×
363
        }
×
364

365
        @Override
366
        protected int getDefaultUpdateInterval() {
367
            return GeneralConfig.minCraftingInterfaceUpdateFreq;
×
368
        }
369

370
        /**
371
         * @return The inner variables inventory
372
         */
373
        public SimpleInventory getInventoryVariables() {
374
            return this.inventoryVariables;
×
375
        }
376

377
        @Override
378
        public void writeToNBT(CompoundTag tag) {
379
            super.writeToNBT(tag);
×
380
            inventoryVariables.writeToNBT(tag, "variables");
×
381

382
            ListTag instanceTags = new ListTag();
×
383
            for (IngredientInstanceWrapper instanceWrapper : inventoryOutputBuffer) {
×
384
                CompoundTag instanceTag = new CompoundTag();
×
385
                instanceTag.putString("component", IngredientComponent.REGISTRY.getKey(instanceWrapper.getComponent()).toString());
×
386
                instanceTag.put("instance", instanceWrapper.getComponent().getSerializer().serializeInstance(instanceWrapper.getInstance()));
×
387
                instanceTags.add(instanceTag);
×
388
            }
×
389
            tag.put("inventoryOutputBuffer", instanceTags);
×
390

391
            this.craftingJobHandler.writeToNBT(tag);
×
392
            tag.putInt("channelCrafting", channelCrafting);
×
393

394
            CompoundTag recipeSlotErrorsTag = new CompoundTag();
×
395
            for (Int2ObjectMap.Entry<MutableComponent> entry : this.recipeSlotMessages.int2ObjectEntrySet()) {
×
396
                NBTClassType.writeNbt(MutableComponent.class, String.valueOf(entry.getIntKey()), entry.getValue(), recipeSlotErrorsTag);
×
397
            }
×
398
            tag.put("recipeSlotMessages", recipeSlotErrorsTag);
×
399

400
            CompoundTag recipeSlotValidatedTag = new CompoundTag();
×
401
            for (Int2BooleanMap.Entry entry : this.recipeSlotValidated.int2BooleanEntrySet()) {
×
402
                recipeSlotValidatedTag.putBoolean(String.valueOf(entry.getIntKey()), entry.getBooleanValue());
×
403
            }
×
404
            tag.put("recipeSlotValidated", recipeSlotValidatedTag);
×
405

406
            tag.putBoolean("disableCraftingCheck", disableCraftingCheck);
×
407
        }
×
408

409
        @Override
410
        public void readFromNBT(ValueDeseralizationContext valueDeseralizationContext, CompoundTag tag) {
411
            super.readFromNBT(valueDeseralizationContext, tag);
×
412
            inventoryVariables.readFromNBT(tag, "variables");
×
413

414
            this.inventoryOutputBuffer.clear();
×
415
            for (Tag instanceTagRaw : tag.getList("inventoryOutputBuffer", Tag.TAG_COMPOUND)) {
×
416
                CompoundTag instanceTag = (CompoundTag) instanceTagRaw;
×
417
                String componentName = instanceTag.getString("component");
×
418
                IngredientComponent<?, ?> component = IngredientComponent.REGISTRY.getValue(new ResourceLocation(componentName));
×
419
                this.inventoryOutputBuffer.add(new IngredientInstanceWrapper(component,
×
420
                        component.getSerializer().deserializeInstance(instanceTag.get("instance"))));
×
421
            }
×
422

423
            this.craftingJobHandler.readFromNBT(tag);
×
424
            this.channelCrafting = tag.getInt("channelCrafting");
×
425

426
            this.recipeSlotMessages.clear();
×
427
            CompoundTag recipeSlotErrorsTag = tag.getCompound("recipeSlotMessages");
×
428
            for (String slot : recipeSlotErrorsTag.getAllKeys()) {
×
429
                MutableComponent unlocalizedString = NBTClassType.readNbt(MutableComponent.class, slot, recipeSlotErrorsTag);
×
430
                this.recipeSlotMessages.put(Integer.parseInt(slot), unlocalizedString);
×
431
            }
×
432

433
            this.recipeSlotValidated.clear();
×
434
            CompoundTag recipeSlotValidatedTag = tag.getCompound("recipeSlotValidated");
×
435
            for (String slot : recipeSlotValidatedTag.getAllKeys()) {
×
436
                this.recipeSlotValidated.put(Integer.parseInt(slot), recipeSlotValidatedTag.getBoolean(slot));
×
437
            }
×
438

439
            this.disableCraftingCheck = tag.getBoolean("disableCraftingCheck");
×
440
        }
×
441

442
        public void setChannelCrafting(int channelCrafting) {
443
            if (this.channelCrafting != channelCrafting) {
×
444
                // Unregister from the network
445
                if (craftingNetwork != null) {
×
446
                    craftingNetwork.removeCraftingInterface(this.channelCrafting, this);
×
447
                }
448

449
                // Update the channel
450
                this.channelCrafting = channelCrafting;
×
451

452
                // Re-register to the network
453
                if (craftingNetwork != null) {
×
454
                    craftingNetwork.addCraftingInterface(this.channelCrafting, this);
×
455
                }
456

457
                sendUpdate();
×
458
            }
459
        }
×
460

461
        public int getChannelCrafting() {
462
            return channelCrafting;
×
463
        }
464

465
        public void reloadRecipes(boolean initialize) {
466
            this.currentRecipes.clear();
×
467
            this.recipeSlotMessages.clear();
×
468
            this.recipeSlotValidated.clear();
×
469
            variableEvaluators.clear();
×
470
            for (int i = 0; i < getInventoryVariables().getContainerSize(); i++) {
×
471
                int slot = i;
×
472
                variableEvaluators.add(new InventoryVariableEvaluator<ValueObjectTypeRecipe.ValueRecipe>(
×
473
                        getInventoryVariables(), slot, valueDeseralizationContext, ValueTypes.OBJECT_RECIPE) {
×
474
                    @Override
475
                    public void onErrorsChanged() {
476
                        super.onErrorsChanged();
×
477
                        setLocalErrors(slot, getErrors());
×
478
                    }
×
479
                });
480
            }
481
            if (this.partNetwork != null) {
×
482
                for (int i = 0; i < getInventoryVariables().getContainerSize(); i++) {
×
483
                    reloadRecipe(i, initialize);
×
484
                }
485
            }
486
        }
×
487

488
        private void setLocalErrors(int slot, List<MutableComponent> errors) {
489
            if (errors.isEmpty()) {
×
490
                if (this.recipeSlotMessages.size() > slot) {
×
491
                    this.recipeSlotMessages.remove(slot);
×
492
                }
493
            } else {
494
                this.recipeSlotMessages.put(slot, errors.get(0));
×
495
            }
496
        }
×
497

498
        protected void reloadRecipe(int slot, boolean initialize) {
499
            this.currentRecipes.remove(slot);
×
500
            if (this.recipeSlotMessages.size() > slot) {
×
501
                this.recipeSlotMessages.remove(slot);
×
502
            }
503
            if (this.recipeSlotValidated.size() > slot) {
×
504
                this.recipeSlotValidated.remove(slot);
×
505
            }
506
            if (this.partNetwork != null) {
×
507
                InventoryVariableEvaluator<ValueObjectTypeRecipe.ValueRecipe> evaluator = variableEvaluators.get(slot);
×
508
                evaluator.refreshVariable(network, false);
×
509
                IVariable<ValueObjectTypeRecipe.ValueRecipe> variable = evaluator.getVariable(network);
×
510
                if (variable != null) {
×
511
                    try {
512
                        // Refresh the recipe if variable is changed
513
                        // The map is needed because we only want to register the listener once for each variable
514
                        if (!this.variableListeners.containsKey(variable)) {
×
515
                            variable.addInvalidationListener(() -> {
×
516
                                this.variableListeners.remove(variable);
×
517
                                delayedReloadRecipe(slot);
×
518
                            });
×
519
                            this.variableListeners.put(variable, true);
×
520
                        }
521

522
                        IValue value = variable.getValue();
×
523
                        if (value.getType() == ValueTypes.OBJECT_RECIPE) {
×
524
                            Optional<IRecipeDefinition> recipeWrapper = ((ValueObjectTypeRecipe.ValueRecipe) value).getRawValue();
×
525
                            if (recipeWrapper.isPresent()) {
×
526
                                IRecipeDefinition recipe = recipeWrapper.get();
×
527
                                if (!GeneralConfig.validateRecipesCraftingInterface || this.disableCraftingCheck || isValid(recipe)) {
×
528
                                    this.currentRecipes.put(slot, recipe);
×
529
                                    this.recipeSlotValidated.put(slot, true);
×
530
                                    this.recipeSlotMessages.put(slot, Component.translatable("gui.integratedcrafting.partinterface.slot.message.valid"));
×
531
                                } else {
532
                                    this.recipeSlotMessages.put(slot, Component.translatable("gui.integratedcrafting.partinterface.slot.message.invalid"));
×
533
                                }
534
                            }
535
                        } else {
×
536
                            this.recipeSlotMessages.put(slot, Component.translatable("gui.integratedcrafting.partinterface.slot.message.norecipe"));
×
537
                        }
538
                    } catch (EvaluationException e) {
×
539
                        this.recipeSlotMessages.put(slot, e.getErrorMessage());
×
540
                    }
×
541
                } else {
542
                    // If we're initializing, the variable might be referencing other variables that are not yet loaded.
543
                    // So let's retry once in the next tick.
544
                    if (initialize && evaluator.hasVariable()) {
×
545
                        this.delayedReloadRecipe(slot);
×
546
                    } else {
547
                        this.recipeSlotMessages.put(slot, Component.translatable("gui.integratedcrafting.partinterface.slot.message.norecipe"));
×
548
                    }
549
                }
550

551
                try {
552
                    IPartNetwork partNetwork = NetworkHelpers.getPartNetworkChecked(network);
×
553
                    MinecraftForge.EVENT_BUS.post(new PartVariableDrivenVariableContentsUpdatedEvent<>(network,
×
554
                            partNetwork, getTarget(),
×
555
                            PartTypes.INTERFACE_CRAFTING, this, lastPlayer, variable,
556
                            variable != null ? variable.getValue() : null));
×
557
                } catch (EvaluationException e) {
×
558
                    // Ignore error
559
                }
×
560
            }
561
            sendUpdate();
×
562
        }
×
563

564
        public void setLastPlayer(Player lastPlayer) {
565
            this.lastPlayer = lastPlayer;
×
566
        }
×
567

568
        private void delayedReloadRecipe(int slot) {
569
            this.delayedRecipeReloads.add(slot);
×
570
        }
×
571

572

573
        private boolean isValid(IRecipeDefinition recipe) {
574
            DimPos dimPos = getTarget().getTarget().getPos();
×
575
            Direction side = getTarget().getTarget().getSide();
×
576
            IRecipeHandler recipeHandler = BlockEntityHelpers.getCapability(dimPos.getLevel(true), dimPos.getBlockPos(), side, Capabilities.RECIPE_HANDLER).orElse(null);
×
577
            if (recipeHandler == null) {
×
578
                BlockState blockState = dimPos.getLevel(true).getBlockState(dimPos.getBlockPos());
×
579
                recipeHandler = BlockCapabilities.getInstance().getCapability(blockState, Capabilities.RECIPE_HANDLER,
×
580
                        dimPos.getLevel(true), dimPos.getBlockPos(), side)
×
581
                .orElse(null);
×
582
            }
583
            if (recipeHandler != null) {
×
NEW
584
                IMixedIngredients simulatedOutput = recipeHandler.simulate(recipe);
×
585
                if (simulatedOutput != null && !simulatedOutput.isEmpty()) {
×
586
                    if (recipe.getOutput().containsAll(simulatedOutput)) {
×
587
                        return true;
×
588
                    } else {
589
                        if (GeneralConfig.logRecipeValidationFailures) {
×
590
                            IntegratedCrafting.clog(Level.INFO, "Recipe validation failure: incompatible recipe output and simulated output:\nRecipe output: " + recipe.getOutput() + "\nSimulated output: " + simulatedOutput);
×
591
                        }
592
                        return false;
×
593
                    }
594
                }
595
                if (GeneralConfig.logRecipeValidationFailures) {
×
596
                    IntegratedCrafting.clog(Level.INFO, "Recipe validation failure: No output was obtained when simulating a recipe\n" + recipe);
×
597
                }
598
                return false;
×
599
            }
600
            return true; // No recipe handler capability is present, so we can't confirm that the recipe will work.
×
601
        }
602

603
        @Override
604
        public void onDirty() {
605
            super.onDirty();
×
606

607
            // Unregister from the network, when all old recipes are still in place
608
            if (craftingNetwork != null) {
×
609
                craftingNetwork.removeCraftingInterface(channelCrafting, this);
×
610
            }
611

612
            // Recalculate recipes
613
            if (getTarget() != null && !getTarget().getCenter().getPos().getLevel(true).isClientSide) {
×
614
                reloadRecipes(false);
×
615
            }
616

617
            // Re-register to the network, to force an update for all new recipes
618
            if (craftingNetwork != null) {
×
619
                craftingNetwork.addCraftingInterface(channelCrafting, this);
×
620
            }
621
        }
×
622

623
        public void setTarget(PartTarget target) {
624
            this.target = target;
×
625
        }
×
626

627
        public PartTarget getTarget() {
628
            return target;
×
629
        }
630

631
        public void setNetworks(@Nullable INetwork network, @Nullable ICraftingNetwork craftingNetwork,
632
                                @Nullable IPartNetwork partNetwork, int channel,
633
                                @Nullable ValueDeseralizationContext valueDeseralizationContext,
634
                                boolean initialize) {
635
            this.network = network;
×
636
            this.craftingNetwork = craftingNetwork;
×
637
            this.partNetwork = partNetwork;
×
638
            this.channel = channel;
×
639
            this.valueDeseralizationContext = valueDeseralizationContext;
×
640
            reloadRecipes(initialize);
×
641
            if (network != null) {
×
642
                this.getCraftingJobHandler().reRegisterObservers(network);
×
643
            }
644
        }
×
645

646
        public ICraftingNetwork getCraftingNetwork() {
647
            return craftingNetwork;
×
648
        }
649

650
        @Override
651
        public int getChannel() {
652
            return channel;
×
653
        }
654

655
        @Override
656
        public Collection<IRecipeDefinition> getRecipes() {
657
            return this.currentRecipes.values();
×
658
        }
659

660
        public Int2ObjectMap<IRecipeDefinition> getRecipesIndexed() {
661
            return currentRecipes;
×
662
        }
663

664
        @Override
665
        public boolean canScheduleCraftingJobs() {
666
            return getCraftingJobHandler().canScheduleCraftingJobs();
×
667
        }
668

669
        @Override
670
        public void scheduleCraftingJob(CraftingJob craftingJob) {
671
            getCraftingJobHandler().scheduleCraftingJob(craftingJob);
×
672
        }
×
673

674
        @Override
675
        public int getCraftingJobsCount() {
676
            return this.craftingJobHandler.getAllCraftingJobs().size();
×
677
        }
678

679
        @Override
680
        public Iterator<CraftingJob> getCraftingJobs() {
681
            return this.craftingJobHandler.getAllCraftingJobs().values().iterator();
×
682
        }
683

684
        @Override
685
        public List<Map<IngredientComponent<?, ?>, List<IPrototypedIngredient<?, ?>>>> getPendingCraftingJobOutputs(int craftingJobId) {
686
            List<Map<IngredientComponent<?, ?>, List<IPrototypedIngredient<?, ?>>>> pending = this.craftingJobHandler.getProcessingCraftingJobsPendingIngredients().get(craftingJobId);
×
687
            if (pending == null) {
×
688
                pending = Lists.newArrayList();
×
689
            }
690
            return pending;
×
691
        }
692

693
        @Override
694
        public CraftingJobStatus getCraftingJobStatus(ICraftingNetwork network, int channel, int craftingJobId) {
695
            return craftingJobHandler.getCraftingJobStatus(network, channel, craftingJobId);
×
696
        }
697

698
        @Override
699
        public void cancelCraftingJob(int channel, int craftingJobId) {
700
            craftingJobHandler.markCraftingJobFinished(craftingJobId);
×
701
        }
×
702

703
        @Override
704
        public PrioritizedPartPos getPosition() {
705
            return PrioritizedPartPos.of(getTarget().getCenter(), getPriority());
×
706
        }
707

708
        public CraftingJobHandler getCraftingJobHandler() {
709
            return craftingJobHandler;
×
710
        }
711

712
        public boolean shouldAddToCraftingNetwork() {
713
            return shouldAddToCraftingNetwork;
×
714
        }
715

716
        public void setShouldAddToCraftingNetwork(boolean shouldAddToCraftingNetwork) {
717
            this.shouldAddToCraftingNetwork = shouldAddToCraftingNetwork;
×
718
        }
×
719

720
        public List<IngredientInstanceWrapper<?, ?>> getInventoryOutputBuffer() {
721
            return inventoryOutputBuffer;
×
722
        }
723

724
        @Override
725
        public <T> LazyOptional<T> getCapability(Capability<T> capability, INetwork network, IPartNetwork partNetwork, PartTarget target) {
726
            if (capability == CraftingInterfaceConfig.CAPABILITY) {
×
727
                return LazyOptional.of(() -> this).cast();
×
728
            }
729

730
            // Expose the whole storage
731
            if (this.network != null) {
×
732
                IngredientComponent<?, ?> ingredientComponent = IngredientComponent.getIngredientComponentForStorageCapability(capability);
×
733
                if (ingredientComponent != null) {
×
734
                    T cap = wrapStorageCapability(capability, ingredientComponent);
×
735
                    if (cap != null) {
×
736
                        return LazyOptional.of(() -> cap);
×
737
                    }
738
                }
739
            }
740

741
            return super.getCapability(capability, network, partNetwork, target);
×
742
        }
743

744
        protected <C, T, M> C wrapStorageCapability(Capability<C> capability, IngredientComponent<T, M> ingredientComponent) {
745
            IIngredientComponentStorage<T, M> storage = CraftingHelpers.getNetworkStorage(this.network, this.channelCrafting,
×
746
                    ingredientComponent, false);
747

748
            // Don't allow extraction, only insertion
749
            storage = new IngredientComponentStorageSlottedInsertProxy<>(storage);
×
750

751
            return ingredientComponent.getStorageWrapperHandler(capability).wrapStorage(storage);
×
752
        }
753

754
        @Override
755
        public <T, M> void addResult(IngredientComponent<T, M> ingredientComponent, T instance) {
756
            this.getInventoryOutputBuffer().add(new IngredientInstanceWrapper<>(ingredientComponent, instance));
×
757

758
            // Try to flush buffer immediately
759
            if (this.network != null) {
×
760
                this.flushInventoryOutputBuffer(this.network);
×
761
            }
762
        }
×
763

764
        public void setIngredientComponentTargetSideOverride(IngredientComponent<?, ?> ingredientComponent, Direction side) {
765
            if (getTarget().getTarget().getSide() == side) {
×
766
                craftingJobHandler.setIngredientComponentTarget(ingredientComponent, null);
×
767
            } else {
768
                craftingJobHandler.setIngredientComponentTarget(ingredientComponent, side);
×
769
            }
770
            sendUpdate();
×
771
        }
×
772

773
        public Direction getIngredientComponentTargetSideOverride(IngredientComponent<?, ?> ingredientComponent) {
774
            Direction side = craftingJobHandler.getIngredientComponentTarget(ingredientComponent);
×
775
            if (side == null) {
×
776
                side = getTarget().getTarget().getSide();
×
777
            }
778
            return side;
×
779
        }
780

781
        public boolean isRecipeSlotValid(int slot) {
782
            return this.recipeSlotValidated.containsKey(slot);
×
783
        }
784

785
        @Nullable
786
        public MutableComponent getRecipeSlotUnlocalizedMessage(int slot) {
787
            return this.recipeSlotMessages.get(slot);
×
788
        }
789

790
        public IntSet getDelayedRecipeReloads() {
791
            return delayedRecipeReloads;
×
792
        }
793

794
        public void setDisableCraftingCheck(boolean disableCraftingCheck) {
795
            if (disableCraftingCheck != this.disableCraftingCheck) {
×
796
                this.disableCraftingCheck = disableCraftingCheck;
×
797

798
                this.sendUpdate();
×
799
            }
800
        }
×
801

802
        public boolean isDisableCraftingCheck() {
803
            return disableCraftingCheck;
×
804
        }
805

806
        public void flushInventoryOutputBuffer(INetwork network) {
807
            // Try to insert each ingredient in the buffer into the network.
808
            boolean changed = false;
×
809
            ListIterator<IngredientInstanceWrapper<?, ?>> outputBufferIt = this.getInventoryOutputBuffer().listIterator();
×
810
            while (outputBufferIt.hasNext()) {
×
811
                IngredientInstanceWrapper<?, ?> oldWrapper = outputBufferIt.next();
×
812

813
                // Force observation before insertion (see #98 on why this is necessary)
814
                this.forceObservationOnInsertable(oldWrapper);
×
815

816
                IngredientInstanceWrapper<?, ?> newWrapper = insertIntoNetwork(oldWrapper,
×
817
                        network, this.getChannelCrafting());
×
818
                if (newWrapper != oldWrapper) {
×
819
                    changed = true;
×
820
                }
821
                if (newWrapper == null) {
×
822
                    outputBufferIt.remove();
×
823
                } else {
824
                    outputBufferIt.set(newWrapper);
×
825
                }
826
            }
×
827

828
            // If at least one ingredient was inserted, force a sync observer update in the network.
829
            if (changed) {
×
830
                CraftingHelpers.beforeCalculateCraftingJobs(network, getChannelCrafting());
×
831
            }
832
        }
×
833

834
        /**
835
         * Iterate over all positions that *could* accept the given instance,
836
         * and force an observation over them.
837
         *
838
         * This is necessary to ensure that we have the latest state indexed right before insertion.
839
         * This allows us to force another observation right after the insertion,
840
         * which will guarantee that we will track the expected diff events as result.
841
         *
842
         * @param oldWrapper The ingredient to attempt to insert (simulated).
843
         * @param <T> Ingredient type.
844
         * @param <M> Match flags.
845
         */
846
        protected <T, M> void forceObservationOnInsertable(IngredientInstanceWrapper<T, M> oldWrapper) {
847
            IIngredientMatcher<T, M> matcher = oldWrapper.getComponent().getMatcher();
×
848
            IPositionedAddonsNetworkIngredients<T, M> ingredientsNetwork = CraftingHelpers.getIngredientsNetwork(network, oldWrapper.getComponent()).orElse(null);
×
849
            if (ingredientsNetwork != null) {
×
850
                boolean marked = false;
×
851
                INetworkIngredientsChannel<?, ?> ingredientsNetworkChannel = ingredientsNetwork.getChannelInternal(this.getChannelCrafting());
×
852
                T instance = oldWrapper.getInstance();
×
853
                for (PartPos position : ingredientsNetworkChannel.findNonFullPositions()) {
×
854
                    T instanceOut = ingredientsNetwork.getPositionedStorage(position).insert(instance, true);
×
855
                    if (!matcher.matchesExactly(instanceOut, instance)) {
×
856
                        marked = true;
×
857
                        instance = instanceOut;
×
858
                        ingredientsNetwork.scheduleObservationForced(this.getChannelCrafting(), position);
×
859
                        if (matcher.isEmpty(instance)) {
×
860
                            break;
×
861
                        }
862
                    }
863
                }
×
864

865
                if (marked || ingredientsNetwork.isObservationForcedPending(channel)) {
×
866
                    ingredientsNetwork.runObserverSync();
×
867
                }
868
            }
869
        }
×
870
    }
871
}
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