Recipe priority
Abalieno opened this issue ยท 10 comments
I was trying to figure out the way similar set of recipes are prioritized, but without being able to figure out the system.
So the first question is how the system is currently working. Locking slots and capacity doesn't seem to do anything, it only sets upper limits. It doesn't seem to enforce recipe selection. If a recipe has two outputs, I lock them both, but also have another recipe that has one output only, both these recipes are "valid". One is a subset of the other.
The question is: which one gets selected to run?
From my tests it looks like the "smaller" recipe is selected. There wasn't any randomness to it, with two valid recipes, with over 20 runs, only one was selected and the other ignored.
For the purpose of this request I'd like to have more control on this as packdev (and to be able to make more interesting automation).
The best option would be to have a priority system where, if two or more recipes are valid, they get prioritized by ID. This way I can customize their logic manually.
The other option, but I don't think there's any system that does recipe sorting, would be to default to the other way: go from the "larger" recipe to the smallest. This would also work because by locking slots and capacity you can obviously force the small recipe, and instead increase capacity to move back to the larger ones.
The current system doesn't seem to give any control instead, and if it defaults to the smallest then it simple "cuts" any larger batch/recipe.
I guess it's pointless but I don't like leaving "unknowns" behind.
It doesn't seem the case that the item name has any effect. I inverted the output in the two recipes above, and the first recipe in that image continues to be run even if the output was swapped.
So none of the answers seem to match observation. IDs don't matter, recipe order (that I can see) don't matter, recipes aren't randomly selected, and the item name of the output isn't affecting the recipe selection.
I thought there was maybe some caching that defaulted the recipe choice to the previously run recipe, so I modified the test so that I could force a "reset". But any time I stopped the reset run, the machine would default always to the previous recipe choice.
event.custom({
type: "modern_industrialization:hsla_blast",
eu: 4,
duration: 200,
item_inputs: [
{item: "minecraft:iron_ingot", amount: 9}
],
item_outputs: [
{item: "geggy:hsla_ingot", amount: 9}
]
}).id('aaa1')
event.custom({
type: "modern_industrialization:hsla_blast",
eu: 4,
duration: 200,
item_inputs: [
{item: "minecraft:iron_ingot", amount: 3}
],
item_outputs: [
{item: "geggy:hsla_ingot", amount: 3}
]
}).id('bbb2')
event.custom({
type: "modern_industrialization:hsla_blast",
eu: 4,
duration: 200,
item_inputs: [
{item: "minecraft:iron_ingot", amount: 1}
],
item_outputs: [
{item: "geggy:hsla_ingot", amount: 1}
]
}).id('ccc3')
This is the current one, not counting a fourth recipe I use to force resets. And for some reason always the one in the middle is the automatic choice. So this proves that the priority to the small set is ALSO wrong. And I then have no idea what makes it choose the second recipe.
it should select the first found recipe in the recipe list, which is generally in load order. order is not guaranteed which is why overlapping recipes are called "conflict"s not "option"s.
Mods like polymorph specifically were made to be the lazy way out of this with crafting recipes, but the issue affects all Minecraft recipes. The best option is to not create conflicting recipes. Pack devs are generally expected to remove conflicts by either changing or removing conflicting recipes.
Order by ID is not respected, so it doesn't follow the recipe list as shown in game.
And I'm deliberately nesting recipes for more interesting automation. Worse throughput leads to worse recipes. That's why I asked if it's possible to tweak recipe selection so that I have control and being able to do things in a more interesting way.
The system should select from "small" to "big". The point is whether or not this can be implemented easily or if it won't happen.
This is for automation, not manual selection.
By the way, Polymorph is a completely different context. On a crafting table if you add an ingredient you invalidate a recipe. Polymorph is only used when two identical input ingredients have different outputs.
The case described here is when a machine selects a subset of a larger recipe, even if more ingredients are already available. On a crafting table adding another ingredient cancels the recipe, in a machine the ingredient is ignored and the smaller recipe coexists with the larger. By locking the output you already have the same function of Polymorph built-in. What doesn't work is subsets.
Therefore the request to deal with priorities. There could be many ways, depending on the technical constraints that are set.
Adding an example:
Even if the ingredients of the second recipe are locked to their slots, only the first recipe is selected. I assume it defaults to the "smaller" available. Or maybe it's some weird load order. I know setting order by recipe ID doesn't matter.
In this example I can "force" the second recipe by locking the output slot to the second recipe ingot.
The problem is dealing with recipes with same outputs but different counts, used for varying batches and yields.
I've tried to modify both IDs and the recipe order, neither seem to affect the priority logic. Always the "smaller" recipe is selected if both are valid. So, if that's the case, it should be possible to invert that selection, since it's a much better logic to use considering you can limit the count on the slots and manually force smaller recipes.
Recipe priority causes problems when parts of the inputs go missing, and the machine unexpectedly switches recipe. If slot locking isn't enough then you'll have to tweak the recipes in your modpack.
I was going to go into the processing time for recipes, but looking at how the machines calculate recipes, sorting would work fine.
if implemented, it looks like
Unfortunately, you also run into the issue of it using the current recipe without a lookup if possible, so if someone runs out of mechanisms, it will then only do iron_only recipes until the machine stops. This is an optimization that is being done due to the processing time of lookups.
It might be possible as-is if you know what recipes you are adding to be use custom processing rules which grab the BE, grab the inventory, and check inputslots to see if it matches the other, known-conflicting recipe you added
https://github.com/AztechMC/Modern-Industrialization/blob/master/docs/ADDING_RECIPES.md#custom-process-conditions
psuedocode:
(context, recipe) => {
return !context.getBlockEntity().getInvetory().getItemStacks().subList(0, MY_MACHINE_INPUT_COUNT).map(confItemStack->confItemStack.getReference()).containsAll(MY_OTHER_RECIPE_ITEMSTACK_LIST);
},
you could build a function in kubejs that does the invalidation bit based on a list of "upgradable" recipes or something:
again psuedocode. and a bit unwieldy...
function isBestRecipe(recipe,inventoryList,recipeGroup) {
for (checkrecipe of MY_OVERRIDING_RECIPES[recipeGroup) {
if (recipe==checkrecipe) return true;
if (inventoryList.containsAll(checkrecipe)) return false;
}
return true;
}
(context, recipe) => {
return isBestRecipe(recipe.itemInputs,context.getBlockEntity().getInvetory().getItemStacks().subList(0, MY_MACHINE_INPUT_COUNT).map(confItemStack->confItemStack.getReference()),
},IRON_INGOT_PROCESSING_GROUP)
The goal in this case is to invalidate the recipe if the higher level recipe inputs are available (it would reset the overclocking/efficiency built up, of course)
I would still like to know how recipes are selected, and why in the example above only the first recipe is run in several tests I've done.
Doesn't explain what I wrote: "in the example above only the first recipe is run in several tests I've done"
Changing id, recipe order in script, locking slots in different way, several full reloads, breaking and reforming the structure several times, and so on. Only the first recipe is run, even when the first recipe in the image is ordered after the the other in the recipe browser.