Skip to content

Commit

Permalink
Fix #3494 Protect against modded tooltip crashes
Browse files Browse the repository at this point in the history
  • Loading branch information
mezz committed Jul 18, 2024
1 parent 6a2c7fc commit 03c9f11
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 15 deletions.
12 changes: 12 additions & 0 deletions Common/src/main/java/mezz/jei/common/Internal.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package mezz.jei.common;

import com.google.common.base.Preconditions;
import mezz.jei.api.runtime.IIngredientManager;
import mezz.jei.common.config.WorldConfig;
import mezz.jei.common.gui.textures.Textures;
import mezz.jei.common.input.IInternalKeyMappings;
Expand All @@ -20,6 +21,8 @@ public final class Internal {
private static IInternalKeyMappings keyMappings;
@Nullable
private static IWorldConfig worldConfig;
@Nullable
private static IIngredientManager ingredientManager;

private Internal() {

Expand Down Expand Up @@ -58,4 +61,13 @@ public static IWorldConfig getWorldConfig() {
}
return worldConfig;
}

public static void setIngredientManager(IIngredientManager ingredientManager) {
Internal.ingredientManager = ingredientManager;
}

public static IIngredientManager getIngredientManager() {
Preconditions.checkState(ingredientManager != null, "Ingredient Manager has not been created yet.");
return ingredientManager;
}
}
58 changes: 49 additions & 9 deletions Common/src/main/java/mezz/jei/common/gui/TooltipRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,91 @@
import mezz.jei.api.ingredients.IIngredientType;
import mezz.jei.api.ingredients.ITypedIngredient;
import mezz.jei.api.runtime.IIngredientManager;
import mezz.jei.common.Internal;
import mezz.jei.common.platform.IPlatformRenderHelper;
import mezz.jei.common.platform.Services;
import mezz.jei.common.util.ErrorUtil;
import mezz.jei.core.util.LimitedLogger;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.world.inventory.tooltip.TooltipComponent;
import net.minecraft.world.item.ItemStack;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.time.Duration;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public final class TooltipRenderer {
private static final Logger LOGGER = LogManager.getLogger();
private static final LimitedLogger LIMITED_LOGGER = new LimitedLogger(LOGGER, Duration.ofSeconds(30));

private TooltipRenderer() {
}

public static void drawHoveringText(PoseStack poseStack, List<Component> textLines, int x, int y) {
Minecraft minecraft = Minecraft.getInstance();
Screen screen = minecraft.screen;
if (screen == null) {
return;
}
Font font = minecraft.font;
drawHoveringText(poseStack, textLines, x, y, ItemStack.EMPTY, font);
IPlatformRenderHelper renderHelper = Services.PLATFORM.getRenderHelper();
try {
renderHelper.renderTooltip(screen, poseStack, textLines, Optional.empty(), x, y, font, ItemStack.EMPTY);
} catch (RuntimeException e) {
String stringTooltip = getTooltipDebugString(textLines, "\n");
String message = "Failed to render tooltip:\n" + stringTooltip;
LIMITED_LOGGER.log(Level.ERROR, message, message, stringTooltip, e);
}
}

public static <T> void drawHoveringText(PoseStack poseStack, List<Component> textLines, int x, int y, ITypedIngredient<T> typedIngredient, IIngredientManager ingredientManager) {
IIngredientType<T> ingredientType = typedIngredient.getType();
T ingredient = typedIngredient.getIngredient();
IIngredientRenderer<T> ingredientRenderer = ingredientManager.getIngredientRenderer(ingredientType);
drawHoveringText(poseStack, textLines, x, y, ingredient, ingredientRenderer);
drawHoveringText(poseStack, textLines, x, y, typedIngredient, ingredientRenderer);
}

public static <T> void drawHoveringText(PoseStack poseStack, List<Component> textLines, int x, int y, T ingredient, IIngredientRenderer<T> ingredientRenderer) {
public static <T> void drawHoveringText(PoseStack poseStack, List<Component> textLines, int x, int y, ITypedIngredient<T> typedIngredient, IIngredientRenderer<T> ingredientRenderer) {
Minecraft minecraft = Minecraft.getInstance();
T ingredient = typedIngredient.getIngredient();
Font font = ingredientRenderer.getFontRenderer(minecraft, ingredient);
ItemStack itemStack = ingredient instanceof ItemStack ? (ItemStack) ingredient : ItemStack.EMPTY;
drawHoveringText(poseStack, textLines, x, y, itemStack, font);
drawHoveringText(poseStack, textLines, x, y, typedIngredient, font);
}

private static void drawHoveringText(PoseStack poseStack, List<Component> textLines, int x, int y, ItemStack itemStack, Font font) {
private static <T> void drawHoveringText(PoseStack poseStack, List<Component> textLines, int x, int y, ITypedIngredient<T> typedIngredient, Font font) {
Minecraft minecraft = Minecraft.getInstance();
Screen screen = minecraft.screen;
if (screen == null) {
return;
}

Optional<TooltipComponent> tooltipImage = itemStack.getTooltipImage();
ItemStack itemStack = typedIngredient.getItemStack().orElse(ItemStack.EMPTY);
IPlatformRenderHelper renderHelper = Services.PLATFORM.getRenderHelper();
renderHelper.renderTooltip(screen, poseStack, textLines, tooltipImage, x, y, font, itemStack);
try {
Optional<TooltipComponent> tooltipImage = itemStack.getTooltipImage();
renderHelper.renderTooltip(screen, poseStack, textLines, tooltipImage, x, y, font, itemStack);
} catch (RuntimeException e) {
T ingredient = typedIngredient.getIngredient();
IIngredientType<T> type = typedIngredient.getType();
IIngredientManager ingredientManager = Internal.getIngredientManager();
String ingredientInfo = ErrorUtil.getIngredientInfo(ingredient, type, ingredientManager);
String stringTooltip = getTooltipDebugString(textLines, "\n");
String message = String.format("Failed to render tooltip for ingredient %s:\n%s", ingredientInfo, stringTooltip);
LIMITED_LOGGER.log(Level.ERROR, message, message, e);
}
}

private static String getTooltipDebugString(List<Component> textLines, String joinDelimiter) {
return textLines.stream()
.map(Component::plainCopy)
.map(MutableComponent::toString)
.collect(Collectors.joining(joinDelimiter));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,17 @@ public IngredientGridTooltipHelper(
this.colorHelper = colorHelper;
}

public <T> void drawTooltip(PoseStack poseStack, int mouseX, int mouseY, ITypedIngredient<T> value) {
IIngredientType<T> ingredientType = value.getType();
T ingredient = value.getIngredient();
public <T> void drawTooltip(PoseStack poseStack, int mouseX, int mouseY, ITypedIngredient<T> typedIngredient) {
IIngredientType<T> ingredientType = typedIngredient.getType();
IIngredientRenderer<T> ingredientRenderer = ingredientManager.getIngredientRenderer(ingredientType);
IIngredientHelper<T> ingredientHelper = ingredientManager.getIngredientHelper(ingredientType);

List<Component> tooltip = getTooltip(ingredient, ingredientRenderer, ingredientHelper);
TooltipRenderer.drawHoveringText(poseStack, tooltip, mouseX, mouseY, ingredient, ingredientRenderer);
List<Component> tooltip = getTooltip(typedIngredient, ingredientRenderer, ingredientHelper);
TooltipRenderer.drawHoveringText(poseStack, tooltip, mouseX, mouseY, typedIngredient, ingredientRenderer);
}

public <T> List<Component> getTooltip(T ingredient, IIngredientRenderer<T> ingredientRenderer, IIngredientHelper<T> ingredientHelper) {
public <T> List<Component> getTooltip(ITypedIngredient<T> typedIngredient, IIngredientRenderer<T> ingredientRenderer, IIngredientHelper<T> ingredientHelper) {
T ingredient = typedIngredient.getIngredient();
List<Component> ingredientTooltipSafe = IngredientTooltipHelper.getMutableIngredientTooltipSafe(ingredient, ingredientRenderer);
List<Component> tooltip = modIdHelper.addModNameToIngredientTooltip(ingredientTooltipSafe, ingredient, ingredientHelper);

Expand Down
2 changes: 2 additions & 0 deletions Library/src/main/java/mezz/jei/library/load/PluginLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import mezz.jei.api.runtime.IIngredientManager;
import mezz.jei.api.runtime.IIngredientVisibility;
import mezz.jei.api.runtime.IScreenHelper;
import mezz.jei.common.Internal;
import mezz.jei.common.gui.textures.Textures;
import mezz.jei.common.platform.IPlatformFluidHelperInternal;
import mezz.jei.common.platform.Services;
Expand Down Expand Up @@ -74,6 +75,7 @@ public PluginLoader(StartData data, IModIdFormatConfig modIdFormatConfig, IColor
IngredientManagerBuilder ingredientManagerBuilder = new IngredientManagerBuilder(subtypeManager, colorHelper);
PluginCaller.callOnPlugins("Registering ingredients", plugins, p -> p.registerIngredients(ingredientManagerBuilder));
this.ingredientManager = ingredientManagerBuilder.build();
Internal.setIngredientManager(ingredientManager);

StackHelper stackHelper = new StackHelper(subtypeManager);
GuiHelper guiHelper = new GuiHelper(ingredientManager, data.textures());
Expand Down

0 comments on commit 03c9f11

Please sign in to comment.