Skip to content

Commit d20b43e

Browse files
committed
Add support for bookmarking recipes that have no outputs
1 parent 78370e7 commit d20b43e

File tree

5 files changed

+125
-40
lines changed

5 files changed

+125
-40
lines changed

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

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,25 @@ public MapCodec<? extends IngredientBookmark<?>> getCodec(ICodecHelper codecHelp
4040
return DataResult.error(() -> "Recipe has no registry name");
4141
}
4242
IIngredientSupplier ingredients = recipeManager.getRecipeIngredients(recipeCategory, recipe);
43+
44+
boolean displayIsOutput;
45+
ITypedIngredient<?> displayIngredient;
46+
4347
List<ITypedIngredient<?>> outputs = ingredients.getIngredients(RecipeIngredientRole.OUTPUT);
44-
if (outputs.isEmpty()) {
45-
return DataResult.error(() -> "Recipe has no outputs");
48+
if (!outputs.isEmpty()) {
49+
displayIngredient = outputs.getFirst();
50+
displayIsOutput = true;
51+
} else {
52+
List<ITypedIngredient<?>> inputs = ingredients.getIngredients(RecipeIngredientRole.INPUT);
53+
if (inputs.isEmpty()) {
54+
return DataResult.error(() -> "Recipe has no inputs or outputs");
55+
}
56+
displayIngredient = inputs.getFirst();
57+
displayIsOutput = false;
4658
}
47-
ITypedIngredient<?> output = outputs.getFirst();
48-
output = ingredientManager.normalizeTypedIngredient(output);
49-
RecipeBookmark<R, ?> bookmark = new RecipeBookmark<>(recipeCategory, recipe, recipeUid, output);
59+
60+
displayIngredient = ingredientManager.normalizeTypedIngredient(displayIngredient);
61+
RecipeBookmark<R, ?> bookmark = new RecipeBookmark<>(recipeCategory, recipe, recipeUid, displayIngredient, displayIsOutput);
5062
return DataResult.success(bookmark);
5163
},
5264
bookmark -> {

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

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import mezz.jei.gui.overlay.elements.RecipeBookmarkElement;
1212
import net.minecraft.resources.ResourceLocation;
1313

14-
import java.util.List;
1514
import java.util.Objects;
1615
import java.util.Optional;
1716

@@ -20,7 +19,8 @@ public class RecipeBookmark<R, I> implements IBookmark {
2019
private final IRecipeCategory<R> recipeCategory;
2120
private final R recipe;
2221
private final ResourceLocation recipeUid;
23-
private final ITypedIngredient<I> recipeOutput;
22+
private final ITypedIngredient<I> displayIngredient;
23+
private final boolean displayIsOutput;
2424
private boolean visible = true;
2525

2626
public static <T> Optional<RecipeBookmark<T, ?>> create(
@@ -35,30 +35,44 @@ public class RecipeBookmark<R, I> implements IBookmark {
3535
}
3636

3737
IRecipeSlotsView recipeSlotsView = recipeLayoutDrawable.getRecipeSlotsView();
38-
List<IRecipeSlotView> outputSlots = recipeSlotsView.getSlotViews(RecipeIngredientRole.OUTPUT);
39-
for (IRecipeSlotView slotView : outputSlots) {
40-
Optional<ITypedIngredient<?>> outputOptional = slotView.getAllIngredients().findFirst();
41-
if (outputOptional.isEmpty()) {
42-
continue;
38+
{
39+
Optional<ITypedIngredient<?>> outputOptional = recipeSlotsView.getSlotViews(RecipeIngredientRole.OUTPUT).stream()
40+
.flatMap(IRecipeSlotView::getAllIngredients)
41+
.findFirst();
42+
if (outputOptional.isPresent()) {
43+
ITypedIngredient<?> output = outputOptional.get();
44+
output = ingredientManager.normalizeTypedIngredient(output);
45+
return Optional.of(new RecipeBookmark<>(recipeCategory, recipe, recipeUid, output, true));
4346
}
44-
ITypedIngredient<?> output = outputOptional.get();
45-
output = ingredientManager.normalizeTypedIngredient(output);
46-
return Optional.of(new RecipeBookmark<>(recipeCategory, recipe, recipeUid, output));
4747
}
48+
49+
{
50+
Optional<ITypedIngredient<?>> inputOptional = recipeSlotsView.getSlotViews(RecipeIngredientRole.INPUT).stream()
51+
.flatMap(IRecipeSlotView::getAllIngredients)
52+
.findFirst();
53+
if (inputOptional.isPresent()) {
54+
ITypedIngredient<?> input = inputOptional.get();
55+
input = ingredientManager.normalizeTypedIngredient(input);
56+
return Optional.of(new RecipeBookmark<>(recipeCategory, recipe, recipeUid, input, false));
57+
}
58+
}
59+
4860
return Optional.empty();
4961
}
5062

5163
public RecipeBookmark(
5264
IRecipeCategory<R> recipeCategory,
5365
R recipe,
5466
ResourceLocation recipeUid,
55-
ITypedIngredient<I> recipeOutput
67+
ITypedIngredient<I> displayIngredient,
68+
boolean displayIsOutput
5669
) {
5770
this.recipeCategory = recipeCategory;
5871
this.recipe = recipe;
5972
this.recipeUid = recipeUid;
60-
this.recipeOutput = recipeOutput;
73+
this.displayIngredient = displayIngredient;
6174
this.element = new RecipeBookmarkElement<>(this);
75+
this.displayIsOutput = displayIsOutput;
6276
}
6377

6478
@Override
@@ -74,8 +88,12 @@ public R getRecipe() {
7488
return recipe;
7589
}
7690

77-
public ITypedIngredient<I> getRecipeOutput() {
78-
return recipeOutput;
91+
public ITypedIngredient<I> getDisplayIngredient() {
92+
return displayIngredient;
93+
}
94+
95+
public boolean isDisplayIsOutput() {
96+
return displayIsOutput;
7997
}
8098

8199
@Override
@@ -113,7 +131,7 @@ public String toString() {
113131
"recipeCategory=" + recipeCategory.getRecipeType() +
114132
", recipe=" + recipe +
115133
", recipeUid=" + recipeUid +
116-
", recipeOutput=" + recipeOutput +
134+
", displayIngredient=" + displayIngredient +
117135
", visible=" + visible +
118136
'}';
119137
}

Gui/src/main/java/mezz/jei/gui/config/file/serializers/LegacyRecipeBookmarkSerializer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public LegacyRecipeBookmarkSerializer(
8484
}
8585

8686
T recipe = recipeResult.get();
87-
RecipeBookmark<T, ?> recipeBookmark = new RecipeBookmark<>(recipeCategory, recipe, recipeUid, output);
87+
RecipeBookmark<T, ?> recipeBookmark = new RecipeBookmark<>(recipeCategory, recipe, recipeUid, output, true);
8888
return new DeserializeResult<>(recipeBookmark);
8989
}
9090

Gui/src/main/java/mezz/jei/gui/overlay/elements/RecipeBookmarkElement.java

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public RecipeBookmarkElement(RecipeBookmark<R, I> recipeBookmark) {
6666

6767
@Override
6868
public ITypedIngredient<I> getTypedIngredient() {
69-
return recipeBookmark.getRecipeOutput();
69+
return recipeBookmark.getDisplayIngredient();
7070
}
7171

7272
@Override
@@ -120,33 +120,35 @@ public void show(IRecipesGui recipesGui, FocusUtil focusUtil, List<RecipeIngredi
120120

121121
@Override
122122
public void getTooltip(JeiTooltip tooltip, IngredientGridTooltipHelper tooltipHelper, IIngredientRenderer<I> ingredientRenderer, IIngredientHelper<I> ingredientHelper) {
123-
ITypedIngredient<I> recipeOutput = recipeBookmark.getRecipeOutput();
123+
ITypedIngredient<I> displayIngredient = recipeBookmark.getDisplayIngredient();
124124
R recipe = recipeBookmark.getRecipe();
125125

126126
IRecipeCategory<R> recipeCategory = recipeBookmark.getRecipeCategory();
127127
tooltip.add(Component.translatable("jei.tooltip.bookmarks.recipe", recipeCategory.getTitle()));
128128

129129
addBookmarkTooltipFeaturesIfEnabled(tooltip);
130130

131-
IJeiRuntime jeiRuntime = Internal.getJeiRuntime();
132-
IIngredientManager ingredientManager = jeiRuntime.getIngredientManager();
133-
IModIdHelper modIdHelper = jeiRuntime.getJeiHelpers().getModIdHelper();
134-
135-
ResourceLocation recipeName = recipeCategory.getRegistryName(recipe);
136-
if (recipeName != null) {
137-
String recipeModId = recipeName.getNamespace();
138-
ResourceLocation ingredientName = ingredientHelper.getResourceLocation(recipeOutput.getIngredient());
139-
String ingredientModId = ingredientName.getNamespace();
140-
if (!recipeModId.equals(ingredientModId)) {
141-
String modName = modIdHelper.getFormattedModNameForModId(recipeModId);
142-
MutableComponent recipeBy = Component.translatable("jei.tooltip.recipe.by", modName);
143-
tooltip.add(recipeBy.withStyle(ChatFormatting.GRAY));
131+
if (recipeBookmark.isDisplayIsOutput()) {
132+
IJeiRuntime jeiRuntime = Internal.getJeiRuntime();
133+
IIngredientManager ingredientManager = jeiRuntime.getIngredientManager();
134+
IModIdHelper modIdHelper = jeiRuntime.getJeiHelpers().getModIdHelper();
135+
136+
ResourceLocation recipeName = recipeCategory.getRegistryName(recipe);
137+
if (recipeName != null) {
138+
String recipeModId = recipeName.getNamespace();
139+
ResourceLocation ingredientName = ingredientHelper.getResourceLocation(displayIngredient.getIngredient());
140+
String ingredientModId = ingredientName.getNamespace();
141+
if (!recipeModId.equals(ingredientModId)) {
142+
String modName = modIdHelper.getFormattedModNameForModId(recipeModId);
143+
MutableComponent recipeBy = Component.translatable("jei.tooltip.recipe.by", modName);
144+
tooltip.add(recipeBy.withStyle(ChatFormatting.GRAY));
145+
}
144146
}
145-
}
146147

147-
tooltip.add(Component.empty());
148+
tooltip.add(Component.empty());
148149

149-
SafeIngredientUtil.getTooltip(tooltip, ingredientManager, ingredientRenderer, recipeOutput);
150+
SafeIngredientUtil.getTooltip(tooltip, ingredientManager, ingredientRenderer, displayIngredient);
151+
}
150152
}
151153

152154
private void addBookmarkTooltipFeaturesIfEnabled(JeiTooltip tooltip) {

Library/src/main/java/mezz/jei/library/helpers/CodecHelper.java

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import mezz.jei.api.recipe.RecipeType;
1818
import mezz.jei.api.recipe.category.IRecipeCategory;
1919
import mezz.jei.api.runtime.IIngredientManager;
20+
import mezz.jei.common.codecs.EnumCodec;
2021
import mezz.jei.common.codecs.TupleCodec;
2122
import mezz.jei.common.codecs.TypedIngredientCodecs;
2223
import net.minecraft.client.Minecraft;
@@ -122,9 +123,61 @@ public <T> Codec<T> getSlowRecipeCategoryCodec(IRecipeCategory<T> recipeCategory
122123
}
123124

124125
private <T> Codec<T> createDefaultRecipeCategoryCodec(IRecipeManager recipeManager, IRecipeCategory<T> recipeCategory) {
126+
Codec<Data> dataCodec = RecordCodecBuilder.create((builder) -> {
127+
return builder.group(
128+
ResourceLocation.CODEC.fieldOf("registryName")
129+
.forGetter(Data::registryName),
130+
getTypedIngredientCodec().codec().fieldOf("ingredient")
131+
.forGetter(Data::ingredient),
132+
EnumCodec.create(RecipeIngredientRole.class, RecipeIngredientRole::valueOf).fieldOf("ingredient_role")
133+
.forGetter(Data::ingredientRole)
134+
).apply(builder, Data::new);
135+
});
136+
Codec<T> codec = dataCodec.flatXmap(
137+
data -> {
138+
ResourceLocation registryName = data.registryName();
139+
ITypedIngredient<?> ingredient = data.ingredient();
140+
IFocus<?> focus = focusFactory.createFocus(data.ingredientRole(), ingredient);
141+
142+
RecipeType<T> recipeType = recipeCategory.getRecipeType();
143+
144+
return recipeManager.createRecipeLookup(recipeType)
145+
.limitFocus(List.of(focus))
146+
.get()
147+
.filter(recipe -> registryName.equals(recipeCategory.getRegistryName(recipe)))
148+
.findFirst()
149+
.map(DataResult::success)
150+
.orElseGet(() -> DataResult.error(() -> "No recipe found for registry name: " + registryName));
151+
},
152+
recipe -> {
153+
ResourceLocation registryName = recipeCategory.getRegistryName(recipe);
154+
if (registryName == null) {
155+
return DataResult.error(() -> "No registry name for recipe");
156+
}
157+
IIngredientSupplier ingredients = recipeManager.getRecipeIngredients(recipeCategory, recipe);
158+
List<ITypedIngredient<?>> outputs = ingredients.getIngredients(RecipeIngredientRole.OUTPUT);
159+
if (!outputs.isEmpty()) {
160+
Data result = new Data(registryName, outputs.getFirst(), RecipeIngredientRole.OUTPUT);
161+
return DataResult.success(result);
162+
}
163+
List<ITypedIngredient<?>> inputs = ingredients.getIngredients(RecipeIngredientRole.INPUT);
164+
if (!inputs.isEmpty()) {
165+
Data result = new Data(registryName, inputs.getFirst(), RecipeIngredientRole.INPUT);
166+
return DataResult.success(result);
167+
}
168+
return DataResult.error(() -> "No inputs or outputs for recipe");
169+
}
170+
);
171+
172+
return Codec.withAlternative(codec, createLegacyDefaultRecipeCategoryCodec(recipeManager, recipeCategory));
173+
}
174+
175+
private record Data(ResourceLocation registryName, ITypedIngredient<?> ingredient, RecipeIngredientRole ingredientRole) {}
176+
177+
private <T> Codec<T> createLegacyDefaultRecipeCategoryCodec(IRecipeManager recipeManager, IRecipeCategory<T> recipeCategory) {
125178
Codec<Pair<ResourceLocation, ITypedIngredient<?>>> legacyPairCodec = RecordCodecBuilder.create((builder) -> {
126179
return builder.group(
127-
ResourceLocation.CODEC.fieldOf("resourceLocation")
180+
ResourceLocation.CODEC.fieldOf("registryName")
128181
.forGetter(Pair::getFirst),
129182
getTypedIngredientCodec().codec().fieldOf("output")
130183
.forGetter(Pair::getSecond)

0 commit comments

Comments
 (0)