Skip to content

Commit

Permalink
Add more protections against invalid ITypedIngredient being created
Browse files Browse the repository at this point in the history
  • Loading branch information
mezz committed Aug 28, 2024
1 parent fb3f804 commit d485380
Show file tree
Hide file tree
Showing 12 changed files with 84 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package mezz.jei.common.input;

import mezz.jei.api.ingredients.IIngredientType;
import mezz.jei.api.ingredients.ITypedIngredient;
import mezz.jei.api.runtime.IClickableIngredient;
import mezz.jei.common.util.ErrorUtil;
Expand All @@ -16,11 +17,22 @@ public ClickableIngredient(ITypedIngredient<V> value, ImmutableRect2i area) {
this.area = area;
}

@SuppressWarnings("removal")
@Override
public ITypedIngredient<V> getTypedIngredient() {
return value;
}

@Override
public IIngredientType<V> getIngredientType() {
return value.getType();
}

@Override
public V getIngredient() {
return value.getIngredient();
}

@Override
public Rect2i getArea() {
return area.toMutable();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.runtime.IIngredientManager;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.ApiStatus;

import java.util.Optional;

Expand All @@ -14,6 +15,7 @@
*
* @since 9.3.0
*/
@ApiStatus.NonExtendable
public interface ITypedIngredient<T> {
/**
* @return the type of this ingredient
Expand Down
2 changes: 2 additions & 0 deletions CommonApi/src/main/java/mezz/jei/api/recipe/IFocus.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import mezz.jei.api.ingredients.IIngredientType;
import mezz.jei.api.ingredients.ITypedIngredient;
import org.jetbrains.annotations.ApiStatus;

import java.util.Optional;

Expand All @@ -14,6 +15,7 @@
*
* Use a null IFocus to signify no focus, like in the case of looking up categories of recipes.
*/
@ApiStatus.NonExtendable
public interface IFocus<V> {
/**
* The ingredient that is being focused on.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package mezz.jei.api.runtime;

import mezz.jei.api.ingredients.IIngredientType;
import mezz.jei.api.ingredients.ITypedIngredient;
import net.minecraft.client.renderer.Rect2i;

Expand All @@ -16,9 +17,26 @@ public interface IClickableIngredient<T> {
* Get the typed ingredient that can be looked up by JEI for recipes.
*
* @since 11.5.0
* @deprecated use {@link #getIngredient()} and {@link #getIngredientType()} instead.
*/
@Deprecated(since = "19.12.0", forRemoval = true)
ITypedIngredient<T> getTypedIngredient();

/**
* @since 19.12.0
*/
default IIngredientType<T> getIngredientType() {
return getTypedIngredient().getType();
}

/**
* @since 19.12.0
*/
default T getIngredient() {
ITypedIngredient<T> typedIngredient = getTypedIngredient();
return typedIngredient.getIngredient();
}

/**
* Get the area that this clickable ingredient is drawn in, in absolute screen coordinates.
* This is used for click handling, to ensure the mouse-down and mouse-up are on the same ingredient.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ default Collection<ItemStack> getAllItemStacks() {
/**
* Create a typed ingredient, if the given ingredient is valid.
*
* Invalid ingredients (according to {@link IIngredientHelper#isValidIngredient}
* cannot be created into {@link ITypedIngredient} and will instead be {@link Optional#empty()}.
* Invalid ingredients (according to {@link IIngredientHelper#isValidIngredient})
* cannot be used in {@link ITypedIngredient} and will instead be {@link Optional#empty()}.
* This helps turn all special cases like {@link ItemStack#EMPTY} into {@link Optional#empty()} instead.
*
* @since 11.5.0
Expand Down
25 changes: 20 additions & 5 deletions Gui/src/main/java/mezz/jei/gui/input/GuiContainerWrapper.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package mezz.jei.gui.input;

import mezz.jei.api.ingredients.IIngredientType;
import mezz.jei.api.ingredients.ITypedIngredient;
import mezz.jei.api.runtime.IClickableIngredient;
import mezz.jei.api.runtime.IIngredientManager;
import mezz.jei.api.runtime.IScreenHelper;
import mezz.jei.common.Internal;
import mezz.jei.common.util.ImmutableRect2i;
import mezz.jei.gui.overlay.elements.IElement;
import mezz.jei.gui.overlay.elements.IngredientElement;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;

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

public class GuiContainerWrapper implements IRecipeFocusSource {
Expand All @@ -24,14 +29,24 @@ public Stream<IClickableIngredientInternal<?>> getIngredientUnderMouse(double mo
return Stream.empty();
}
return screenHelper.getClickableIngredientUnderMouse(guiScreen, mouseX, mouseY)
.map(clickableSlot -> {
ITypedIngredient<?> typedIngredient = clickableSlot.getTypedIngredient();
ImmutableRect2i area = new ImmutableRect2i(clickableSlot.getArea());
IElement<?> element = new IngredientElement<>(typedIngredient);
return new ClickableIngredientInternal<>(element, area::contains, false, false);
.flatMap(clickableSlot -> {
return createTypedIngredient(clickableSlot)
.map(i -> {
ImmutableRect2i area = new ImmutableRect2i(clickableSlot.getArea());
IElement<?> element = new IngredientElement<>(i);
return new ClickableIngredientInternal<>(element, area::contains, false, false);
})
.stream();
});
}

private <T> Optional<ITypedIngredient<T>> createTypedIngredient(IClickableIngredient<T> clickableIngredient) {
IIngredientManager ingredientManager = Internal.getJeiRuntime().getIngredientManager();
IIngredientType<T> ingredientType = clickableIngredient.getIngredientType();
T ingredient = clickableIngredient.getIngredient();
return ingredientManager.createTypedIngredient(ingredientType, ingredient);
}

@Override
public Stream<IDraggableIngredientInternal<?>> getDraggableIngredientUnderMouse(double mouseX, double mouseY) {
return Stream.empty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,14 @@ public DisplayIngredientAcceptor addTypedIngredients(List<ITypedIngredient<?>> i
public DisplayIngredientAcceptor addOptionalTypedIngredients(List<Optional<ITypedIngredient<?>>> ingredients) {
ErrorUtil.checkNotNull(ingredients, "ingredients");

this.ingredients.addAll(ingredients);
for (Optional<ITypedIngredient<?>> o : ingredients) {
if (o.isPresent()) {
this.addTypedIngredient(o.get());
} else {
this.ingredients.add(o);
}
}

return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ public <V> void removeIngredientsAtRuntime(IIngredientType<V> ingredientType, Co

if (!this.listeners.isEmpty()) {
List<ITypedIngredient<V>> typedIngredients = ingredients.stream()
.map(i -> TypedIngredient.createUnvalidated(ingredientType, i))
.flatMap(i -> TypedIngredient.createAndFilterInvalid(this, ingredientType, i, false).stream())
.toList();

IIngredientHelper<V> ingredientHelper = ingredientInfo.getIngredientHelper();
Expand Down Expand Up @@ -185,7 +185,6 @@ public <V> ITypedIngredient<V> normalizeTypedIngredient(ITypedIngredient<V> type
return TypedIngredient.createUnvalidated(type, normalized);
}

@SuppressWarnings("removal")
@Override
@Deprecated
public <V> Optional<V> getIngredientByUid(IIngredientType<V> ingredientType, String ingredientUuid) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import mezz.jei.api.helpers.IGuiHelper;
import mezz.jei.api.helpers.IJeiHelpers;
import mezz.jei.api.helpers.IPlatformFluidHelper;
import mezz.jei.api.ingredients.IIngredientType;
import mezz.jei.api.ingredients.IIngredientTypeWithSubtypes;
import mezz.jei.api.ingredients.ITypedIngredient;
import mezz.jei.api.registration.IAdvancedRegistration;
Expand Down Expand Up @@ -275,11 +276,22 @@ private record DebugClickableIngredient<T>(
Rect2i area
) implements IClickableIngredient<T> {

@SuppressWarnings("removal")
@Override
public ITypedIngredient<T> getTypedIngredient() {
return typedIngredient;
}

@Override
public IIngredientType<T> getIngredientType() {
return typedIngredient.getType();
}

@Override
public T getIngredient() {
return typedIngredient.getIngredient();
}

@Override
public Rect2i getArea() {
return area;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,12 @@ public ItemStack normalizeIngredient(ItemStack ingredient) {
if (ingredient.getCount() == 1) {
return ingredient;
}
// Temporarily setting the count on the original stack this way can "recover" some empty ItemStacks.
// Copying it first results in the copy being a hard-coded ItemStack#EMPTY that cannot be recovered.
int originalCount = ingredient.getCount();
ingredient.setCount(1);
ItemStack copy = ingredient.copy();
copy.setCount(1);
ingredient.setCount(originalCount);
return copy;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public Optional<TextureAtlasSprite> getStillFluidSprite(FluidStack fluidStack) {
@Override
public Component getDisplayName(FluidStack ingredient) {
Component displayName = ingredient.getHoverName();

Fluid fluid = ingredient.getFluid();
if (!fluid.isSource(fluid.defaultFluidState())) {
return Component.translatable("jei.tooltip.liquid.flowing", displayName);
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,4 @@ curseHomepageUrl=https://www.curseforge.com/minecraft/mc-mods/jei
jUnitVersion=5.8.2

# Version
specificationVersion=19.11.0
specificationVersion=19.12.0

0 comments on commit d485380

Please sign in to comment.