EMI

EMI

14M Downloads

JEI Crash when attempting to auto move items using TerraFirmaCraft's extended crafting recipes

Pyritie opened this issue · 1 comments

commented

Describe the bug, attach relevant log files, and describe what environment the issue occurs in

We've been getting a couple reports here recently, which didn't happen in some older versions of EMI.

After talking to the author of KubeJS TFC, since we use that mod to make TFC recipes, he thinks the issue might have something to do with EMI's JEI integration parsing TFC's extended crafting recipes as EmiCraftingRecipes instead of JemiRecipes. He guesses it's "because Math.max(9, recipe.getInputs().size()) - blankedSlots results in a index greater than the length of the recipe's ingredients"?

For reference these are the kind of recipes that are causing issues.

commented

KubeJS TFC dev here, I've done some proper analysis with a remote debugger on the TFG pack and believe I've found the cause of the crash

When EMI iterates over the CRAFTING recipe type in its VanillaPlugin, any recipes which are not explicitly handled (such as TFC's delegate recipe types) end up being handled by

if (!recipe.getIngredients().isEmpty() && !EmiPort.getOutput(recipe).isEmpty() && recipe.fits(3, 3)) {
boolean shapeless = recipe.fits(1, recipe.getIngredients().size()) && recipe.fits(recipe.getIngredients().size(), 1);
List<EmiIngredient> input;
if (shapeless) {
input = recipe.getIngredients().stream().map(EmiIngredient::of).toList();
} else {
int width = recipe.fits(2, 3) ? recipe.fits(1, 3) ? 1 : 2 : 3;
input = Lists.newArrayList();
for (int i = 0; i < recipe.getIngredients().size(); i++) {
input.add(EmiIngredient.of(recipe.getIngredients().get(i)));
if ((i + 1) % width == 0) {
for (int j = width; j < 3; j++) {
input.add(EmiStack.EMPTY);
}
}
}
}
EmiShapedRecipe.setRemainders(input, recipe);
addRecipeSafe(registry, () -> new EmiCraftingRecipe(input, EmiStack.of(EmiPort.getOutput(recipe)), id, shapeless));

which expands the width, but not the height of the input array of recipes determined to be shaped

So for a 2x2 recipe using one TFC's shaped delegate recipe types, as TFG is doing here, the input list will only have a length of six when the EmiCraftingRecipe is constructed

This causes problems in the JEI integration layer here

if (ecr.canFit(1, 3)) {
addBlankIngredients(builder, slotWidgets, 1, RecipeIngredientRole.INPUT);
blankedSlots += 1;
} else if (ecr.canFit(3, 1) || (ecr.canFit(3, 2) && !ecr.canFit(2, 2))) {
addBlankIngredients(builder, slotWidgets, 3, RecipeIngredientRole.INPUT);
blankedSlots += 3;
}
addIngredients(builder, slotWidgets, recipe.getInputs().subList(0, Math.max(9, recipe.getInputs().size()) - blankedSlots), RecipeIngredientRole.INPUT);

where blank slots are added to the slot view during recipe transfer1

The 2x2 recipe (with an input list of length 6) doesn't fit in 1x3, skipping adding one blank slot. 3 blank spots don't end up being added as the #canFit calls evaluate to (false || (true && !true)) or simply false

Which brings us to recipe.getInputs().subList(0, Math.max(9, recipe.getInputs().size()) - blankedSlots) with 0 blanked slots and 6 inputs and a #subList being called with 0 and 9, causing an IndexOutOfBoundsException and the game to crash

As far as I can tell, the creation of the input list in VanillaPlugin should add empty ingredients until the list is 9 long, as is done in EmiShapedRecipe#padIngredients to fix this

For now, TFG can just have three tall recipes and not crash

hopefully this is coherent enough to understand

Footnotes

  1. This only happens with Sophisticated Backpacks backpacks with crafting upgrades, presumably the regular crafting menu is fully handled by EMI and thus doesn't have to go through JEI