Skip to content

Commit e10b47c

Browse files
committed
Optimize by moving away from Optional and Stream in hot code paths
1 parent 13616d8 commit e10b47c

28 files changed

+347
-184
lines changed

CommonApi/src/main/java/mezz/jei/api/gui/ingredient/IRecipeSlotView.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@
1111
import net.minecraft.network.chat.Component;
1212
import net.minecraft.world.item.ItemStack;
1313
import org.jetbrains.annotations.ApiStatus;
14+
import org.jetbrains.annotations.Nullable;
15+
import org.jetbrains.annotations.Unmodifiable;
1416

1517
import java.util.Collection;
18+
import java.util.List;
1619
import java.util.Optional;
1720
import java.util.stream.Stream;
1821

@@ -30,7 +33,7 @@
3033
@ApiStatus.NonExtendable
3134
public interface IRecipeSlotView {
3235
/**
33-
* All ingredient variations that can be shown.
36+
* All ingredient variations that can be shown, ignoring focus and visibility.
3437
*
3538
* @see #getItemStacks() to limit to only ItemStack ingredients.
3639
* @see #getIngredients(IIngredientType) to limit to one type of ingredient.
@@ -39,6 +42,15 @@ public interface IRecipeSlotView {
3942
*/
4043
Stream<ITypedIngredient<?>> getAllIngredients();
4144

45+
/**
46+
* All ingredients, ignoring focus and visibility
47+
* null ingredients represent a "blank" drawn ingredient in the rotation.
48+
*
49+
* @since 19.19.5
50+
*/
51+
@Unmodifiable
52+
List<@Nullable ITypedIngredient<?>> getAllIngredientsList();
53+
4254
/**
4355
* The ingredient variation that is shown at this moment.
4456
* For ingredients that rotate through several values, this will change over time.

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,16 @@ default Object getGroupingUid(V ingredient) {
8080
return getWildcardId(ingredient);
8181
}
8282

83+
/**
84+
* Unique ID for use in grouping ingredients together.
85+
* This is used for hiding groups of ingredients together at once.
86+
*
87+
* @since 19.19.5
88+
*/
89+
default Object getGroupingUid(ITypedIngredient<V> typedIngredient) {
90+
return getGroupingUid(typedIngredient.getIngredient());
91+
}
92+
8393
/**
8494
* Return true if the given ingredient can have subtypes.
8595
* For example in the vanilla game an enchanted book may have subtypes, but an apple does not.
@@ -227,6 +237,17 @@ default boolean isHiddenFromRecipeViewersByTags(V ingredient) {
227237
.anyMatch(Tags.HIDDEN_FROM_RECIPE_VIEWERS::equals);
228238
}
229239

240+
/**
241+
* Return true if the given ingredient is hidden from recipe viewers by its tags.
242+
*
243+
* @see Tags#HIDDEN_FROM_RECIPE_VIEWERS
244+
*
245+
* @since 19.19.5
246+
*/
247+
default boolean isHiddenFromRecipeViewersByTags(ITypedIngredient<V> ingredient) {
248+
return isHiddenFromRecipeViewersByTags(ingredient.getIngredient());
249+
}
250+
230251
/**
231252
* Get information for error messages involving this ingredient.
232253
* Be extremely careful not to crash here, get as much useful info as possible.

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,18 @@ default Optional<T> castIngredient(@Nullable Object ingredient) {
4444
}
4545
return Optional.empty();
4646
}
47+
48+
/**
49+
* Helper to cast an unknown ingredient to this type if it matches.
50+
*
51+
* @since 19.19.5
52+
*/
53+
@Nullable
54+
default T getCastIngredient(@Nullable Object ingredient) {
55+
Class<? extends T> ingredientClass = getIngredientClass();
56+
if (ingredientClass.isInstance(ingredient)) {
57+
return ingredientClass.cast(ingredient);
58+
}
59+
return null;
60+
}
4761
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import mezz.jei.api.runtime.IIngredientManager;
55
import net.minecraft.world.item.ItemStack;
66
import org.jetbrains.annotations.ApiStatus;
7+
import org.jetbrains.annotations.Nullable;
78

89
import java.util.Optional;
910

@@ -42,6 +43,17 @@ default <V> Optional<V> getIngredient(IIngredientType<V> ingredientType) {
4243
return ingredientType.castIngredient(getIngredient());
4344
}
4445

46+
/**
47+
* @return the ingredient wrapped by this instance, only if it matches the given type.
48+
* This is useful when handling a wildcard generic instance of `ITypedIngredient<?>`.
49+
*
50+
* @since 19.19.5
51+
*/
52+
@Nullable
53+
default <V> V getCastIngredient(IIngredientType<V> ingredientType) {
54+
return ingredientType.getCastIngredient(getIngredient());
55+
}
56+
4557
/**
4658
* @return the ingredient's base ingredient. (For example, an ItemStack's base ingredient is the Item)
4759
*

CommonApi/src/main/java/mezz/jei/api/runtime/IIngredientManager.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import mezz.jei.api.registration.IIngredientAliasRegistration;
1616
import net.minecraft.client.renderer.Rect2i;
1717
import net.minecraft.world.item.ItemStack;
18+
import org.jetbrains.annotations.Nullable;
1819
import org.jetbrains.annotations.Unmodifiable;
1920

2021
import java.util.Collection;
@@ -101,6 +102,15 @@ default Collection<ItemStack> getAllItemStacks() {
101102
*/
102103
<V> void removeIngredientsAtRuntime(IIngredientType<V> ingredientType, Collection<V> ingredients);
103104

105+
/**
106+
* Helper method to get ingredient type for an ingredient.
107+
* Returns null if there is no known type for the given ingredient.
108+
*
109+
* @since 19.19.5
110+
*/
111+
@Nullable
112+
<V> IIngredientType<V> getIngredientType(V ingredient);
113+
104114
/**
105115
* Helper method to get ingredient type for an ingredient.
106116
* Returns {@link Optional#empty()} if there is no known type for the given ingredient.

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

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import mezz.jei.gui.overlay.elements.IElement;
1111
import mezz.jei.gui.overlay.elements.RecipeBookmarkElement;
1212
import net.minecraft.resources.ResourceLocation;
13+
import org.jetbrains.annotations.Nullable;
1314

1415
import java.util.Objects;
1516
import java.util.Optional;
@@ -36,22 +37,16 @@ public class RecipeBookmark<R, I> implements IBookmark {
3637

3738
IRecipeSlotsView recipeSlotsView = recipeLayoutDrawable.getRecipeSlotsView();
3839
{
39-
Optional<ITypedIngredient<?>> outputOptional = recipeSlotsView.getSlotViews(RecipeIngredientRole.OUTPUT).stream()
40-
.flatMap(IRecipeSlotView::getAllIngredients)
41-
.findFirst();
42-
if (outputOptional.isPresent()) {
43-
ITypedIngredient<?> output = outputOptional.get();
40+
ITypedIngredient<?> output = findFirst(recipeSlotsView, RecipeIngredientRole.OUTPUT);
41+
if (output != null) {
4442
output = ingredientManager.normalizeTypedIngredient(output);
4543
return Optional.of(new RecipeBookmark<>(recipeCategory, recipe, recipeUid, output, true));
4644
}
4745
}
4846

4947
{
50-
Optional<ITypedIngredient<?>> inputOptional = recipeSlotsView.getSlotViews(RecipeIngredientRole.INPUT).stream()
51-
.flatMap(IRecipeSlotView::getAllIngredients)
52-
.findFirst();
53-
if (inputOptional.isPresent()) {
54-
ITypedIngredient<?> input = inputOptional.get();
48+
ITypedIngredient<?> input = findFirst(recipeSlotsView, RecipeIngredientRole.INPUT);
49+
if (input != null) {
5550
input = ingredientManager.normalizeTypedIngredient(input);
5651
return Optional.of(new RecipeBookmark<>(recipeCategory, recipe, recipeUid, input, false));
5752
}
@@ -60,6 +55,21 @@ public class RecipeBookmark<R, I> implements IBookmark {
6055
return Optional.empty();
6156
}
6257

58+
@Nullable
59+
private static ITypedIngredient<?> findFirst(IRecipeSlotsView slotsView, RecipeIngredientRole role) {
60+
for (IRecipeSlotView slotView : slotsView.getSlotViews()) {
61+
if (slotView.getRole() != role) {
62+
continue;
63+
}
64+
for (ITypedIngredient<?> ingredient : slotView.getAllIngredientsList()) {
65+
if (ingredient != null) {
66+
return ingredient;
67+
}
68+
}
69+
}
70+
return null;
71+
}
72+
6373
public RecipeBookmark(
6474
IRecipeCategory<R> recipeCategory,
6575
R recipe,

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import mezz.jei.api.gui.IRecipeLayoutDrawable;
44
import mezz.jei.api.gui.ingredient.IRecipeSlotView;
5+
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
56
import mezz.jei.api.recipe.RecipeIngredientRole;
67
import mezz.jei.api.recipe.category.IRecipeCategory;
78
import mezz.jei.api.recipe.transfer.IRecipeTransferManager;
@@ -48,16 +49,15 @@ public static Comparator<RecipeLayoutWithButtons<?>> getCraftableComparator() {
4849
private static Comparator<RecipeLayoutWithButtons<?>> createCraftableComparator() {
4950
return Comparator.comparingInt(r -> {
5051
IRecipeLayoutDrawable<?> recipeLayout = r.recipeLayout();
51-
List<IRecipeSlotView> inputSlotViews = recipeLayout.getRecipeSlotsView()
52-
.getSlotViews(RecipeIngredientRole.INPUT);
5352

5453
RecipeTransferButton transferButton = r.transferButton();
5554
int missingCount = transferButton.getMissingCountHint();
5655
if (missingCount == -1) {
5756
return 0;
5857
}
5958

60-
int ingredientCount = ingredientCount(inputSlotViews);
59+
IRecipeSlotsView recipeSlotsView = recipeLayout.getRecipeSlotsView();
60+
int ingredientCount = ingredientCount(recipeSlotsView);
6161
if (ingredientCount == 0) {
6262
return 0;
6363
}
@@ -68,10 +68,10 @@ private static Comparator<RecipeLayoutWithButtons<?>> createCraftableComparator(
6868
});
6969
}
7070

71-
private static int ingredientCount(List<IRecipeSlotView> inputSlotViews) {
71+
private static int ingredientCount(IRecipeSlotsView recipeSlotsView) {
7272
int count = 0;
73-
for (IRecipeSlotView i : inputSlotViews) {
74-
if (!i.isEmpty()) {
73+
for (IRecipeSlotView i : recipeSlotsView.getSlotViews()) {
74+
if (i.getRole() == RecipeIngredientRole.INPUT && !i.isEmpty()) {
7575
count++;
7676
}
7777
}

Library/src/main/java/mezz/jei/library/focus/Focus.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import mezz.jei.api.runtime.IIngredientManager;
99
import mezz.jei.common.util.ErrorUtil;
1010
import mezz.jei.library.ingredients.TypedIngredient;
11+
import org.jetbrains.annotations.Nullable;
1112

1213
import java.util.List;
1314
import java.util.Optional;
@@ -69,21 +70,20 @@ public static <V> Focus<V> checkOne(IFocus<V> focus, IIngredientManager ingredie
6970
}
7071

7172
public static <V> Focus<V> createFromApi(IIngredientManager ingredientManager, RecipeIngredientRole role, IIngredientType<V> ingredientType, V value) {
72-
Optional<ITypedIngredient<V>> typedIngredient = TypedIngredient.createAndFilterInvalid(ingredientManager, ingredientType, value, false)
73-
.flatMap(i -> TypedIngredient.deepCopy(ingredientManager, i));
73+
@Nullable ITypedIngredient<V> typedIngredient = TypedIngredient.createAndFilterInvalid(ingredientManager, ingredientType, value, false);
7474

75-
if (typedIngredient.isEmpty()) {
75+
if (typedIngredient == null) {
7676
throw new IllegalArgumentException("Focus value is invalid: " + ErrorUtil.getIngredientInfo(value, ingredientType, ingredientManager));
7777
}
78-
return new Focus<>(role, typedIngredient.get());
78+
return new Focus<>(role, typedIngredient);
7979
}
8080

8181
public static <V> Focus<V> createFromApi(IIngredientManager ingredientManager, RecipeIngredientRole role, ITypedIngredient<V> typedIngredient) {
82-
Optional<ITypedIngredient<V>> typedIngredientCopy = TypedIngredient.deepCopy(ingredientManager, typedIngredient);
83-
if (typedIngredientCopy.isEmpty()) {
82+
@Nullable ITypedIngredient<V> typedIngredientCopy = TypedIngredient.defensivelyCopyTypedIngredientFromApi(ingredientManager, typedIngredient);
83+
if (typedIngredientCopy == null) {
8484
throw new IllegalArgumentException("Focus value is invalid: " + ErrorUtil.getIngredientInfo(typedIngredient.getIngredient(), typedIngredient.getType(), ingredientManager));
8585
}
86-
return new Focus<>(role, typedIngredientCopy.get());
86+
return new Focus<>(role, typedIngredientCopy);
8787
}
8888

8989
@Override

Library/src/main/java/mezz/jei/library/gui/helpers/ScreenHelper.java

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import mezz.jei.api.gui.handlers.IGuiClickableArea;
77
import mezz.jei.api.gui.handlers.IGuiProperties;
88
import mezz.jei.api.gui.handlers.IScreenHandler;
9+
import mezz.jei.api.ingredients.ITypedIngredient;
910
import mezz.jei.api.runtime.IClickableIngredient;
1011
import mezz.jei.api.runtime.IIngredientManager;
1112
import mezz.jei.api.runtime.IScreenHelper;
@@ -21,6 +22,7 @@
2122
import net.minecraft.client.renderer.Rect2i;
2223
import net.minecraft.world.inventory.Slot;
2324
import net.minecraft.world.item.ItemStack;
25+
import org.jetbrains.annotations.Nullable;
2426

2527
import java.util.ArrayList;
2628
import java.util.Collection;
@@ -124,17 +126,19 @@ private Stream<IClickableIngredient<?>> getPluginsIngredientUnderMouse(Screen gu
124126

125127
private Optional<IClickableIngredient<?>> getClickedIngredient(Slot slot, AbstractContainerScreen<?> guiContainer) {
126128
ItemStack stack = slot.getItem();
127-
return TypedIngredient.createAndFilterInvalid(ingredientManager, VanillaTypes.ITEM_STACK, stack, false)
128-
.map(typedIngredient -> {
129-
IPlatformScreenHelper screenHelper = Services.PLATFORM.getScreenHelper();
130-
ImmutableRect2i slotArea = new ImmutableRect2i(
131-
screenHelper.getGuiLeft(guiContainer) + slot.x,
132-
screenHelper.getGuiTop(guiContainer) + slot.y,
133-
16,
134-
16
135-
);
136-
return new ClickableIngredient<>(typedIngredient, slotArea);
137-
});
129+
@Nullable ITypedIngredient<ItemStack> typedIngredient = TypedIngredient.createAndFilterInvalid(ingredientManager, VanillaTypes.ITEM_STACK, stack, false);
130+
if (typedIngredient == null) {
131+
return Optional.empty();
132+
}
133+
IPlatformScreenHelper screenHelper = Services.PLATFORM.getScreenHelper();
134+
ImmutableRect2i slotArea = new ImmutableRect2i(
135+
screenHelper.getGuiLeft(guiContainer) + slot.x,
136+
screenHelper.getGuiTop(guiContainer) + slot.y,
137+
16,
138+
16
139+
);
140+
ClickableIngredient<ItemStack> clickableIngredient = new ClickableIngredient<>(typedIngredient, slotArea);
141+
return Optional.of(clickableIngredient);
138142
}
139143

140144
private <T extends AbstractContainerScreen<?>> Stream<IClickableIngredient<?>> getGuiContainerHandlerIngredients(T guiContainer, double mouseX, double mouseY) {

Library/src/main/java/mezz/jei/library/gui/ingredients/CycleTicker.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package mezz.jei.library.gui.ingredients;
22

33
import net.minecraft.client.gui.screens.Screen;
4+
import org.jetbrains.annotations.Nullable;
45

56
import java.util.List;
6-
import java.util.Optional;
77

88
public class CycleTicker implements ICycler {
99
private static final int MAX_INDEX = 100_000;
@@ -22,9 +22,10 @@ private CycleTicker(int cycleOffset) {
2222
}
2323

2424
@Override
25-
public <T> Optional<T> getCycled(List<Optional<T>> list) {
25+
@Nullable
26+
public <T> T getCycled(List<@Nullable T> list) {
2627
if (list.isEmpty()) {
27-
return Optional.empty();
28+
return null;
2829
}
2930
int index = this.index % list.size();
3031
return list.get(index);

0 commit comments

Comments
 (0)