Skip to content

Commit

Permalink
Optimize by moving away from Optional and Stream in hot code paths
Browse files Browse the repository at this point in the history
  • Loading branch information
mezz committed Oct 3, 2024
1 parent 13616d8 commit e10b47c
Show file tree
Hide file tree
Showing 28 changed files with 347 additions and 184 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;

import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

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

/**
* All ingredients, ignoring focus and visibility
* null ingredients represent a "blank" drawn ingredient in the rotation.
*
* @since 19.19.5
*/
@Unmodifiable
List<@Nullable ITypedIngredient<?>> getAllIngredientsList();

/**
* The ingredient variation that is shown at this moment.
* For ingredients that rotate through several values, this will change over time.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,16 @@ default Object getGroupingUid(V ingredient) {
return getWildcardId(ingredient);
}

/**
* Unique ID for use in grouping ingredients together.
* This is used for hiding groups of ingredients together at once.
*
* @since 19.19.5
*/
default Object getGroupingUid(ITypedIngredient<V> typedIngredient) {
return getGroupingUid(typedIngredient.getIngredient());
}

/**
* Return true if the given ingredient can have subtypes.
* For example in the vanilla game an enchanted book may have subtypes, but an apple does not.
Expand Down Expand Up @@ -227,6 +237,17 @@ default boolean isHiddenFromRecipeViewersByTags(V ingredient) {
.anyMatch(Tags.HIDDEN_FROM_RECIPE_VIEWERS::equals);
}

/**
* Return true if the given ingredient is hidden from recipe viewers by its tags.
*
* @see Tags#HIDDEN_FROM_RECIPE_VIEWERS
*
* @since 19.19.5
*/
default boolean isHiddenFromRecipeViewersByTags(ITypedIngredient<V> ingredient) {
return isHiddenFromRecipeViewersByTags(ingredient.getIngredient());
}

/**
* Get information for error messages involving this ingredient.
* Be extremely careful not to crash here, get as much useful info as possible.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,18 @@ default Optional<T> castIngredient(@Nullable Object ingredient) {
}
return Optional.empty();
}

/**
* Helper to cast an unknown ingredient to this type if it matches.
*
* @since 19.19.5
*/
@Nullable
default T getCastIngredient(@Nullable Object ingredient) {
Class<? extends T> ingredientClass = getIngredientClass();
if (ingredientClass.isInstance(ingredient)) {
return ingredientClass.cast(ingredient);
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import mezz.jei.api.runtime.IIngredientManager;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;

import java.util.Optional;

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

/**
* @return the ingredient wrapped by this instance, only if it matches the given type.
* This is useful when handling a wildcard generic instance of `ITypedIngredient<?>`.
*
* @since 19.19.5
*/
@Nullable
default <V> V getCastIngredient(IIngredientType<V> ingredientType) {
return ingredientType.getCastIngredient(getIngredient());
}

/**
* @return the ingredient's base ingredient. (For example, an ItemStack's base ingredient is the Item)
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import mezz.jei.api.registration.IIngredientAliasRegistration;
import net.minecraft.client.renderer.Rect2i;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;

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

/**
* Helper method to get ingredient type for an ingredient.
* Returns null if there is no known type for the given ingredient.
*
* @since 19.19.5
*/
@Nullable
<V> IIngredientType<V> getIngredientType(V ingredient);

/**
* Helper method to get ingredient type for an ingredient.
* Returns {@link Optional#empty()} if there is no known type for the given ingredient.
Expand Down
30 changes: 20 additions & 10 deletions Gui/src/main/java/mezz/jei/gui/bookmarks/RecipeBookmark.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import mezz.jei.gui.overlay.elements.IElement;
import mezz.jei.gui.overlay.elements.RecipeBookmarkElement;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.Nullable;

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

IRecipeSlotsView recipeSlotsView = recipeLayoutDrawable.getRecipeSlotsView();
{
Optional<ITypedIngredient<?>> outputOptional = recipeSlotsView.getSlotViews(RecipeIngredientRole.OUTPUT).stream()
.flatMap(IRecipeSlotView::getAllIngredients)
.findFirst();
if (outputOptional.isPresent()) {
ITypedIngredient<?> output = outputOptional.get();
ITypedIngredient<?> output = findFirst(recipeSlotsView, RecipeIngredientRole.OUTPUT);
if (output != null) {
output = ingredientManager.normalizeTypedIngredient(output);
return Optional.of(new RecipeBookmark<>(recipeCategory, recipe, recipeUid, output, true));
}
}

{
Optional<ITypedIngredient<?>> inputOptional = recipeSlotsView.getSlotViews(RecipeIngredientRole.INPUT).stream()
.flatMap(IRecipeSlotView::getAllIngredients)
.findFirst();
if (inputOptional.isPresent()) {
ITypedIngredient<?> input = inputOptional.get();
ITypedIngredient<?> input = findFirst(recipeSlotsView, RecipeIngredientRole.INPUT);
if (input != null) {
input = ingredientManager.normalizeTypedIngredient(input);
return Optional.of(new RecipeBookmark<>(recipeCategory, recipe, recipeUid, input, false));
}
Expand All @@ -60,6 +55,21 @@ public class RecipeBookmark<R, I> implements IBookmark {
return Optional.empty();
}

@Nullable
private static ITypedIngredient<?> findFirst(IRecipeSlotsView slotsView, RecipeIngredientRole role) {
for (IRecipeSlotView slotView : slotsView.getSlotViews()) {
if (slotView.getRole() != role) {
continue;
}
for (ITypedIngredient<?> ingredient : slotView.getAllIngredientsList()) {
if (ingredient != null) {
return ingredient;
}
}
}
return null;
}

public RecipeBookmark(
IRecipeCategory<R> recipeCategory,
R recipe,
Expand Down
12 changes: 6 additions & 6 deletions Gui/src/main/java/mezz/jei/gui/recipes/RecipeSortUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import mezz.jei.api.gui.IRecipeLayoutDrawable;
import mezz.jei.api.gui.ingredient.IRecipeSlotView;
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
import mezz.jei.api.recipe.RecipeIngredientRole;
import mezz.jei.api.recipe.category.IRecipeCategory;
import mezz.jei.api.recipe.transfer.IRecipeTransferManager;
Expand Down Expand Up @@ -48,16 +49,15 @@ public static Comparator<RecipeLayoutWithButtons<?>> getCraftableComparator() {
private static Comparator<RecipeLayoutWithButtons<?>> createCraftableComparator() {
return Comparator.comparingInt(r -> {
IRecipeLayoutDrawable<?> recipeLayout = r.recipeLayout();
List<IRecipeSlotView> inputSlotViews = recipeLayout.getRecipeSlotsView()
.getSlotViews(RecipeIngredientRole.INPUT);

RecipeTransferButton transferButton = r.transferButton();
int missingCount = transferButton.getMissingCountHint();
if (missingCount == -1) {
return 0;
}

int ingredientCount = ingredientCount(inputSlotViews);
IRecipeSlotsView recipeSlotsView = recipeLayout.getRecipeSlotsView();
int ingredientCount = ingredientCount(recipeSlotsView);
if (ingredientCount == 0) {
return 0;
}
Expand All @@ -68,10 +68,10 @@ private static Comparator<RecipeLayoutWithButtons<?>> createCraftableComparator(
});
}

private static int ingredientCount(List<IRecipeSlotView> inputSlotViews) {
private static int ingredientCount(IRecipeSlotsView recipeSlotsView) {
int count = 0;
for (IRecipeSlotView i : inputSlotViews) {
if (!i.isEmpty()) {
for (IRecipeSlotView i : recipeSlotsView.getSlotViews()) {
if (i.getRole() == RecipeIngredientRole.INPUT && !i.isEmpty()) {
count++;
}
}
Expand Down
14 changes: 7 additions & 7 deletions Library/src/main/java/mezz/jei/library/focus/Focus.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import mezz.jei.api.runtime.IIngredientManager;
import mezz.jei.common.util.ErrorUtil;
import mezz.jei.library.ingredients.TypedIngredient;
import org.jetbrains.annotations.Nullable;

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

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

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

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

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import mezz.jei.api.gui.handlers.IGuiClickableArea;
import mezz.jei.api.gui.handlers.IGuiProperties;
import mezz.jei.api.gui.handlers.IScreenHandler;
import mezz.jei.api.ingredients.ITypedIngredient;
import mezz.jei.api.runtime.IClickableIngredient;
import mezz.jei.api.runtime.IIngredientManager;
import mezz.jei.api.runtime.IScreenHelper;
Expand All @@ -21,6 +22,7 @@
import net.minecraft.client.renderer.Rect2i;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;

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

private Optional<IClickableIngredient<?>> getClickedIngredient(Slot slot, AbstractContainerScreen<?> guiContainer) {
ItemStack stack = slot.getItem();
return TypedIngredient.createAndFilterInvalid(ingredientManager, VanillaTypes.ITEM_STACK, stack, false)
.map(typedIngredient -> {
IPlatformScreenHelper screenHelper = Services.PLATFORM.getScreenHelper();
ImmutableRect2i slotArea = new ImmutableRect2i(
screenHelper.getGuiLeft(guiContainer) + slot.x,
screenHelper.getGuiTop(guiContainer) + slot.y,
16,
16
);
return new ClickableIngredient<>(typedIngredient, slotArea);
});
@Nullable ITypedIngredient<ItemStack> typedIngredient = TypedIngredient.createAndFilterInvalid(ingredientManager, VanillaTypes.ITEM_STACK, stack, false);
if (typedIngredient == null) {
return Optional.empty();
}
IPlatformScreenHelper screenHelper = Services.PLATFORM.getScreenHelper();
ImmutableRect2i slotArea = new ImmutableRect2i(
screenHelper.getGuiLeft(guiContainer) + slot.x,
screenHelper.getGuiTop(guiContainer) + slot.y,
16,
16
);
ClickableIngredient<ItemStack> clickableIngredient = new ClickableIngredient<>(typedIngredient, slotArea);
return Optional.of(clickableIngredient);
}

private <T extends AbstractContainerScreen<?>> Stream<IClickableIngredient<?>> getGuiContainerHandlerIngredients(T guiContainer, double mouseX, double mouseY) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package mezz.jei.library.gui.ingredients;

import net.minecraft.client.gui.screens.Screen;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.Optional;

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

@Override
public <T> Optional<T> getCycled(List<Optional<T>> list) {
@Nullable
public <T> T getCycled(List<@Nullable T> list) {
if (list.isEmpty()) {
return Optional.empty();
return null;
}
int index = this.index % list.size();
return list.get(index);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package mezz.jei.library.gui.ingredients;

import net.minecraft.client.gui.screens.Screen;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.Optional;

public class CycleTimer implements ICycler {
private static final CycleTimer ZERO_OFFSET = new CycleTimer(0);
Expand Down Expand Up @@ -38,9 +38,10 @@ private static int calculateIndex(long now, int cycleOffset) {
}

@Override
public <T> Optional<T> getCycled(List<Optional<T>> list) {
@Nullable
public <T> T getCycled(List<@Nullable T> list) {
if (list.isEmpty()) {
return Optional.empty();
return null;
}
if (!Screen.hasShiftDown()) {
long now = System.currentTimeMillis();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package mezz.jei.library.gui.ingredients;

import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.Optional;

public interface ICycler {
<T> Optional<T> getCycled(List<Optional<T>> list);
@Nullable
<T> T getCycled(List<@Nullable T> list);
}
Loading

0 comments on commit e10b47c

Please sign in to comment.