diff --git a/src/main/java/org/cyclops/integratedcrafting/part/PartTypeInterfaceCrafting.java b/src/main/java/org/cyclops/integratedcrafting/part/PartTypeInterfaceCrafting.java index 9b9e18eb..c43e1e23 100644 --- a/src/main/java/org/cyclops/integratedcrafting/part/PartTypeInterfaceCrafting.java +++ b/src/main/java/org/cyclops/integratedcrafting/part/PartTypeInterfaceCrafting.java @@ -7,6 +7,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.IntArraySet; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; @@ -159,7 +160,7 @@ protected PartTypeInterfaceCrafting.State constructDefaultState() { @Override public void afterNetworkReAlive(INetwork network, IPartNetwork partNetwork, PartTarget target, PartTypeInterfaceCrafting.State state) { super.afterNetworkReAlive(network, partNetwork, target, state); - addTargetToNetwork(network, target, state); + addTargetToNetwork(network, target, state, true); } @Override @@ -171,7 +172,7 @@ public void onNetworkRemoval(INetwork network, IPartNetwork partNetwork, PartTar @Override public void onNetworkAddition(INetwork network, IPartNetwork partNetwork, PartTarget target, PartTypeInterfaceCrafting.State state) { super.onNetworkAddition(network, partNetwork, target, state); - addTargetToNetwork(network, target, state); + addTargetToNetwork(network, target, state, true); } @Override @@ -180,19 +181,19 @@ public void setPriorityAndChannel(INetwork network, IPartNetwork partNetwork, Pa // so we have to re-add it. removeTargetFromNetwork(network, target.getTarget(), state); super.setPriorityAndChannel(network, partNetwork, target, state, priority, channel); - addTargetToNetwork(network, target, state); + addTargetToNetwork(network, target, state, false); } protected Capability getNetworkCapability() { return CraftingNetworkConfig.CAPABILITY; } - protected void addTargetToNetwork(INetwork network, PartTarget pos, PartTypeInterfaceCrafting.State state) { + protected void addTargetToNetwork(INetwork network, PartTarget pos, PartTypeInterfaceCrafting.State state, boolean initialize) { network.getCapability(getNetworkCapability()) .ifPresent(craftingNetwork -> { int channelCrafting = state.getChannelCrafting(); state.setTarget(pos); - state.setNetworks(network, craftingNetwork, NetworkHelpers.getPartNetworkChecked(network), channelCrafting); + state.setNetworks(network, craftingNetwork, NetworkHelpers.getPartNetworkChecked(network), channelCrafting, initialize); state.setShouldAddToCraftingNetwork(true); }); } @@ -203,7 +204,7 @@ protected void removeTargetFromNetwork(INetwork network, PartPos pos, PartTypeIn network.getCapability(getNetworkCapability()) .ifPresent(n -> n.removeCraftingInterface(state.getChannelCrafting(), state)); } - state.setNetworks(null, null, null, -1); + state.setNetworks(null, null, null, -1, false); state.setTarget(null); } @@ -224,7 +225,7 @@ public void update(INetwork network, IPartNetwork partNetwork, PartTarget target // Init network data in part state if it has not been done yet. // This can occur when the part chunk is being reloaded. if (state.getCraftingNetwork() == null) { - addTargetToNetwork(network, target, state); + addTargetToNetwork(network, target, state, false); } int channel = state.getChannelCrafting(); @@ -251,7 +252,9 @@ public void update(INetwork network, IPartNetwork partNetwork, PartTarget target if (!slots.isEmpty()) { ICraftingNetwork craftingNetwork = network.getCapability(getNetworkCapability()).orElse(null); if (craftingNetwork != null) { - for (Integer slot : slots) { + IntSet slotsCopy = new IntOpenHashSet(slots); // Create a copy, to allow insertion into slots during this loop + slots.clear(); + for (Integer slot : slotsCopy) { // Remove the old recipe from the network Int2ObjectMap recipes = state.getRecipesIndexed(); IRecipeDefinition oldRecipe = recipes.get(slot); @@ -260,7 +263,10 @@ public void update(INetwork network, IPartNetwork partNetwork, PartTarget target } // Reload the recipe in the slot - state.reloadRecipe(slot); + // We simulate initialization for the first two ticks, as dependency variables may still be loading, + // and errored may only go away after these dependencies are fully loaded. + // Related to CyclopsMC/IntegratedCrafting#110 + state.reloadRecipe(slot, state.ticksAfterReload <= 1); // Add the new recipe to the network IRecipeDefinition newRecipe = recipes.get(slot); @@ -269,8 +275,10 @@ public void update(INetwork network, IPartNetwork partNetwork, PartTarget target } } } - slots.clear(); } + + // Internal tick counter + state.ticksAfterReload++; } @Nullable @@ -317,6 +325,8 @@ public void addDrops(PartTarget target, State state, List itemStacks, public static class State extends PartStateBase implements ICraftingInterface, ICraftingResultsSink { + protected int ticksAfterReload = 0; + private final CraftingJobHandler craftingJobHandler; private final SimpleInventory inventoryVariables; private final List> variableEvaluators; @@ -451,7 +461,7 @@ public int getChannelCrafting() { return channelCrafting; } - public void reloadRecipes() { + public void reloadRecipes(boolean initialize) { this.currentRecipes.clear(); this.recipeSlotMessages.clear(); this.recipeSlotValidated.clear(); @@ -469,7 +479,7 @@ public void onErrorsChanged() { } if (this.partNetwork != null) { for (int i = 0; i < getInventoryVariables().getContainerSize(); i++) { - reloadRecipe(i); + reloadRecipe(i, initialize); } } } @@ -484,7 +494,7 @@ private void setLocalErrors(int slot, List errors) { } } - protected void reloadRecipe(int slot) { + protected void reloadRecipe(int slot, boolean initialize) { this.currentRecipes.remove(slot); if (this.recipeSlotMessages.size() > slot) { this.recipeSlotMessages.remove(slot); @@ -528,7 +538,13 @@ protected void reloadRecipe(int slot) { this.recipeSlotMessages.put(slot, e.getErrorMessage()); } } else { - this.recipeSlotMessages.put(slot, Component.translatable("gui.integratedcrafting.partinterface.slot.message.norecipe")); + // If we're initializing, the variable might be referencing other variables that are not yet loaded. + // So let's retry once in the next tick. + if (initialize && evaluator.hasVariable()) { + this.delayedReloadRecipe(slot); + } else { + this.recipeSlotMessages.put(slot, Component.translatable("gui.integratedcrafting.partinterface.slot.message.norecipe")); + } } try { @@ -594,7 +610,7 @@ public void onDirty() { // Recalculate recipes if (getTarget() != null && !getTarget().getCenter().getPos().getLevel(true).isClientSide) { - reloadRecipes(); + reloadRecipes(false); } // Re-register to the network, to force an update for all new recipes @@ -612,12 +628,12 @@ public PartTarget getTarget() { } public void setNetworks(@Nullable INetwork network, @Nullable ICraftingNetwork craftingNetwork, - @Nullable IPartNetwork partNetwork, int channel) { + @Nullable IPartNetwork partNetwork, int channel, boolean initialize) { this.network = network; this.craftingNetwork = craftingNetwork; this.partNetwork = partNetwork; this.channel = channel; - reloadRecipes(); + reloadRecipes(initialize); if (network != null) { this.getCraftingJobHandler().reRegisterObservers(network); }