diff --git a/src/main/java/org/cyclops/integratedcrafting/GeneralConfig.java b/src/main/java/org/cyclops/integratedcrafting/GeneralConfig.java index 378069e94..9b19a75b2 100644 --- a/src/main/java/org/cyclops/integratedcrafting/GeneralConfig.java +++ b/src/main/java/org/cyclops/integratedcrafting/GeneralConfig.java @@ -40,7 +40,7 @@ public class GeneralConfig extends DummyConfig { @ConfigurableProperty(category = "machine", comment = "The maximum number of recipes that will be read from a list inside an enumerated crafting interface. Set to 0 for no limit.", minimalValue = 0, isCommandable = true, configLocation = ModConfig.Type.SERVER) public static int maxCraftingInterfaceEnumeratedRecipes = 4096; - @ConfigurableProperty(category = "machine", comment = "The minimal update frequency in ticks to use for enumerated crafting interfaces. Reading a list of recipes is more expensive than reading a single recipe, so this defaults higher than the regular crafting interface.", minimalValue = 1, configLocation = ModConfig.Type.SERVER) + @ConfigurableProperty(category = "machine", comment = "The minimal number of ticks between two recipe list reloads in enumerated crafting interfaces, which happen when the list variable changes. Reading a list of recipes is more expensive than reading a single recipe, so this defaults higher than the regular crafting interface update frequency.", minimalValue = 1, configLocation = ModConfig.Type.SERVER) public static int minCraftingInterfaceEnumeratedUpdateFreq = 20; @ConfigurableProperty(category = "machine", comment = "The maximum number of recipes that a crafting interface remembers crafting durations for, which are used to estimate the duration of crafting jobs. Set to 0 to disable recipe-specific estimations.", minimalValue = 0, isCommandable = true, configLocation = ModConfig.Type.SERVER) diff --git a/src/main/java/org/cyclops/integratedcrafting/core/part/PartTypeInterfaceCraftingVariableBase.java b/src/main/java/org/cyclops/integratedcrafting/core/part/PartTypeInterfaceCraftingVariableBase.java index 974981647..c80d6110d 100644 --- a/src/main/java/org/cyclops/integratedcrafting/core/part/PartTypeInterfaceCraftingVariableBase.java +++ b/src/main/java/org/cyclops/integratedcrafting/core/part/PartTypeInterfaceCraftingVariableBase.java @@ -156,7 +156,9 @@ public void update(INetwork network, IPartNetwork partNetwork, PartTarget target // Reload recipes if needed IntSet slots = state.getDelayedRecipeReloads(); - if (!slots.isEmpty()) { + long gameTime = target.getCenter().getPos().getLevel(true).getGameTime(); + if (!slots.isEmpty() && state.canReloadDelayedRecipes(gameTime)) { + state.lastDelayedRecipeReloadTick = gameTime; ICraftingNetwork craftingNetwork = network.getCapability(getNetworkCapability()).orElse(null); if (craftingNetwork != null) { IntSet slotsCopy = new IntOpenHashSet(slots); // Create a copy, to allow insertion into slots during this loop @@ -219,6 +221,7 @@ public static abstract class State

{ protected int ticksAfterReload = 0; + protected long lastDelayedRecipeReloadTick = Integer.MIN_VALUE; private final SimpleInventory inventoryVariables; private final List> variableEvaluators; @@ -463,6 +466,19 @@ private void setSlotRecipes(int slot, List recipes) { } } + /** + * @return The minimal number of ticks between two reloads of invalidated recipe variables. + */ + protected int getMinDelayedRecipeReloadInterval() { + return 0; + } + + protected boolean canReloadDelayedRecipes(long gameTime) { + // Never throttle during initialization, as dependency variables may still be loading. + return ticksAfterReload <= 1 + || gameTime - lastDelayedRecipeReloadTick >= getMinDelayedRecipeReloadInterval(); + } + protected void delayedReloadRecipe(int slot) { this.delayedRecipeReloads.add(slot); } diff --git a/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestsItemsCraftEnumerated.java b/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestsItemsCraftEnumerated.java index 6da336a84..7887ed2f9 100644 --- a/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestsItemsCraftEnumerated.java +++ b/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestsItemsCraftEnumerated.java @@ -15,6 +15,7 @@ import net.neoforged.neoforge.gametest.GameTestHolder; import net.neoforged.neoforge.gametest.PrefixGameTestTemplate; import org.apache.commons.lang3.tuple.Pair; +import org.cyclops.integratedcrafting.GeneralConfig; import org.cyclops.integratedcrafting.Reference; import org.cyclops.integratedcrafting.part.PartTypeInterfaceCraftingEnumerated; import org.cyclops.integratedcrafting.part.PartTypes; @@ -99,6 +100,37 @@ public void testItemsCraftEnumeratedCraftingTable(GameTestHelper helper) { }); } + /** + * The enumerated interface can process jobs as fast as the regular interface, + * as only reloads of its list are throttled. + */ + @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = TIMEOUT) + public void testItemsCraftEnumeratedMinUpdateInterval(GameTestHelper helper) { + GameTestHelpersIntegratedCrafting.INetworkPositions positions = + createBasicNetwork(helper, POS, PartTypes.INTERFACE_CRAFTING_ENUMERATED, Blocks.CRAFTING_TABLE); + PartTypeInterfaceCraftingEnumerated.State state = positions.interfaceStates().get(0); + + helper.assertValueEqual(PartTypes.INTERFACE_CRAFTING_ENUMERATED.getMinimumUpdateInterval(state), + GeneralConfig.minCraftingInterfaceUpdateFreq, "Minimum update interval is incorrect"); + GameTestHelpersIntegratedCrafting.setCraftingInterfaceUpdateInterval(positions.interfaces().get(0), + GeneralConfig.minCraftingInterfaceUpdateFreq); + + ChestBlockEntity chestIn = helper.getBlockEntity(POS.east()); + chestIn.setItem(0, new ItemStack(Items.OAK_PLANKS, 64)); + + state.getInventoryVariables().setItem(0, createVariableForRecipeList(helper.getLevel(), List.of( + Pair.of(RecipeType.CRAFTING, ResourceLocation.fromNamespaceAndPath("minecraft", "chest")) + ))); + + enableRecipeInWriter(helper, positions.writer(), new ItemStack(Items.CHEST)); + + helper.succeedWhen(() -> { + helper.assertValueEqual(state.getUpdateInterval(), GeneralConfig.minCraftingInterfaceUpdateFreq, + "Update interval is incorrect"); + helper.assertValueEqual(chestIn.getItem(1).getItem(), Items.CHEST, "Slot 1 item is incorrect"); + }); + } + /** * Replacing the list variable re-indexes the interface with the new recipes. */ diff --git a/src/main/java/org/cyclops/integratedcrafting/part/PartTypeInterfaceCraftingEnumerated.java b/src/main/java/org/cyclops/integratedcrafting/part/PartTypeInterfaceCraftingEnumerated.java index 1bcaf23ad..cfcd9ba8f 100644 --- a/src/main/java/org/cyclops/integratedcrafting/part/PartTypeInterfaceCraftingEnumerated.java +++ b/src/main/java/org/cyclops/integratedcrafting/part/PartTypeInterfaceCraftingEnumerated.java @@ -62,9 +62,10 @@ protected PartTypeInterfaceCraftingEnumerated getPartTypeInstance() { } @Override - protected int getDefaultUpdateInterval() { + protected int getMinDelayedRecipeReloadInterval() { // Reading a whole list of recipes is more expensive than reading a single recipe, // and reader-backed list variables are invalidated on every reader tick. + // Only reloads are throttled, so static lists can process jobs at the regular rate. return GeneralConfig.minCraftingInterfaceEnumeratedUpdateFreq; }