Skip to content

Commit

Permalink
Show a placeholder error recipe when a recipe layout crashes
Browse files Browse the repository at this point in the history
  • Loading branch information
mezz committed Sep 22, 2024
1 parent 63e9d92 commit 7b71ce6
Show file tree
Hide file tree
Showing 10 changed files with 291 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.google.common.base.Preconditions;
import net.minecraft.client.gui.navigation.ScreenPosition;
import net.minecraft.client.gui.navigation.ScreenRectangle;
import net.minecraft.client.renderer.Rect2i;

import javax.annotation.Nonnegative;
Expand Down Expand Up @@ -288,6 +289,10 @@ public Rect2i toMutable() {
return new Rect2i(x, y, width, height);
}

public ScreenRectangle toScreenRectangle() {
return new ScreenRectangle(x, y, width, height);
}

public ImmutablePoint2i getPosition() {
return new ImmutablePoint2i(x, y);
}
Expand Down
8 changes: 7 additions & 1 deletion Common/src/main/java/mezz/jei/common/util/StringUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public final class StringUtil {
private StringUtil() {
Expand Down Expand Up @@ -48,7 +49,12 @@ public static List<FormattedText> splitLines(List<FormattedText> lines, int widt
Font font = minecraft.font;
StringSplitter splitter = font.getSplitter();
return lines.stream()
.flatMap(text -> splitter.splitLines(text, width, Style.EMPTY).stream())
.flatMap(text -> {
if (text.getString().isEmpty()) {
return Stream.of(text);
}
return splitter.splitLines(text, width, Style.EMPTY).stream();
})
.toList();
}

Expand Down
1 change: 1 addition & 0 deletions Common/src/main/resources/assets/jei/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@
"gui.jei.category.registry.item": "Item",
"gui.jei.category.registry.fluid": "Fluid",
"gui.jei.category.tagInformation": "%s Tags",
"gui.jei.category.recipe.crashed": "This recipe crashed. Please see client logs for details.",

"_comment": "Messages",
"jei.message.configured": "Install the \"Configured\" mod to access the in-game config",
Expand Down
8 changes: 8 additions & 0 deletions CommonApi/src/main/java/mezz/jei/api/helpers/IGuiHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,14 @@ default IDrawable createDrawableItemLike(ItemLike itemLike) {
@Deprecated(since = "19.18.9", forRemoval = true)
IScrollBoxWidget createScrollBoxWidget(IDrawable contents, int visibleHeight, int xPos, int yPos);

/**
* Create a scroll box widget.
* Handles displaying drawable contents in a scrolling area.
*
* @since 19.18.10
*/
IScrollBoxWidget createScrollBoxWidget(int width, int height, int xPos, int yPos);

/**
* The amount of extra horizontal space that a {@link IScrollBoxWidget} takes up with its scroll bar.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public abstract class GuiIconToggleButton {
public GuiIconToggleButton(IDrawable offIcon, IDrawable onIcon) {
this.offIcon = offIcon;
this.onIcon = onIcon;
this.button = new GuiIconButton(new DrawableBlank(0, 0), b -> {});
this.button = new GuiIconButton(DrawableBlank.EMPTY, b -> {});
this.area = ImmutableRect2i.EMPTY;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package mezz.jei.gui.input.handlers;

import com.mojang.blaze3d.platform.InputConstants;
import mezz.jei.api.gui.inputs.IJeiInputHandler;
import mezz.jei.api.gui.inputs.IJeiUserInput;
import mezz.jei.common.util.MathUtil;
import net.minecraft.client.gui.navigation.ScreenPosition;
import net.minecraft.client.gui.navigation.ScreenRectangle;

import java.util.function.Supplier;

public class OffsetJeiInputHandler implements IJeiInputHandler {
private final IJeiInputHandler inputHandler;
private final Supplier<ScreenPosition> offset;

public OffsetJeiInputHandler(IJeiInputHandler inputHandler, Supplier<ScreenPosition> offset) {
this.inputHandler = inputHandler;
this.offset = offset;
}

@Override
public boolean handleInput(double mouseX, double mouseY, IJeiUserInput input) {
ScreenPosition screenPosition = offset.get();
final double offsetMouseX = mouseX - screenPosition.x();
final double offsetMouseY = mouseY - screenPosition.y();

ScreenRectangle originalArea = inputHandler.getArea();
if (MathUtil.contains(originalArea, offsetMouseX, offsetMouseY)) {
ScreenPosition position = originalArea.position();
double relativeMouseX = offsetMouseX - position.x();
double relativeMouseY = offsetMouseY - position.y();
return inputHandler.handleInput(relativeMouseX, relativeMouseY, input);
}

return false;
}

@Override
public boolean handleMouseScrolled(double mouseX, double mouseY, double scrollDeltaX, double scrollDeltaY) {
ScreenPosition screenPosition = offset.get();
final double offsetMouseX = mouseX - screenPosition.x();
final double offsetMouseY = mouseY - screenPosition.y();

ScreenRectangle originalArea = inputHandler.getArea();
if (MathUtil.contains(originalArea, offsetMouseX, offsetMouseY)) {
ScreenPosition position = originalArea.position();
double relativeMouseX = offsetMouseX - position.x();
double relativeMouseY = offsetMouseY - position.y();
return inputHandler.handleMouseScrolled(relativeMouseX, relativeMouseY, scrollDeltaX, scrollDeltaY);
}

return false;
}

@Override
public boolean handleMouseDragged(double mouseX, double mouseY, InputConstants.Key mouseKey, double dragX, double dragY) {
ScreenPosition screenPosition = offset.get();
final double offsetMouseX = mouseX - screenPosition.x();
final double offsetMouseY = mouseY - screenPosition.y();

ScreenRectangle originalArea = inputHandler.getArea();
if (MathUtil.contains(originalArea, offsetMouseX, offsetMouseY)) {
ScreenPosition position = originalArea.position();
double relativeMouseX = offsetMouseX - position.x();
double relativeMouseY = offsetMouseY - position.y();
return inputHandler.handleMouseDragged(relativeMouseX, relativeMouseY, mouseKey, dragX, dragY);
}

return false;
}

@Override
public void handleMouseMoved(double mouseX, double mouseY) {
ScreenPosition screenPosition = offset.get();
final double offsetMouseX = mouseX - screenPosition.x();
final double offsetMouseY = mouseY - screenPosition.y();

ScreenRectangle originalArea = inputHandler.getArea();
if (MathUtil.contains(originalArea, offsetMouseX, offsetMouseY)) {
ScreenPosition position = originalArea.position();
double relativeMouseX = offsetMouseX - position.x();
double relativeMouseY = offsetMouseY - position.y();
inputHandler.handleMouseMoved(relativeMouseX, relativeMouseY);
}
}

@Override
public ScreenRectangle getArea() {
ScreenRectangle area = inputHandler.getArea();
return new ScreenRectangle(offset.get(), area.width(), area.height());
}
}
19 changes: 14 additions & 5 deletions Gui/src/main/java/mezz/jei/gui/recipes/RecipeGuiLogic.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
import mezz.jei.common.config.IJeiClientConfigs;
import mezz.jei.common.config.RecipeSorterStage;
import mezz.jei.common.gui.elements.DrawableBlank;
import mezz.jei.common.gui.elements.DrawableNineSliceTexture;
import mezz.jei.common.util.MathUtil;
import mezz.jei.gui.recipes.layouts.IRecipeLayoutList;
import mezz.jei.gui.recipes.layouts.RecipeLayoutDrawableErrored;
import mezz.jei.gui.recipes.lookups.IFocusedRecipes;
import mezz.jei.gui.recipes.lookups.ILookupState;
import mezz.jei.gui.recipes.lookups.IngredientLookupState;
Expand Down Expand Up @@ -251,13 +253,20 @@ private <T> IRecipeLayoutList createRecipeLayoutsWithButtons(
List<T> brokenRecipes = new ArrayList<>();

List<RecipeLayoutWithButtons<T>> results = recipes.stream()
.<IRecipeLayoutDrawable<T>>mapMulti((recipe, acceptor) -> {
.map(recipe -> {
if (recipeCategory.needsRecipeBorder()) {
recipeManager.createRecipeLayoutDrawable(recipeCategory, recipe, state.getFocuses())
.ifPresentOrElse(acceptor, () -> brokenRecipes.add(recipe));
DrawableNineSliceTexture recipeBackground = Internal.getTextures().getRecipeBackground();
return recipeManager.createRecipeLayoutDrawable(recipeCategory, recipe, state.getFocuses(), recipeBackground, 4)
.orElseGet(() -> {
brokenRecipes.add(recipe);
return new RecipeLayoutDrawableErrored<>(recipeCategory, recipe, recipeBackground, 4);
});
} else {
recipeManager.createRecipeLayoutDrawable(recipeCategory, recipe, state.getFocuses(), new DrawableBlank(0, 0), 0)
.ifPresentOrElse(acceptor, () -> brokenRecipes.add(recipe));
return recipeManager.createRecipeLayoutDrawable(recipeCategory, recipe, state.getFocuses(), DrawableBlank.EMPTY, 0)
.orElseGet(() -> {
brokenRecipes.add(recipe);
return new RecipeLayoutDrawableErrored<>(recipeCategory, recipe, DrawableBlank.EMPTY, 0);
});
}
})
.map(recipeLayoutFactory::create)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package mezz.jei.gui.recipes.layouts;

import com.mojang.blaze3d.vertex.PoseStack;
import mezz.jei.api.gui.IRecipeLayoutDrawable;
import mezz.jei.api.gui.drawable.IScalableDrawable;
import mezz.jei.api.gui.ingredient.IRecipeSlotDrawable;
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
import mezz.jei.api.gui.inputs.IJeiInputHandler;
import mezz.jei.api.gui.inputs.RecipeSlotUnderMouse;
import mezz.jei.api.gui.widgets.IScrollBoxWidget;
import mezz.jei.api.helpers.IGuiHelper;
import mezz.jei.api.helpers.IJeiHelpers;
import mezz.jei.api.ingredients.IIngredientType;
import mezz.jei.api.recipe.category.IRecipeCategory;
import mezz.jei.api.runtime.IJeiRuntime;
import mezz.jei.common.Internal;
import mezz.jei.common.util.ImmutableRect2i;
import mezz.jei.gui.input.handlers.OffsetJeiInputHandler;
import net.minecraft.ChatFormatting;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.navigation.ScreenPosition;
import net.minecraft.client.renderer.Rect2i;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.FormattedText;
import net.minecraft.resources.ResourceLocation;

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

public class RecipeLayoutDrawableErrored<R> implements IRecipeLayoutDrawable<R> {
private final IRecipeCategory<R> recipeCategory;
private final R recipe;
private final IScrollBoxWidget scrollBoxWidget;
private final IJeiInputHandler inputHandler;
private final IScalableDrawable background;
private final int borderPadding;
private ImmutableRect2i area;

public RecipeLayoutDrawableErrored(IRecipeCategory<R> recipeCategory, R recipe, IScalableDrawable background, int borderPadding) {
this.recipeCategory = recipeCategory;
this.recipe = recipe;
this.area = new ImmutableRect2i(0, 0, Math.max(100, recipeCategory.getWidth()), recipeCategory.getHeight());
this.background = background;
this.borderPadding = borderPadding;

List<FormattedText> lines = new ArrayList<>();
lines.add(Component.translatable("gui.jei.category.recipe.crashed").withStyle(ChatFormatting.RED));
lines.add(Component.empty());
lines.add(Component.literal(recipeCategory.getRecipeType().getUid().toString()).withStyle(ChatFormatting.GRAY));
ResourceLocation registryName = recipeCategory.getRegistryName(recipe);
if (registryName != null) {
lines.add(Component.empty());
lines.add(Component.literal(registryName.toString()).withStyle(ChatFormatting.GRAY));
}

IJeiRuntime jeiRuntime = Internal.getJeiRuntime();
IJeiHelpers jeiHelpers = jeiRuntime.getJeiHelpers();
IGuiHelper guiHelper = jeiHelpers.getGuiHelper();
this.scrollBoxWidget = guiHelper.createScrollBoxWidget(area.width(), area.getHeight(), 0, 0)
.setContents(lines);

this.inputHandler = new OffsetJeiInputHandler(this.scrollBoxWidget, this::getScreenPosition);
}

private ScreenPosition getScreenPosition() {
return this.area.getScreenPosition();
}

@Override
public void setPosition(int posX, int posY) {
this.area = this.area.setPosition(posX, posY);
}

@Override
public void drawRecipe(GuiGraphics guiGraphics, int mouseX, int mouseY) {
background.draw(guiGraphics, getRectWithBorder());

PoseStack poseStack = guiGraphics.pose();
poseStack.pushPose();
{
poseStack.translate(area.x(), area.y(), 0);
scrollBoxWidget.draw(guiGraphics, mouseX, mouseY);
}
poseStack.popPose();
}

@Override
public void drawOverlays(GuiGraphics guiGraphics, int mouseX, int mouseY) {

}

@Override
public boolean isMouseOver(double mouseX, double mouseY) {
return area.contains(mouseX, mouseY);
}

@Override
public <T> Optional<T> getIngredientUnderMouse(int mouseX, int mouseY, IIngredientType<T> ingredientType) {
return Optional.empty();
}

@Override
public Optional<IRecipeSlotDrawable> getRecipeSlotUnderMouse(double mouseX, double mouseY) {
return Optional.empty();
}

@Override
public Optional<RecipeSlotUnderMouse> getSlotUnderMouse(double mouseX, double mouseY) {
return Optional.empty();
}

@Override
public Rect2i getRect() {
return area.toMutable();
}

@Override
public Rect2i getRectWithBorder() {
return area.expandBy(borderPadding).toMutable();
}

@Override
public Rect2i getRecipeTransferButtonArea() {
return new Rect2i(0, 0, 0, 0);
}

@Override
public Rect2i getRecipeBookmarkButtonArea() {
return new Rect2i(0, 0, 0, 0);
}

@Override
public IRecipeSlotsView getRecipeSlotsView() {
return List::of;
}

@Override
public IRecipeCategory<R> getRecipeCategory() {
return recipeCategory;
}

@Override
public R getRecipe() {
return recipe;
}

@Override
public IJeiInputHandler getInputHandler() {
return inputHandler;
}

@Override
public void tick() {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ public IScrollBoxWidget createScrollBoxWidget(IDrawable contents, int visibleHei
return widget;
}

@Override
public IScrollBoxWidget createScrollBoxWidget(int width, int height, int xPos, int yPos) {
return new ScrollBoxRecipeWidget(width, height, xPos, yPos);
}

@SuppressWarnings("removal")
@Override
public int getScrollBoxScrollbarExtraWidth() {
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,4 @@ modrinthId=u6dRKJwZ
jUnitVersion=5.8.2

# Version
specificationVersion=19.18.9
specificationVersion=19.18.10

0 comments on commit 7b71ce6

Please sign in to comment.