Skip to content

Commit bc0098d

Browse files
committed
Optimize uid lookups, recipe transfer, and make recipe layout creation run in background
1 parent 601e452 commit bc0098d

28 files changed

+447
-316
lines changed

Common/src/main/java/mezz/jei/common/transfer/RecipeTransferUtil.java

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
import it.unimi.dsi.fastutil.Hash;
44
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
55
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap;
6+
import mezz.jei.api.constants.VanillaTypes;
67
import mezz.jei.api.gui.IRecipeLayoutDrawable;
78
import mezz.jei.api.gui.ingredient.IRecipeSlotView;
89
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
910
import mezz.jei.api.helpers.IStackHelper;
11+
import mezz.jei.api.ingredients.ITypedIngredient;
1012
import mezz.jei.api.ingredients.subtypes.UidContext;
1113
import mezz.jei.api.recipe.category.IRecipeCategory;
1214
import mezz.jei.api.recipe.transfer.IRecipeTransferError;
@@ -20,9 +22,10 @@
2022
import org.apache.logging.log4j.LogManager;
2123
import org.apache.logging.log4j.Logger;
2224

23-
import javax.annotation.Nullable;
25+
import org.jetbrains.annotations.Nullable;
2426
import java.util.ArrayList;
2527
import java.util.Collection;
28+
import java.util.HashSet;
2629
import java.util.IdentityHashMap;
2730
import java.util.List;
2831
import java.util.Map;
@@ -183,30 +186,18 @@ public static RecipeTransferOperationsResult getRecipeTransferOperations(
183186
.filter(r -> !r.isEmpty())
184187
.toList();
185188

189+
UidHashStrategy uidHashStrategy = new UidHashStrategy(stackhelper);
190+
186191
for (Map.Entry<Slot, ItemStack> slotTuple : availableItemStacks.entrySet()) {
187192
ItemStack slotItemStack = slotTuple.getValue();
188193
Object slotItemStackUid = stackhelper.getUidForStack(slotItemStack, UidContext.Ingredient);
189194

190195
for (IRecipeSlotView ingredient : nonEmptyRequiredStacks) {
191-
Set<Object> ingredientUids = slotUidCache.computeIfAbsent(ingredient, s ->
192-
s.getItemStacks()
193-
.map(i -> stackhelper.getUidForStack(i, UidContext.Ingredient))
194-
.collect(Collectors.toSet())
195-
);
196+
Set<Object> ingredientUids = slotUidCache.computeIfAbsent(ingredient, s -> calculateUids(s, stackhelper));
196197

197198
if (ingredientUids.contains(slotItemStackUid)) {
198199
relevantSlots
199-
.computeIfAbsent(ingredient, it -> new Object2ObjectOpenCustomHashMap<>(new Hash.Strategy<>() {
200-
@Override
201-
public int hashCode(ItemStack o) {
202-
return o.getItem().hashCode();
203-
}
204-
205-
@Override
206-
public boolean equals(ItemStack a, ItemStack b) {
207-
return stackhelper.isEquivalent(a, b, UidContext.Ingredient);
208-
}
209-
}))
200+
.computeIfAbsent(ingredient, it -> new Object2ObjectOpenCustomHashMap<>(uidHashStrategy))
210201
.computeIfAbsent(slotItemStack, it -> new ArrayList<>())
211202
.add(new PhantomSlotState(slotTuple.getKey(), slotItemStack));
212203
}
@@ -298,6 +289,22 @@ public boolean equals(ItemStack a, ItemStack b) {
298289
return transferOperations;
299290
}
300291

292+
private static Set<Object> calculateUids(IRecipeSlotView recipeSlotView, IStackHelper stackhelper) {
293+
List<@Nullable ITypedIngredient<?>> allIngredientsList = recipeSlotView.getAllIngredientsList();
294+
Set<Object> uids = new HashSet<>(allIngredientsList.size());
295+
for (ITypedIngredient<?> typedIngredient : allIngredientsList) {
296+
if (typedIngredient == null) {
297+
continue;
298+
}
299+
ITypedIngredient<ItemStack> typedItemStack = typedIngredient.cast(VanillaTypes.ITEM_STACK);
300+
if (typedItemStack != null) {
301+
Object uid = stackhelper.getUidForStack(typedItemStack, UidContext.Ingredient);
302+
uids.add(uid);
303+
}
304+
}
305+
return uids;
306+
}
307+
301308
private record PhantomSlotState(Slot slot, ItemStack itemStack) {}
302309

303310
private record PhantomSlotStateList(List<PhantomSlotState> stateList, long totalItemCount) {
@@ -315,4 +322,22 @@ public PhantomSlotState getFirstNonEmpty() {
315322
return null;
316323
}
317324
}
325+
326+
private static class UidHashStrategy implements Hash.Strategy<ItemStack> {
327+
private final IStackHelper stackhelper;
328+
329+
public UidHashStrategy(IStackHelper stackhelper) {
330+
this.stackhelper = stackhelper;
331+
}
332+
333+
@Override
334+
public int hashCode(ItemStack o) {
335+
return o.getItem().hashCode();
336+
}
337+
338+
@Override
339+
public boolean equals(ItemStack a, ItemStack b) {
340+
return stackhelper.isEquivalent(a, b, UidContext.Ingredient);
341+
}
342+
}
318343
}

CommonApi/src/main/java/mezz/jei/api/ingredients/IIngredientHelper.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ default Object getUid(V ingredient, UidContext context) {
5555
return getUniqueId(ingredient, context);
5656
}
5757

58-
5958
/**
6059
* Unique ID for use in comparing and looking up ingredients.
6160
*

CommonApi/src/main/java/mezz/jei/api/ingredients/ITypedIngredient.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,22 @@ default <V> V getCastIngredient(IIngredientType<V> ingredientType) {
5454
return ingredientType.getCastIngredient(getIngredient());
5555
}
5656

57+
/**
58+
* @return this instance, only if it matches the given type.
59+
* This is useful when handling a wildcard generic instance of `ITypedIngredient<?>`.
60+
*
61+
* @since 19.19.6
62+
*/
63+
@Nullable
64+
default <V> ITypedIngredient<V> cast(IIngredientType<V> ingredientType) {
65+
if (getType().equals(ingredientType)) {
66+
@SuppressWarnings("unchecked")
67+
ITypedIngredient<V> cast = (ITypedIngredient<V>) this;
68+
return cast;
69+
}
70+
return null;
71+
}
72+
5773
/**
5874
* @return the ingredient's base ingredient. (For example, an ItemStack's base ingredient is the Item)
5975
*

CommonApi/src/main/java/mezz/jei/api/recipe/IRecipeManager.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,22 @@ public interface IRecipeManager {
118118
*/
119119
void unhideRecipeCategory(RecipeType<?> recipeType);
120120

121+
/**
122+
* Returns a drawable recipe layout, for addons that want to draw the layouts somewhere.
123+
* If there is something wrong and the recipe layout crashes, this will display an error recipe instead.
124+
*
125+
* @param recipeCategory the recipe category that the recipe belongs to
126+
* @param recipe the specific recipe to draw.
127+
* @param focusGroup the focuses of the recipe layout.
128+
*
129+
* @since 19.19.6
130+
*/
131+
<T> IRecipeLayoutDrawable<T> createRecipeLayoutDrawableOrShowError(
132+
IRecipeCategory<T> recipeCategory,
133+
T recipe,
134+
IFocusGroup focusGroup
135+
);
136+
121137
/**
122138
* Returns a drawable recipe layout, for addons that want to draw the layouts somewhere.
123139
*

Gui/src/main/java/mezz/jei/gui/bookmarks/BookmarkList.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
import mezz.jei.api.ingredients.ITypedIngredient;
66
import mezz.jei.api.recipe.IFocusFactory;
77
import mezz.jei.api.recipe.IRecipeManager;
8+
import mezz.jei.api.recipe.RecipeType;
89
import mezz.jei.api.runtime.IIngredientManager;
910
import mezz.jei.common.config.IClientConfig;
1011
import mezz.jei.gui.config.IBookmarkConfig;
1112
import mezz.jei.gui.overlay.IIngredientGridSource;
1213
import mezz.jei.gui.overlay.elements.IElement;
1314
import net.minecraft.core.RegistryAccess;
15+
import org.jetbrains.annotations.Nullable;
1416

1517
import java.util.ArrayList;
1618
import java.util.HashSet;
@@ -150,6 +152,20 @@ public List<IElement<?>> getElements() {
150152
.toList();
151153
}
152154

155+
@Nullable
156+
public <R> RecipeBookmark<R,?> getMatchingBookmark(RecipeType<R> recipeType, R recipe) {
157+
for (IBookmark bookmark : bookmarksList) {
158+
if (bookmark instanceof RecipeBookmark<?,?> recipeBookmark) {
159+
if (recipeBookmark.isRecipe(recipeType, recipe)) {
160+
@SuppressWarnings("unchecked")
161+
RecipeBookmark<R, ?> castBookmark = (RecipeBookmark<R, ?>) recipeBookmark;
162+
return castBookmark;
163+
}
164+
}
165+
}
166+
return null;
167+
}
168+
153169
public boolean isEmpty() {
154170
return bookmarksSet.isEmpty();
155171
}

Gui/src/main/java/mezz/jei/gui/bookmarks/IngredientBookmark.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public static <T> IngredientBookmark<T> create(ITypedIngredient<T> typedIngredie
2020
IIngredientType<T> type = typedIngredient.getType();
2121
typedIngredient = ingredientManager.normalizeTypedIngredient(typedIngredient);
2222
IIngredientHelper<T> ingredientHelper = ingredientManager.getIngredientHelper(type);
23-
Object uniqueId = ingredientHelper.getUid(typedIngredient.getIngredient(), UidContext.Ingredient);
23+
Object uniqueId = ingredientHelper.getUid(typedIngredient, UidContext.Ingredient);
2424
return new IngredientBookmark<>(typedIngredient, uniqueId);
2525
}
2626

Gui/src/main/java/mezz/jei/gui/bookmarks/RecipeBookmark.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
66
import mezz.jei.api.ingredients.ITypedIngredient;
77
import mezz.jei.api.recipe.RecipeIngredientRole;
8+
import mezz.jei.api.recipe.RecipeType;
89
import mezz.jei.api.recipe.category.IRecipeCategory;
910
import mezz.jei.api.runtime.IIngredientManager;
1011
import mezz.jei.gui.overlay.elements.IElement;
@@ -24,35 +25,36 @@ public class RecipeBookmark<R, I> implements IBookmark {
2425
private final boolean displayIsOutput;
2526
private boolean visible = true;
2627

27-
public static <T> Optional<RecipeBookmark<T, ?>> create(
28+
@Nullable
29+
public static <T> RecipeBookmark<T, ?> create(
2830
IRecipeLayoutDrawable<T> recipeLayoutDrawable,
2931
IIngredientManager ingredientManager
3032
) {
3133
T recipe = recipeLayoutDrawable.getRecipe();
3234
IRecipeCategory<T> recipeCategory = recipeLayoutDrawable.getRecipeCategory();
3335
ResourceLocation recipeUid = recipeCategory.getRegistryName(recipe);
3436
if (recipeUid == null) {
35-
return Optional.empty();
37+
return null;
3638
}
3739

3840
IRecipeSlotsView recipeSlotsView = recipeLayoutDrawable.getRecipeSlotsView();
3941
{
4042
ITypedIngredient<?> output = findFirst(recipeSlotsView, RecipeIngredientRole.OUTPUT);
4143
if (output != null) {
4244
output = ingredientManager.normalizeTypedIngredient(output);
43-
return Optional.of(new RecipeBookmark<>(recipeCategory, recipe, recipeUid, output, true));
45+
return new RecipeBookmark<>(recipeCategory, recipe, recipeUid, output, true);
4446
}
4547
}
4648

4749
{
4850
ITypedIngredient<?> input = findFirst(recipeSlotsView, RecipeIngredientRole.INPUT);
4951
if (input != null) {
5052
input = ingredientManager.normalizeTypedIngredient(input);
51-
return Optional.of(new RecipeBookmark<>(recipeCategory, recipe, recipeUid, input, false));
53+
return new RecipeBookmark<>(recipeCategory, recipe, recipeUid, input, false);
5254
}
5355
}
5456

55-
return Optional.empty();
57+
return null;
5658
}
5759

5860
@Nullable
@@ -145,4 +147,17 @@ public String toString() {
145147
", visible=" + visible +
146148
'}';
147149
}
150+
151+
public <T> boolean isRecipe(RecipeType<T> otherType, T otherRecipe) {
152+
RecipeType<R> recipeType = recipeCategory.getRecipeType();
153+
if (recipeType.equals(otherType)) {
154+
Class<? extends R> recipeClass = recipeType.getRecipeClass();
155+
if (recipeClass.isInstance(otherRecipe)) {
156+
R castRecipe = recipeClass.cast(otherRecipe);
157+
ResourceLocation otherUid = recipeCategory.getRegistryName(castRecipe);
158+
return recipeUid.equals(otherUid);
159+
}
160+
}
161+
return false;
162+
}
148163
}

Gui/src/main/java/mezz/jei/gui/ingredients/IngredientFilter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ public <V> Optional<IListElement<V>> searchForMatchingElement(
136136
) {
137137
V ingredient = typedIngredient.getIngredient();
138138
IIngredientType<V> type = typedIngredient.getType();
139-
Function<ITypedIngredient<V>, Object> uidFunction = (i) -> ingredientHelper.getUid(i.getIngredient(), UidContext.Ingredient);
139+
Function<ITypedIngredient<V>, Object> uidFunction = (i) -> ingredientHelper.getUid(i, UidContext.Ingredient);
140140
Object ingredientUid = uidFunction.apply(typedIngredient);
141141
String lowercaseDisplayName = DisplayNameUtil.getLowercaseDisplayNameForSearch(ingredient, ingredientHelper);
142142

Gui/src/main/java/mezz/jei/gui/overlay/bookmarks/IngredientsTooltipComponent.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,7 @@ private static <T> void addToSummary(ITypedIngredient<T> typedIngredient, IIngre
7272
private static <T> Object getUid(ITypedIngredient<T> typedIngredient, IIngredientManager ingredientManager) {
7373
IIngredientType<T> type = typedIngredient.getType();
7474
IIngredientHelper<T> ingredientHelper = ingredientManager.getIngredientHelper(type);
75-
T ingredient = typedIngredient.getIngredient();
76-
return ingredientHelper.getUid(ingredient, UidContext.Recipe);
75+
return ingredientHelper.getUid(typedIngredient, UidContext.Recipe);
7776
}
7877

7978
@Override

Gui/src/main/java/mezz/jei/gui/recipes/IRecipeGuiLogic.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import mezz.jei.api.recipe.IFocusGroup;
55
import mezz.jei.api.recipe.RecipeType;
66
import mezz.jei.api.recipe.category.IRecipeCategory;
7+
import mezz.jei.gui.bookmarks.BookmarkList;
78
import mezz.jei.gui.recipes.lookups.IFocusedRecipes;
89
import net.minecraft.world.inventory.AbstractContainerMenu;
910
import org.jetbrains.annotations.Nullable;
@@ -61,6 +62,8 @@ public interface IRecipeGuiLogic {
6162
List<RecipeLayoutWithButtons<?>> getVisibleRecipeLayoutsWithButtons(
6263
int availableHeight,
6364
int minRecipePadding,
64-
@Nullable AbstractContainerMenu container
65+
@Nullable AbstractContainerMenu container,
66+
BookmarkList bookmarkList,
67+
RecipesGui recipesGui
6568
);
6669
}

0 commit comments

Comments
 (0)