Skip to content

Commit 53a7bc7

Browse files
Merge pull request #28 from CodeMonkeysMods/fix/single-recipe-per-workbench-type
Fix/single recipe per workbench type
2 parents 5e4f31c + c43b5df commit 53a7bc7

20 files changed

Lines changed: 187 additions & 161 deletions

src/client/java/com/tcm/MineTale/MineTaleClient.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,5 @@ public class MineTaleClient implements ClientModInitializer {
2121
public void onInitializeClient() {
2222
MenuScreens.register(ModMenuTypes.FURNACE_WORKBENCH_MENU, FurnaceWorkbenchScreen::new);
2323
MenuScreens.register(ModMenuTypes.CAMPFIRE_WORKBENCH_MENU, CampfireWorkbenchScreen::new);
24-
25-
2624
}
2725
}

src/client/java/com/tcm/MineTale/MineTaleDataGen.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package com.tcm.MineTale;
22

3+
import com.tcm.MineTale.datagen.ModBlockTagProvider;
34
import com.tcm.MineTale.datagen.ModLangProvider;
45
import com.tcm.MineTale.datagen.ModModelProvider;
6+
import com.tcm.MineTale.datagen.ModRecipeProvider;
7+
58
import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint;
69
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
710

@@ -21,5 +24,7 @@ public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) {
2124

2225
pack.addProvider(ModLangProvider::new);
2326
pack.addProvider(ModModelProvider::new);
27+
pack.addProvider(ModRecipeProvider::new);
28+
pack.addProvider(ModBlockTagProvider::new);
2429
}
2530
}

src/client/java/com/tcm/MineTale/block/workbenches/screen/CampfireWorkbenchScreen.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.tcm.MineTale.recipe.MineTaleRecipeBookComponent;
88
import com.tcm.MineTale.registry.ModBlocks;
99
import com.tcm.MineTale.registry.ModRecipeDisplay;
10+
import com.tcm.MineTale.registry.ModRecipes;
1011

1112
import net.minecraft.client.gui.GuiGraphics;
1213
import net.minecraft.client.gui.navigation.ScreenPosition;
@@ -44,7 +45,7 @@ private static MineTaleRecipeBookComponent createRecipeBookComponent(CampfireWor
4445
new RecipeBookComponent.TabInfo(tabIcon.getItem(), ModRecipeDisplay.CAMPFIRE_SEARCH)
4546
);
4647

47-
return new MineTaleRecipeBookComponent(menu, tabs);
48+
return new MineTaleRecipeBookComponent(menu, tabs, ModRecipes.CAMPFIRE_TYPE);
4849
}
4950

5051
/**

src/client/java/com/tcm/MineTale/block/workbenches/screen/FurnaceWorkbenchScreen.java

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import com.tcm.MineTale.block.workbenches.menu.FurnaceWorkbenchMenu;
77
import com.tcm.MineTale.recipe.MineTaleRecipeBookComponent;
88
import com.tcm.MineTale.registry.ModBlocks;
9+
import com.tcm.MineTale.registry.ModRecipeDisplay;
10+
import com.tcm.MineTale.registry.ModRecipes;
911

1012
import net.minecraft.client.gui.GuiGraphics;
1113
import net.minecraft.client.gui.navigation.ScreenPosition;
@@ -15,7 +17,6 @@
1517
import net.minecraft.resources.Identifier;
1618
import net.minecraft.world.entity.player.Inventory;
1719
import net.minecraft.world.item.ItemStack;
18-
import net.minecraft.world.item.crafting.RecipeBookCategories;
1920
import net.minecraft.network.chat.Component;
2021

2122
public class FurnaceWorkbenchScreen extends AbstractRecipeBookScreen<FurnaceWorkbenchMenu> {
@@ -38,10 +39,10 @@ private static MineTaleRecipeBookComponent createRecipeBookComponent(FurnaceWork
3839
ItemStack tabIcon = new ItemStack(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1.asItem());
3940

4041
List<RecipeBookComponent.TabInfo> tabs = List.of(
41-
new RecipeBookComponent.TabInfo(tabIcon.getItem(), RecipeBookCategories.CRAFTING_MISC)
42+
new RecipeBookComponent.TabInfo(tabIcon.getItem(), ModRecipeDisplay.FURNACE_T1_SEARCH)
4243
);
4344

44-
return new MineTaleRecipeBookComponent(menu, tabs);
45+
return new MineTaleRecipeBookComponent(menu, tabs, ModRecipes.FURNACE_T1_TYPE);
4546
}
4647

4748
@Override
@@ -81,17 +82,8 @@ protected void renderBg(GuiGraphics guiGraphics, float f, int i, int j) {
8182
public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
8283
renderBackground(graphics, mouseX, mouseY, delta);
8384

84-
// if (this.recipeBookComponent != null) {
85-
// this.recipeBookComponent.render(graphics, mouseX, mouseY, delta);
86-
// }
87-
8885
super.render(graphics, mouseX, mouseY, delta);
8986

90-
// if (this.recipeBookComponent != null) {
91-
// this.recipeBookComponent.renderGhostRecipe(graphics, true);
92-
// this.recipeBookComponent.renderTooltip(graphics, this.leftPos, this.topPos, this.hoveredSlot);
93-
// }
94-
9587
renderTooltip(graphics, mouseX, mouseY);
9688
}
9789

src/main/java/com/tcm/MineTale/datagen/ModBlockTagProvider.java renamed to src/client/java/com/tcm/MineTale/datagen/ModBlockTagProvider.java

File renamed without changes.

src/main/java/com/tcm/MineTale/datagen/ModRecipeProvider.java renamed to src/client/java/com/tcm/MineTale/datagen/ModRecipeProvider.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import com.tcm.MineTale.MineTale;
66
import com.tcm.MineTale.datagen.builders.WorkbenchRecipeBuilder;
7+
import com.tcm.MineTale.registry.ModRecipeDisplay;
78
import com.tcm.MineTale.registry.ModRecipes;
89

910
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
@@ -46,20 +47,20 @@ protected RecipeProvider createRecipeProvider(HolderLookup.Provider registryLook
4647
public void buildRecipes() {
4748
new WorkbenchRecipeBuilder(ModRecipes.CAMPFIRE_TYPE, ModRecipes.CAMPFIRE_SERIALIZER)
4849
.input(Ingredient.of(Items.PORKCHOP))
49-
// Note: Slot 1 is optional in our logic, so we just don't add a second input
5050
.output(new ItemStack(Items.COOKED_PORKCHOP))
51-
.time(10) // Campfires usually take longer (30 seconds)
51+
.time(10)
5252
.unlockedBy("has_porkchop", has(Items.PORKCHOP))
5353
.category(CraftingBookCategory.MISC)
54+
.bookCategory(ModRecipeDisplay.CAMPFIRE_SEARCH)
5455
.save(exporter, Identifier.fromNamespaceAndPath(MineTale.MOD_ID, "campfire_pork_cooking"));
5556

56-
new WorkbenchRecipeBuilder(ModRecipes.FURNACE_TYPE, ModRecipes.FURNACE_SERIALIZER)
57+
new WorkbenchRecipeBuilder(ModRecipes.FURNACE_T1_TYPE, ModRecipes.FURNACE_SERIALIZER)
5758
.input(Ingredient.of(Items.PORKCHOP))
58-
// Note: Slot 1 is optional in our logic, so we just don't add a second input
59-
.output(new ItemStack(Items.COOKED_PORKCHOP))
60-
.time(10) // Campfires usually take longer (30 seconds)
59+
.output(new ItemStack(Items.ACACIA_BOAT))
60+
.time(10)
6161
.unlockedBy("has_porkchop", has(Items.PORKCHOP))
6262
.category(CraftingBookCategory.MISC)
63+
.bookCategory(ModRecipeDisplay.FURNACE_T1_SEARCH)
6364
.save(exporter, Identifier.fromNamespaceAndPath(MineTale.MOD_ID, "furnace_pork_cooking"));
6465
}
6566
};

src/main/java/com/tcm/MineTale/datagen/builders/WorkbenchRecipeBuilder.java renamed to src/client/java/com/tcm/MineTale/datagen/builders/WorkbenchRecipeBuilder.java

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,15 @@
77

88
import org.jspecify.annotations.Nullable;
99

10-
import com.mojang.serialization.Codec;
11-
import com.mojang.serialization.MapCodec;
12-
import com.mojang.serialization.codecs.RecordCodecBuilder;
1310
import com.tcm.MineTale.recipe.WorkbenchRecipe;
11+
import com.tcm.MineTale.registry.ModRecipeDisplay;
1412

1513
import net.minecraft.advancements.Advancement;
1614
import net.minecraft.advancements.AdvancementRequirements;
1715
import net.minecraft.advancements.AdvancementRewards;
1816
import net.minecraft.advancements.Criterion;
1917
import net.minecraft.advancements.criterion.RecipeUnlockedTrigger;
18+
import net.minecraft.core.registries.BuiltInRegistries;
2019
import net.minecraft.core.registries.Registries;
2120
import net.minecraft.data.recipes.RecipeBuilder;
2221
import net.minecraft.data.recipes.RecipeOutput;
@@ -28,6 +27,7 @@
2827
import net.minecraft.world.item.crafting.CraftingBookCategory;
2928
import net.minecraft.world.item.crafting.Ingredient;
3029
import net.minecraft.world.item.crafting.Recipe;
30+
import net.minecraft.world.item.crafting.RecipeBookCategory;
3131
import net.minecraft.world.item.crafting.RecipeSerializer;
3232
import net.minecraft.world.item.crafting.RecipeType;
3333

@@ -38,29 +38,33 @@ public class WorkbenchRecipeBuilder implements RecipeBuilder {
3838
private final List<ItemStack> results = new ArrayList<>();
3939
private final Map<String, Criterion<?>> criteria = new LinkedHashMap<>();
4040
private CraftingBookCategory category = CraftingBookCategory.MISC;
41+
private Identifier bookCategory = BuiltInRegistries.RECIPE_BOOK_CATEGORY
42+
.getKey(ModRecipeDisplay.CAMPFIRE_SEARCH);
4143
private int cookTime = 200;
4244
@Nullable private String group;
4345

44-
/**
45-
* Creates a MapCodec that serializes and deserializes WorkbenchRecipe instances bound to the given recipe type and serializer.
46-
*
47-
* The codec encodes the recipe's ingredients, results, and cookTime (default 200) and constructs a WorkbenchRecipe using the provided type and serializer.
48-
*
49-
* @param type the RecipeType associated with the encoded WorkbenchRecipe
50-
* @param serializer the RecipeSerializer used to (de)serialize the WorkbenchRecipe
51-
* @return a MapCodec for WorkbenchRecipe that reads/writes ingredients, results, and cookTime and produces WorkbenchRecipe instances tied to the given type and serializer
52-
*/
53-
public static final MapCodec<WorkbenchRecipe> CODEC(RecipeType<WorkbenchRecipe> type, RecipeSerializer<WorkbenchRecipe> serializer) {
54-
return RecordCodecBuilder.mapCodec(inst -> inst.group(
55-
Ingredient.CODEC.listOf().fieldOf("ingredients").forGetter(WorkbenchRecipe::ingredients),
56-
ItemStack.STRICT_CODEC.listOf().fieldOf("results").forGetter(WorkbenchRecipe::results),
57-
Codec.INT.optionalFieldOf("cookTime", 200).forGetter(WorkbenchRecipe::cookTime),
58-
// Updated to CraftingBookCategory codec
59-
CraftingBookCategory.CODEC.optionalFieldOf("category", CraftingBookCategory.MISC).forGetter(WorkbenchRecipe::category)
60-
).apply(inst, (ingredients, results, cookTime, category) ->
61-
new WorkbenchRecipe(ingredients, results, cookTime, type, serializer, category)
62-
));
63-
}
46+
// /**
47+
// * Creates a MapCodec that serializes and deserializes WorkbenchRecipe instances bound to the given recipe type and serializer.
48+
// *
49+
// * The codec encodes the recipe's ingredients, results, and cookTime (default 200) and constructs a WorkbenchRecipe using the provided type and serializer.
50+
// *
51+
// * @param type the RecipeType associated with the encoded WorkbenchRecipe
52+
// * @param serializer the RecipeSerializer used to (de)serialize the WorkbenchRecipe
53+
// * @return a MapCodec for WorkbenchRecipe that reads/writes ingredients, results, and cookTime and produces WorkbenchRecipe instances tied to the given type and serializer
54+
// */
55+
// public static final MapCodec<WorkbenchRecipe> CODEC(RecipeType<WorkbenchRecipe> type, RecipeSerializer<WorkbenchRecipe> serializer) {
56+
// return RecordCodecBuilder.mapCodec(inst -> inst.group(
57+
// Ingredient.CODEC.listOf().fieldOf("ingredients").forGetter(WorkbenchRecipe::ingredients),
58+
// ItemStack.STRICT_CODEC.listOf().fieldOf("results").forGetter(WorkbenchRecipe::results),
59+
// Codec.INT.optionalFieldOf("cookTime", 200).forGetter(WorkbenchRecipe::cookTime),
60+
// // Updated to CraftingBookCategory codec
61+
// CraftingBookCategory.CODEC.optionalFieldOf("category", CraftingBookCategory.MISC).forGetter(WorkbenchRecipe::category),
62+
// Identifier.CODEC.fieldOf("book_category").forGetter(WorkbenchRecipe::bookCategory)
63+
// ).apply(inst, (ingredients, results, cookTime, category, bookCategory) ->
64+
// // 2. Pass the new bookCategory into the constructor
65+
// new WorkbenchRecipe(ingredients, results, cookTime, type, serializer, category, bookCategory)
66+
// ));
67+
// }
6468

6569
/**
6670
* Create a new WorkbenchRecipeBuilder configured for a specific recipe type and its serializer.
@@ -100,6 +104,15 @@ public WorkbenchRecipeBuilder category(CraftingBookCategory category) {
100104
return this;
101105
}
102106

107+
public WorkbenchRecipeBuilder bookCategory(RecipeBookCategory category) {
108+
Identifier id = BuiltInRegistries.RECIPE_BOOK_CATEGORY.getKey(category);
109+
if (id != null) {
110+
this.bookCategory = id;
111+
}
112+
return this;
113+
}
114+
115+
103116
/**
104117
* Adds an output item stack to the recipe.
105118
*
@@ -190,6 +203,9 @@ public void save(RecipeOutput recipeOutput, ResourceKey<Recipe<?>> resourceKey)
190203
.requirements(AdvancementRequirements.Strategy.OR);
191204

192205
// Add your criteria here (omitted for brevity)
206+
for (Map.Entry<String, Criterion<?>> entry : this.criteria.entrySet()) {
207+
advancement.addCriterion(entry.getKey(), entry.getValue());
208+
}
193209

194210
// 3. Create the Recipe Instance
195211
WorkbenchRecipe recipe = new WorkbenchRecipe(
@@ -198,7 +214,8 @@ public void save(RecipeOutput recipeOutput, ResourceKey<Recipe<?>> resourceKey)
198214
cookTime,
199215
this.type,
200216
this.serializer,
201-
this.category
217+
this.category,
218+
this.bookCategory
202219
);
203220

204221
// 4. Accept the recipe into the generator

src/client/java/com/tcm/MineTale/mixin/client/ClientRecipeBookMixin.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,19 @@
1111
import net.minecraft.client.ClientRecipeBook;
1212
import net.minecraft.world.item.crafting.RecipeBookCategory;
1313
import net.minecraft.world.item.crafting.RecipeHolder;
14+
import net.minecraft.world.item.crafting.RecipeType;
1415

1516
@Mixin(ClientRecipeBook.class)
1617
public abstract class ClientRecipeBookMixin {
1718
@Inject(method = "getCategory", at = @At("HEAD"), cancellable = true)
1819
private static void minetale$addCustomCategory(RecipeHolder<?> recipe, CallbackInfoReturnable<RecipeBookCategory> cir) {
19-
if (recipe.value().getType() == ModRecipes.FURNACE_SERIALIZER) {
20-
// This tells the search engine to put your recipes into your custom tab
20+
RecipeType<?> type = recipe.value().getType();
21+
22+
// Using .equals() satisfies the compiler
23+
if (ModRecipes.CAMPFIRE_TYPE.equals(type)) {
2124
cir.setReturnValue(ModRecipeDisplay.CAMPFIRE_SEARCH);
25+
} else if (ModRecipes.FURNACE_T1_TYPE.equals(type)) {
26+
cir.setReturnValue(ModRecipeDisplay.FURNACE_T1_SEARCH);
2227
}
2328
}
2429
}

src/client/java/com/tcm/MineTale/recipe/MineTaleRecipeBookComponent.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
import net.minecraft.world.entity.player.StackedItemContents;
1616
import net.minecraft.world.inventory.RecipeBookMenu;
1717
import net.minecraft.world.inventory.Slot;
18+
import net.minecraft.world.item.crafting.RecipeType;
1819
import net.minecraft.world.item.crafting.display.RecipeDisplay;
1920

2021
public class MineTaleRecipeBookComponent extends RecipeBookComponent<RecipeBookMenu> {
22+
private final RecipeType<?> filterType; // The specific machine type
2123

2224
// Standard button sprites (the "Filter" checkmark button)
2325
protected static final WidgetSprites FILTER_BUTTON_SPRITES = new WidgetSprites(
@@ -27,19 +29,31 @@ public class MineTaleRecipeBookComponent extends RecipeBookComponent<RecipeBookM
2729
Identifier.withDefaultNamespace("recipe_book/filter_disabled_focused")
2830
);
2931

30-
public MineTaleRecipeBookComponent(RecipeBookMenu recipeBookMenu, List<TabInfo> list) {
32+
public MineTaleRecipeBookComponent(RecipeBookMenu recipeBookMenu, List<TabInfo> list, RecipeType<?> filterType) {
3133
super(recipeBookMenu, list);
34+
this.filterType = filterType;
3235
}
3336

3437
@Override
3538
protected void selectMatchingRecipes(RecipeCollection recipeCollection, StackedItemContents stackedItemContents) {
3639
// Force everything to be "selected"
3740
// recipeCollection.selectRecipes(stackedItemContents, (recipeDisplay) -> true);
3841

42+
// recipeCollection.selectRecipes(stackedItemContents, (recipeDisplay) -> {
43+
// // Only allow recipes that use your custom Workbench display type
44+
// // This effectively filters out vanilla CraftingRecipeDisplays (the boats)
45+
// return recipeDisplay.type() == ModRecipeDisplay.WORKBENCH_TYPE;
46+
// });
47+
3948
recipeCollection.selectRecipes(stackedItemContents, (recipeDisplay) -> {
40-
// Only allow recipes that use your custom Workbench display type
41-
// This effectively filters out vanilla CraftingRecipeDisplays (the boats)
42-
return recipeDisplay.type() == ModRecipeDisplay.WORKBENCH_TYPE;
49+
// 1. Check if the display matches your custom type
50+
if (recipeDisplay.type() != ModRecipeDisplay.WORKBENCH_TYPE) return false;
51+
52+
// 2. We need to verify if the underlying recipe matches the current block's type
53+
// Note: In 1.21+, you may need to cast the display or check the recipe's origin
54+
// Here is the logic to ensure we only show recipes meant for THIS specific machine:
55+
return recipeDisplay instanceof WorkbenchRecipeDisplay wbDisplay &&
56+
wbDisplay.getRecipeType() == this.filterType;
4357
});
4458
}
4559

src/main/java/com/tcm/MineTale/block/workbenches/CampfireWorkbench.java

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import com.tcm.MineTale.registry.ModBlockEntities;
1111

1212
import net.minecraft.core.BlockPos;
13-
import net.minecraft.core.Direction;
1413
import net.minecraft.world.level.BlockGetter;
1514
import net.minecraft.world.level.Level;
1615
import net.minecraft.world.level.block.Block;
@@ -19,8 +18,6 @@
1918
import net.minecraft.world.level.block.entity.BlockEntityTicker;
2019
import net.minecraft.world.level.block.entity.BlockEntityType;
2120
import net.minecraft.world.level.block.state.BlockState;
22-
import net.minecraft.world.level.block.state.properties.ChestType;
23-
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
2421
import net.minecraft.world.phys.shapes.CollisionContext;
2522
import net.minecraft.world.phys.shapes.VoxelShape;
2623

@@ -98,35 +95,4 @@ public RenderShape getRenderShape(BlockState state) {
9895
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
9996
return SHAPE;
10097
}
101-
102-
/**
103-
* Create a block entity for the master block of this workbench.
104-
*
105-
* Only the master block of the multi-block workbench receives an entity; other positions return {@code null}.
106-
*
107-
* @return the block entity for the master block ({@link CampfireWorkbenchEntity}), or {@code null} if this position does not host an entity
108-
*/
109-
@Nullable
110-
@Override
111-
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
112-
// AbstractWorkbench logic ensures only the Master block gets the entity.
113-
// We override it here to point specifically to our Furnace entity.
114-
return super.newBlockEntity(pos, state);
115-
}
116-
117-
/**
118-
* Compute the master (base) block position for this block based on its state.
119-
*
120-
* @param state the block state of the current block
121-
* @param pos the position of the current block
122-
* @return the position of the master (base) block: if the block is the upper half, the block below is used; if the block's type is `RIGHT`, the position is offset one block counterclockwise from its facing direction; otherwise the original position
123-
*/
124-
public BlockPos getMasterPos(BlockState state, BlockPos pos) {
125-
BlockPos master = pos;
126-
Direction facing = state.getValue(FACING);
127-
if (state.getValue(HALF) == DoubleBlockHalf.UPPER) master = master.below();
128-
if (state.getValue(TYPE) == ChestType.RIGHT) master = master.relative(facing.getCounterClockWise());
129-
return master;
130-
}
131-
13298
}

0 commit comments

Comments
 (0)