Skip to content

Commit

Permalink
Optimize uid lookups, recipe transfer, and make recipe layout creatio…
Browse files Browse the repository at this point in the history
…n run in background
  • Loading branch information
mezz committed Oct 3, 2024
1 parent 601e452 commit bc0098d
Show file tree
Hide file tree
Showing 28 changed files with 447 additions and 316 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import it.unimi.dsi.fastutil.Hash;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.gui.IRecipeLayoutDrawable;
import mezz.jei.api.gui.ingredient.IRecipeSlotView;
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
import mezz.jei.api.helpers.IStackHelper;
import mezz.jei.api.ingredients.ITypedIngredient;
import mezz.jei.api.ingredients.subtypes.UidContext;
import mezz.jei.api.recipe.category.IRecipeCategory;
import mezz.jei.api.recipe.transfer.IRecipeTransferError;
Expand All @@ -20,9 +22,10 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.annotation.Nullable;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -183,30 +186,18 @@ public static RecipeTransferOperationsResult getRecipeTransferOperations(
.filter(r -> !r.isEmpty())
.toList();

UidHashStrategy uidHashStrategy = new UidHashStrategy(stackhelper);

for (Map.Entry<Slot, ItemStack> slotTuple : availableItemStacks.entrySet()) {
ItemStack slotItemStack = slotTuple.getValue();
Object slotItemStackUid = stackhelper.getUidForStack(slotItemStack, UidContext.Ingredient);

for (IRecipeSlotView ingredient : nonEmptyRequiredStacks) {
Set<Object> ingredientUids = slotUidCache.computeIfAbsent(ingredient, s ->
s.getItemStacks()
.map(i -> stackhelper.getUidForStack(i, UidContext.Ingredient))
.collect(Collectors.toSet())
);
Set<Object> ingredientUids = slotUidCache.computeIfAbsent(ingredient, s -> calculateUids(s, stackhelper));

if (ingredientUids.contains(slotItemStackUid)) {
relevantSlots
.computeIfAbsent(ingredient, it -> new Object2ObjectOpenCustomHashMap<>(new Hash.Strategy<>() {
@Override
public int hashCode(ItemStack o) {
return o.getItem().hashCode();
}

@Override
public boolean equals(ItemStack a, ItemStack b) {
return stackhelper.isEquivalent(a, b, UidContext.Ingredient);
}
}))
.computeIfAbsent(ingredient, it -> new Object2ObjectOpenCustomHashMap<>(uidHashStrategy))
.computeIfAbsent(slotItemStack, it -> new ArrayList<>())
.add(new PhantomSlotState(slotTuple.getKey(), slotItemStack));
}
Expand Down Expand Up @@ -298,6 +289,22 @@ public boolean equals(ItemStack a, ItemStack b) {
return transferOperations;
}

private static Set<Object> calculateUids(IRecipeSlotView recipeSlotView, IStackHelper stackhelper) {
List<@Nullable ITypedIngredient<?>> allIngredientsList = recipeSlotView.getAllIngredientsList();
Set<Object> uids = new HashSet<>(allIngredientsList.size());
for (ITypedIngredient<?> typedIngredient : allIngredientsList) {
if (typedIngredient == null) {
continue;
}
ITypedIngredient<ItemStack> typedItemStack = typedIngredient.cast(VanillaTypes.ITEM_STACK);
if (typedItemStack != null) {
Object uid = stackhelper.getUidForStack(typedItemStack, UidContext.Ingredient);
uids.add(uid);
}
}
return uids;
}

private record PhantomSlotState(Slot slot, ItemStack itemStack) {}

private record PhantomSlotStateList(List<PhantomSlotState> stateList, long totalItemCount) {
Expand All @@ -315,4 +322,22 @@ public PhantomSlotState getFirstNonEmpty() {
return null;
}
}

private static class UidHashStrategy implements Hash.Strategy<ItemStack> {
private final IStackHelper stackhelper;

public UidHashStrategy(IStackHelper stackhelper) {
this.stackhelper = stackhelper;
}

@Override
public int hashCode(ItemStack o) {
return o.getItem().hashCode();
}

@Override
public boolean equals(ItemStack a, ItemStack b) {
return stackhelper.isEquivalent(a, b, UidContext.Ingredient);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ default Object getUid(V ingredient, UidContext context) {
return getUniqueId(ingredient, context);
}


/**
* Unique ID for use in comparing and looking up ingredients.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,22 @@ default <V> V getCastIngredient(IIngredientType<V> ingredientType) {
return ingredientType.getCastIngredient(getIngredient());
}

/**
* @return this instance, only if it matches the given type.
* This is useful when handling a wildcard generic instance of `ITypedIngredient<?>`.
*
* @since 19.19.6
*/
@Nullable
default <V> ITypedIngredient<V> cast(IIngredientType<V> ingredientType) {
if (getType().equals(ingredientType)) {
@SuppressWarnings("unchecked")
ITypedIngredient<V> cast = (ITypedIngredient<V>) this;
return cast;
}
return null;
}

/**
* @return the ingredient's base ingredient. (For example, an ItemStack's base ingredient is the Item)
*
Expand Down
16 changes: 16 additions & 0 deletions CommonApi/src/main/java/mezz/jei/api/recipe/IRecipeManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,22 @@ public interface IRecipeManager {
*/
void unhideRecipeCategory(RecipeType<?> recipeType);

/**
* Returns a drawable recipe layout, for addons that want to draw the layouts somewhere.
* If there is something wrong and the recipe layout crashes, this will display an error recipe instead.
*
* @param recipeCategory the recipe category that the recipe belongs to
* @param recipe the specific recipe to draw.
* @param focusGroup the focuses of the recipe layout.
*
* @since 19.19.6
*/
<T> IRecipeLayoutDrawable<T> createRecipeLayoutDrawableOrShowError(
IRecipeCategory<T> recipeCategory,
T recipe,
IFocusGroup focusGroup
);

/**
* Returns a drawable recipe layout, for addons that want to draw the layouts somewhere.
*
Expand Down
16 changes: 16 additions & 0 deletions Gui/src/main/java/mezz/jei/gui/bookmarks/BookmarkList.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
import mezz.jei.api.ingredients.ITypedIngredient;
import mezz.jei.api.recipe.IFocusFactory;
import mezz.jei.api.recipe.IRecipeManager;
import mezz.jei.api.recipe.RecipeType;
import mezz.jei.api.runtime.IIngredientManager;
import mezz.jei.common.config.IClientConfig;
import mezz.jei.gui.config.IBookmarkConfig;
import mezz.jei.gui.overlay.IIngredientGridSource;
import mezz.jei.gui.overlay.elements.IElement;
import net.minecraft.core.RegistryAccess;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.HashSet;
Expand Down Expand Up @@ -150,6 +152,20 @@ public List<IElement<?>> getElements() {
.toList();
}

@Nullable
public <R> RecipeBookmark<R,?> getMatchingBookmark(RecipeType<R> recipeType, R recipe) {
for (IBookmark bookmark : bookmarksList) {
if (bookmark instanceof RecipeBookmark<?,?> recipeBookmark) {
if (recipeBookmark.isRecipe(recipeType, recipe)) {
@SuppressWarnings("unchecked")
RecipeBookmark<R, ?> castBookmark = (RecipeBookmark<R, ?>) recipeBookmark;
return castBookmark;
}
}
}
return null;
}

public boolean isEmpty() {
return bookmarksSet.isEmpty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static <T> IngredientBookmark<T> create(ITypedIngredient<T> typedIngredie
IIngredientType<T> type = typedIngredient.getType();
typedIngredient = ingredientManager.normalizeTypedIngredient(typedIngredient);
IIngredientHelper<T> ingredientHelper = ingredientManager.getIngredientHelper(type);
Object uniqueId = ingredientHelper.getUid(typedIngredient.getIngredient(), UidContext.Ingredient);
Object uniqueId = ingredientHelper.getUid(typedIngredient, UidContext.Ingredient);
return new IngredientBookmark<>(typedIngredient, uniqueId);
}

Expand Down
25 changes: 20 additions & 5 deletions Gui/src/main/java/mezz/jei/gui/bookmarks/RecipeBookmark.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
import mezz.jei.api.ingredients.ITypedIngredient;
import mezz.jei.api.recipe.RecipeIngredientRole;
import mezz.jei.api.recipe.RecipeType;
import mezz.jei.api.recipe.category.IRecipeCategory;
import mezz.jei.api.runtime.IIngredientManager;
import mezz.jei.gui.overlay.elements.IElement;
Expand All @@ -24,35 +25,36 @@ public class RecipeBookmark<R, I> implements IBookmark {
private final boolean displayIsOutput;
private boolean visible = true;

public static <T> Optional<RecipeBookmark<T, ?>> create(
@Nullable
public static <T> RecipeBookmark<T, ?> create(
IRecipeLayoutDrawable<T> recipeLayoutDrawable,
IIngredientManager ingredientManager
) {
T recipe = recipeLayoutDrawable.getRecipe();
IRecipeCategory<T> recipeCategory = recipeLayoutDrawable.getRecipeCategory();
ResourceLocation recipeUid = recipeCategory.getRegistryName(recipe);
if (recipeUid == null) {
return Optional.empty();
return null;
}

IRecipeSlotsView recipeSlotsView = recipeLayoutDrawable.getRecipeSlotsView();
{
ITypedIngredient<?> output = findFirst(recipeSlotsView, RecipeIngredientRole.OUTPUT);
if (output != null) {
output = ingredientManager.normalizeTypedIngredient(output);
return Optional.of(new RecipeBookmark<>(recipeCategory, recipe, recipeUid, output, true));
return new RecipeBookmark<>(recipeCategory, recipe, recipeUid, output, true);
}
}

{
ITypedIngredient<?> input = findFirst(recipeSlotsView, RecipeIngredientRole.INPUT);
if (input != null) {
input = ingredientManager.normalizeTypedIngredient(input);
return Optional.of(new RecipeBookmark<>(recipeCategory, recipe, recipeUid, input, false));
return new RecipeBookmark<>(recipeCategory, recipe, recipeUid, input, false);
}
}

return Optional.empty();
return null;
}

@Nullable
Expand Down Expand Up @@ -145,4 +147,17 @@ public String toString() {
", visible=" + visible +
'}';
}

public <T> boolean isRecipe(RecipeType<T> otherType, T otherRecipe) {
RecipeType<R> recipeType = recipeCategory.getRecipeType();
if (recipeType.equals(otherType)) {
Class<? extends R> recipeClass = recipeType.getRecipeClass();
if (recipeClass.isInstance(otherRecipe)) {
R castRecipe = recipeClass.cast(otherRecipe);
ResourceLocation otherUid = recipeCategory.getRegistryName(castRecipe);
return recipeUid.equals(otherUid);
}
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public <V> Optional<IListElement<V>> searchForMatchingElement(
) {
V ingredient = typedIngredient.getIngredient();
IIngredientType<V> type = typedIngredient.getType();
Function<ITypedIngredient<V>, Object> uidFunction = (i) -> ingredientHelper.getUid(i.getIngredient(), UidContext.Ingredient);
Function<ITypedIngredient<V>, Object> uidFunction = (i) -> ingredientHelper.getUid(i, UidContext.Ingredient);
Object ingredientUid = uidFunction.apply(typedIngredient);
String lowercaseDisplayName = DisplayNameUtil.getLowercaseDisplayNameForSearch(ingredient, ingredientHelper);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@ private static <T> void addToSummary(ITypedIngredient<T> typedIngredient, IIngre
private static <T> Object getUid(ITypedIngredient<T> typedIngredient, IIngredientManager ingredientManager) {
IIngredientType<T> type = typedIngredient.getType();
IIngredientHelper<T> ingredientHelper = ingredientManager.getIngredientHelper(type);
T ingredient = typedIngredient.getIngredient();
return ingredientHelper.getUid(ingredient, UidContext.Recipe);
return ingredientHelper.getUid(typedIngredient, UidContext.Recipe);
}

@Override
Expand Down
5 changes: 4 additions & 1 deletion Gui/src/main/java/mezz/jei/gui/recipes/IRecipeGuiLogic.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import mezz.jei.api.recipe.IFocusGroup;
import mezz.jei.api.recipe.RecipeType;
import mezz.jei.api.recipe.category.IRecipeCategory;
import mezz.jei.gui.bookmarks.BookmarkList;
import mezz.jei.gui.recipes.lookups.IFocusedRecipes;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -61,6 +62,8 @@ public interface IRecipeGuiLogic {
List<RecipeLayoutWithButtons<?>> getVisibleRecipeLayoutsWithButtons(
int availableHeight,
int minRecipePadding,
@Nullable AbstractContainerMenu container
@Nullable AbstractContainerMenu container,
BookmarkList bookmarkList,
RecipesGui recipesGui
);
}
15 changes: 11 additions & 4 deletions Gui/src/main/java/mezz/jei/gui/recipes/RecipeBookmarkButton.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import mezz.jei.common.gui.JeiTooltip;
import mezz.jei.common.gui.textures.Textures;
import mezz.jei.gui.bookmarks.BookmarkList;
import mezz.jei.gui.bookmarks.IBookmark;
import mezz.jei.gui.bookmarks.RecipeBookmark;
import mezz.jei.gui.elements.GuiIconToggleButton;
import mezz.jei.gui.input.UserInput;
Expand All @@ -18,17 +19,23 @@

public class RecipeBookmarkButton extends GuiIconToggleButton {
private final BookmarkList bookmarks;
private final @Nullable RecipeBookmark<?, ?> recipeBookmark;
private final @Nullable IBookmark recipeBookmark;
private boolean bookmarked;

public static RecipeBookmarkButton create(
IRecipeLayoutDrawable<?> recipeLayout,
IIngredientManager ingredientManager,
BookmarkList bookmarks
) {
RecipeBookmark<?, ?> recipeBookmark = RecipeBookmark.create(recipeLayout, ingredientManager)
.orElse(null);
IBookmark recipeBookmark = RecipeBookmark.create(recipeLayout, ingredientManager);
return create(recipeLayout, bookmarks, recipeBookmark);
}

public static RecipeBookmarkButton create(
IRecipeLayoutDrawable<?> recipeLayout,
BookmarkList bookmarks,
@Nullable IBookmark recipeBookmark
) {
Textures textures = Internal.getTextures();
IDrawable icon = textures.getRecipeBookmark();
Rect2i area = recipeLayout.getRecipeBookmarkButtonArea();
Expand All @@ -41,7 +48,7 @@ public static RecipeBookmarkButton create(
return recipeBookmarkButton;
}

private RecipeBookmarkButton(IDrawable icon, BookmarkList bookmarks, @Nullable RecipeBookmark<?, ?> recipeBookmark) {
private RecipeBookmarkButton(IDrawable icon, BookmarkList bookmarks, @Nullable IBookmark recipeBookmark) {
super(icon, icon);

this.bookmarks = bookmarks;
Expand Down
Loading

0 comments on commit bc0098d

Please sign in to comment.