Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public class ArmorersWorkbenchScreen extends AbstractRecipeBookScreen<ArmorersWo

private final MineTaleRecipeBookComponent mineTaleRecipeBook;

private RecipeDisplayId lastKnownSelectedId = null;

private Button craftOneBtn;
private Button craftTenBtn;
private Button craftAllBtn;
Expand Down Expand Up @@ -81,10 +83,10 @@ private static MineTaleRecipeBookComponent createRecipeBookComponent(ArmorersWor
ItemStack tabIcon = new ItemStack(ModBlocks.ARMORERS_WORKBENCH_BLOCK.asItem());

List<RecipeBookComponent.TabInfo> tabs = List.of(
new RecipeBookComponent.TabInfo(tabIcon.getItem(), ModRecipeDisplay.WORKBENCH_SEARCH)
new RecipeBookComponent.TabInfo(tabIcon.getItem(), ModRecipeDisplay.ARMORERS_SEARCH)
);

return new MineTaleRecipeBookComponent(menu, tabs, ModRecipes.WORKBENCH_TYPE);
return new MineTaleRecipeBookComponent(menu, tabs, ModRecipes.ARMORERS_TYPE);
}

/**
Expand Down Expand Up @@ -128,34 +130,52 @@ protected void init() {
* @param amount the quantity to craft; use -1 to request crafting of the full available stack ("All")
*/

private void handleCraftRequest(int amount) {
// 1. Cast the book component to the Accessor to get the selected data
RecipeBookComponentAccessor accessor = (RecipeBookComponentAccessor) this.mineTaleRecipeBook;
// private void handleCraftRequest(int amount) {
// // 1. Cast the book component to the Accessor to get the selected data
// RecipeBookComponentAccessor accessor = (RecipeBookComponentAccessor) this.mineTaleRecipeBook;

RecipeCollection collection = accessor.getLastRecipeCollection();
RecipeDisplayId displayId = accessor.getLastRecipe();

if (collection != null && displayId != null) {
// 2. Find the visual entry
for (RecipeDisplayEntry entry : collection.getSelectedRecipes(RecipeCollection.CraftableStatus.ANY)) {
if (entry.id().equals(displayId)) {
// 3. Resolve result for the packet
List<ItemStack> results = entry.resultItems(SlotDisplayContext.fromLevel(this.minecraft.level));
// RecipeCollection collection = accessor.getLastRecipeCollection();
// RecipeDisplayId displayId = accessor.getLastRecipe();

// if (collection != null && displayId != null) {
// // 2. Find the visual entry
// for (RecipeDisplayEntry entry : collection.getSelectedRecipes(RecipeCollection.CraftableStatus.ANY)) {
// if (entry.id().equals(displayId)) {
// // 3. Resolve result for the packet
// List<ItemStack> results = entry.resultItems(SlotDisplayContext.fromLevel(this.minecraft.level));

if (!results.isEmpty()) {
ItemStack resultStack = results.get(0);
// if (!results.isEmpty()) {
// ItemStack resultStack = results.get(0);

// 4. LOG FOR DEBUGGING
System.out.println("Sending craft request for: " + resultStack + " amount: " + amount);
// // 4. LOG FOR DEBUGGING
// System.out.println("Sending craft request for: " + resultStack + " amount: " + amount);

ClientPlayNetworking.send(new CraftRequestPayload(resultStack, amount));
}
break;
// ClientPlayNetworking.send(new CraftRequestPayload(resultStack, amount));
// }
// break;
// }
// }
// } else {
// System.out.println("Request failed: Collection or DisplayID is null!");
// }
// }

private void handleCraftRequest(int amount) {
// Look at our "Memory" instead of the component
if (this.lastKnownSelectedId != null) {
ClientRecipeBook book = this.minecraft.player.getRecipeBook();
RecipeDisplayEntry entry = ((ClientRecipeBookAccessor) book).getKnown().get(this.lastKnownSelectedId);

if (entry != null) {
List<ItemStack> results = entry.resultItems(SlotDisplayContext.fromLevel(this.minecraft.level));
if (!results.isEmpty()) {
System.out.println("Persistent Selection Success: " + results.get(0));
ClientPlayNetworking.send(new CraftRequestPayload(results.get(0), amount));
return;
}
}
} else {
System.out.println("Request failed: Collection or DisplayID is null!");
}
System.out.println("Request failed: No recipe was ever selected!");
}

/**
Expand All @@ -177,18 +197,22 @@ public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
renderBackground(graphics, mouseX, mouseY, delta);
super.render(graphics, mouseX, mouseY, delta);

// Get the ID of the recipe clicked in the ghost-book
RecipeDisplayId displayId = this.mineTaleRecipeBook.getSelectedRecipeId();
RecipeDisplayEntry selectedEntry = null;
// 1. Get the current selection from the book
RecipeDisplayId currentId = this.mineTaleRecipeBook.getSelectedRecipeId();

// 2. If it's NOT null, remember it!
if (currentId != null) {
this.lastKnownSelectedId = currentId;
}

if (displayId != null && this.minecraft.level != null) {
// 3. Use the remembered ID to find the entry for button activation
RecipeDisplayEntry selectedEntry = null;
if (this.lastKnownSelectedId != null && this.minecraft.level != null) {
ClientRecipeBook book = this.minecraft.player.getRecipeBook();
// Accessing the known recipes via your Accessor
Map<RecipeDisplayId, RecipeDisplayEntry> knownRecipes = ((ClientRecipeBookAccessor) book).getKnown();
selectedEntry = knownRecipes.get(displayId);
selectedEntry = ((ClientRecipeBookAccessor) book).getKnown().get(this.lastKnownSelectedId);
}

// 2. Button Activation Logic
// Logic for enabling/disabling buttons...
if (selectedEntry != null) {
// We use the entry directly. It contains the 15 ingredients needed!
boolean canCraftOne = canCraft(this.minecraft.player, selectedEntry, 1);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.tcm.MineTale.datagen;

import com.jcraft.jorbis.Block;
import com.tcm.MineTale.registry.ModBlocks;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.tcm.MineTale.block.workbenches.AbstractWorkbench;
import com.tcm.MineTale.registry.ModBlocks;
import com.tcm.MineTale.registry.ModItems;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricBlockLootTableProvider;
import net.minecraft.advancements.criterion.StatePropertiesPredicate;
Expand Down
42 changes: 20 additions & 22 deletions src/client/java/com/tcm/MineTale/datagen/ModRecipeProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

import com.tcm.MineTale.MineTale;
import com.tcm.MineTale.datagen.builders.WorkbenchRecipeBuilder;
import com.tcm.MineTale.registry.ModBlocks;
import com.tcm.MineTale.datagen.recipes.ArmorRecipes;
import com.tcm.MineTale.datagen.recipes.BuilderRecipes;
import com.tcm.MineTale.datagen.recipes.FarmerRecipes;
import com.tcm.MineTale.datagen.recipes.FurnitureRecipes;
import com.tcm.MineTale.datagen.recipes.WorkbenchRecipes;
import com.tcm.MineTale.registry.ModRecipeDisplay;
import com.tcm.MineTale.registry.ModRecipes;

Expand Down Expand Up @@ -68,27 +72,6 @@ public void buildRecipes() {
.bookCategory(ModRecipeDisplay.CAMPFIRE_SEARCH)
.save(exporter, "campfire_pork_cooking");

// Workbench Recipes

// 1. Workbenches
new WorkbenchRecipeBuilder(ModRecipes.WORKBENCH_TYPE, ModRecipes.WORKBENCH_SERIALIZER)
.input(Items.COPPER_INGOT, 2)
.input(ItemTags.LOGS, registryLookup, 10)
.input(ItemTags.STONE_TOOL_MATERIALS, registryLookup, 5)
.output(ModBlocks.ARMORERS_WORKBENCH_BLOCK.asItem())
.unlockedBy("has_workbench", has(ModBlocks.WORKBENCH_WORKBENCH_BLOCK.asItem()))
.bookCategory(ModRecipeDisplay.WORKBENCH_SEARCH)
.save(exporter, "workbench_armorers_workbench");

new WorkbenchRecipeBuilder(ModRecipes.WORKBENCH_TYPE, ModRecipes.WORKBENCH_SERIALIZER)
.input(ItemTags.LOGS, registryLookup, 6)
.input(ItemTags.STONE_TOOL_MATERIALS, registryLookup, 6)
.output(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1.asItem())
.unlockedBy("has_workbench", has(ModBlocks.WORKBENCH_WORKBENCH_BLOCK.asItem()))
.bookCategory(ModRecipeDisplay.WORKBENCH_SEARCH)
.save(exporter, "workbench_furnace_workbench_t1");


// 2. Chests
new WorkbenchRecipeBuilder(ModRecipes.WORKBENCH_TYPE, ModRecipes.WORKBENCH_SERIALIZER)
.input(ItemTags.LOGS, registryLookup, 10)
Expand All @@ -108,6 +91,21 @@ public void buildRecipes() {
.unlockedBy("has_copper_ore", has(Items.COPPER_ORE))
.bookCategory(ModRecipeDisplay.FURNACE_T1_SEARCH)
.save(exporter, "furnace_t1_copper_ingot");

// Workbench Recipes
WorkbenchRecipes.buildRecipes(this, exporter, registryLookup);

// Armor Recipes
ArmorRecipes.buildRecipes(this, exporter, registryLookup);

// Furniture Recipes
FurnitureRecipes.buildRecipes(this, exporter, registryLookup);

// Builder Recipes
BuilderRecipes.buildRecipes(this, exporter, registryLookup);

// Farmer Recipes
FarmerRecipes.buildRecipes(this, exporter, registryLookup);
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public class WorkbenchRecipeBuilder implements RecipeBuilder {
private CraftingBookCategory category = CraftingBookCategory.MISC;
private Identifier bookCategory = BuiltInRegistries.RECIPE_BOOK_CATEGORY
.getKey(ModRecipeDisplay.CAMPFIRE_SEARCH);
private int cookTime = 200;
private float cookTime = 200;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix cookTime type mismatch breaking the build.

cookTime is now a float, but WorkbenchRecipe still expects an int, triggering the lossy conversion error in the build. Convert the float seconds to int ticks at assignment (or update WorkbenchRecipe and its serializer to accept float if that’s truly required).

🛠️ Proposed fix (keep int ticks, allow float seconds)
-    private float cookTime = 200;
+    private int cookTime = 200;
@@
     public WorkbenchRecipeBuilder time(float seconds) {
-        this.cookTime = seconds * 20;
+        this.cookTime = Math.round(seconds * 20f);
         return this;
     }

Also applies to: 188-191, 268-272

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/client/java/com/tcm/MineTale/datagen/builders/WorkbenchRecipeBuilder.java`
at line 47, The cookTime field is declared as a float but WorkbenchRecipe (and
its serializer) expect an int, causing a lossy conversion error; change the
assignment sites for the cookTime field in WorkbenchRecipeBuilder so you convert
seconds (float) to int ticks immediately (e.g., intTicks = Math.round(seconds *
TICKS_PER_SECOND) or (int)(seconds * TICKS_PER_SECOND)) and store that int in
the field used to build WorkbenchRecipe, or alternatively update WorkbenchRecipe
and its serializer to accept float consistently; update all references to
cookTime in WorkbenchRecipeBuilder (field cookTime and any builder methods that
set it) to use the int tick value before constructing the WorkbenchRecipe so the
types match.

@Nullable private String group;

/**
Expand Down Expand Up @@ -185,7 +185,7 @@ public WorkbenchRecipeBuilder output(ItemLike stack) {
* @param seconds the cook time in seconds
* @return the builder instance
*/
public WorkbenchRecipeBuilder time(int seconds) {
public WorkbenchRecipeBuilder time(float seconds) {
this.cookTime = seconds * 20;
return this;
}
Expand Down
83 changes: 83 additions & 0 deletions src/client/java/com/tcm/MineTale/datagen/recipes/ArmorRecipes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.tcm.MineTale.datagen.recipes;

import com.tcm.MineTale.datagen.builders.WorkbenchRecipeBuilder;
import com.tcm.MineTale.registry.ModItems;
import com.tcm.MineTale.registry.ModRecipeDisplay;
import com.tcm.MineTale.registry.ModRecipes;

import net.minecraft.core.HolderLookup;
import net.minecraft.data.recipes.RecipeOutput;
import net.minecraft.data.recipes.RecipeProvider;
import net.minecraft.world.item.Items;

public class ArmorRecipes {
public static void buildRecipes(RecipeProvider provider, RecipeOutput exporter, HolderLookup.Provider lookup) {
new WorkbenchRecipeBuilder(ModRecipes.ARMORERS_TYPE, ModRecipes.ARMORERS_SERIALIZER)
.input(Items.COPPER_INGOT, 11)
.input(ModItems.PLANT_FIBER, 4)
.output(Items.COPPER_CHESTPLATE)
.time(3)
.unlockedBy("has_copper_ingot", provider.has(Items.COPPER_INGOT))
.bookCategory(ModRecipeDisplay.ARMORERS_SEARCH)
.save(exporter, "copper_cuirass");

new WorkbenchRecipeBuilder(ModRecipes.ARMORERS_TYPE, ModRecipes.ARMORERS_SERIALIZER)
.input(Items.COPPER_INGOT, 6)
.input(ModItems.PLANT_FIBER, 2)
.output(Items.COPPER_HELMET)
.time(3)
.unlockedBy("has_copper_ingot", provider.has(Items.COPPER_INGOT))
.bookCategory(ModRecipeDisplay.ARMORERS_SEARCH)
.save(exporter, "copper_helmet");

new WorkbenchRecipeBuilder(ModRecipes.ARMORERS_TYPE, ModRecipes.ARMORERS_SERIALIZER)
.input(Items.COPPER_INGOT, 9)
.input(ModItems.PLANT_FIBER, 3)
.output(Items.COPPER_LEGGINGS)
.time(3)
.unlockedBy("has_copper_ingot", provider.has(Items.COPPER_INGOT))
.bookCategory(ModRecipeDisplay.ARMORERS_SEARCH)
.save(exporter, "copper_greaves");

// CopperGauntlets - TODO: CopperGauntlets Not Implemented.
// new WorkbenchRecipeBuilder(ModRecipes.ARMORERS_TYPE, ModRecipes.ARMORERS_SERIALIZER)
// .input(Items.COPPER_INGOT, 5)
// .input(ModItems.PLANT_FIBER, 1)
// .output(Items.COPPER_LEGGINGS)
// .time(3)
// .unlockedBy("has_copper_ingot", provider.has(Items.COPPER_INGOT))
// .save(exporter, "copper_gauntlets");
Comment on lines +42 to +49
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Copper Gauntlets placeholder has the wrong output and is missing .bookCategory().

Line 46 has .output(Items.COPPER_LEGGINGS) — a direct copy-paste from the greaves recipe. When this block is uncommented it will silently produce leggings instead of gauntlets. It also lacks the .bookCategory(ModRecipeDisplay.ARMORERS_SEARCH) call present on all three active armor recipes.

🐛 Suggested corrections (for when this is uncommented)
-//     .output(Items.COPPER_LEGGINGS)
+//     .output(ModItems.COPPER_GAUNTLETS)  // replace with the actual gauntlets item when registered
     .time(3)
     .unlockedBy("has_copper_ingot", provider.has(Items.COPPER_INGOT))
+//     .bookCategory(ModRecipeDisplay.ARMORERS_SEARCH)
     .save(exporter, "copper_gauntlets");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// CopperGauntlets - TODO: CopperGauntlets Not Implemented.
// new WorkbenchRecipeBuilder(ModRecipes.ARMORERS_TYPE, ModRecipes.ARMORERS_SERIALIZER)
// .input(Items.COPPER_INGOT, 5)
// .input(ModItems.PLANT_FIBER, 1)
// .output(Items.COPPER_LEGGINGS)
// .time(3)
// .unlockedBy("has_copper_ingot", provider.has(Items.COPPER_INGOT))
// .save(exporter, "copper_gauntlets");
// CopperGauntlets - TODO: CopperGauntlets Not Implemented.
// new WorkbenchRecipeBuilder(ModRecipes.ARMORERS_TYPE, ModRecipes.ARMORERS_SERIALIZER)
// .input(Items.COPPER_INGOT, 5)
// .input(ModItems.PLANT_FIBER, 1)
// .output(ModItems.COPPER_GAUNTLETS) // replace with the actual gauntlets item when registered
// .time(3)
// .unlockedBy("has_copper_ingot", provider.has(Items.COPPER_INGOT))
// .bookCategory(ModRecipeDisplay.ARMORERS_SEARCH)
// .save(exporter, "copper_gauntlets");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/client/java/com/tcm/MineTale/datagen/recipes/ArmorRecipes.java` around
lines 42 - 49, The commented Copper Gauntlets recipe block wrongly outputs
Items.COPPER_LEGGINGS and is missing the recipe book category; when uncommenting
the WorkbenchRecipeBuilder block for the Copper Gauntlets (the one saving as
"copper_gauntlets"), change .output(Items.COPPER_LEGGINGS) to the correct
gauntlet item (e.g., ModItems.COPPER_GAUNTLETS or the project’s copper gauntlet
identifier) and add .bookCategory(ModRecipeDisplay.ARMORERS_SEARCH) to match the
other armor recipes before calling .save(exporter, "copper_gauntlets").


// TODO: WoodenArmor Not Implemented
// new WorkbenchRecipeBuilder(ModRecipes.ARMORERS_TYPE, ModRecipes.ARMORERS_SERIALIZER)
// .input(ItemTags.LOGS, lookup, 15)
// .input(ModItems.PLANT_FIBER, 3)
// .output(Items.COPPER_CHESTPLATE)
// .time(3)
// .unlockedBy("has_copper_ingot", provider.has(Items.COPPER_INGOT))
// .save(exporter, "copper_cuirass");
// new WorkbenchRecipeBuilder(ModRecipes.ARMORERS_TYPE, ModRecipes.ARMORERS_SERIALIZER)
// .input(Items.COPPER_INGOT, 6)
// .input(ModItems.PLANT_FIBER, 2)
// .output(Items.COPPER_HELMET)
// .time(3)
// .unlockedBy("has_copper_ingot", provider.has(Items.COPPER_INGOT))
// .save(exporter, "copper_helmet");
// new WorkbenchRecipeBuilder(ModRecipes.ARMORERS_TYPE, ModRecipes.ARMORERS_SERIALIZER)
// .input(Items.COPPER_INGOT, 9)
// .input(ModItems.PLANT_FIBER, 3)
// .output(Items.COPPER_LEGGINGS)
// .time(3)
// .unlockedBy("has_copper_ingot", provider.has(Items.COPPER_INGOT))
// .save(exporter, "copper_greaves");
// new WorkbenchRecipeBuilder(ModRecipes.ARMORERS_TYPE, ModRecipes.ARMORERS_SERIALIZER)
// .input(Items.COPPER_INGOT, 5)
// .input(ModItems.PLANT_FIBER, 1)
// .output(Items.COPPER_LEGGINGS)
// .time(3)
// .unlockedBy("has_copper_ingot", provider.has(Items.COPPER_INGOT))
// .save(exporter, "copper_gauntlets");
Comment on lines +51 to +79
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

WoodenArmor TODO block is an unmodified copy of the copper armor recipes — will crash datagen when uncommented.

The block still uses:

  • Items.COPPER_INGOT / Items.COPPER_HELMET / Items.COPPER_LEGGINGS / Items.COPPER_CHESTPLATE as inputs/outputs instead of any wood-based items.
  • The same save keys ("copper_cuirass", "copper_helmet", "copper_greaves") as the active recipes above.

Duplicate recipe ResourceKeys cause Minecraft's datagen to throw an IllegalStateException, so uncommenting this block as-is will break the data generation run. The inputs, outputs, unlock criterion, and save keys all need to be adapted to wooden armor items before activation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/client/java/com/tcm/MineTale/datagen/recipes/ArmorRecipes.java` around
lines 51 - 79, The commented WoodenArmor block is a copy of the copper recipes
and will cause duplicate ResourceKey errors when enabled; update the
WorkbenchRecipeBuilder calls (the block using WorkbenchRecipeBuilder,
ModRecipes.ARMORERS_TYPE, ModRecipes.ARMORERS_SERIALIZER) to use the correct
wood-based input and output item symbols instead of
Items.COPPER_INGOT/Items.COPPER_HELMET/Items.COPPER_LEGGINGS/Items.COPPER_CHESTPLATE,
change the unlock criterion provider.has(...) to reference the appropriate
wooden item (e.g., a wooden plank/ingot symbol from ModItems or ItemTags), and
give each .save(exporter, "...") a unique wood-specific key (do not reuse
"copper_cuirass", "copper_helmet", "copper_greaves", etc.) so the recipe
ResourceKeys are distinct from the copper recipes.



}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.tcm.MineTale.datagen.recipes;

import net.minecraft.core.HolderLookup;
import net.minecraft.data.recipes.RecipeOutput;
import net.minecraft.data.recipes.RecipeProvider;

public class BuilderRecipes {
public static void buildRecipes(RecipeProvider provider, RecipeOutput exporter, HolderLookup.Provider lookup) {

// TODO: BUILDERS_WORKBENCH_BLOCK & ROPE Not Implemented
// new WorkbenchRecipeBuilder(ModRecipes.BUILDER_TYPE, ModRecipes.BUILDER_SERIALIZER)
// .input(ModItems.PLANT_FIBER)
// .output(ModBlocks.ROPE.asItem())
// .time(3)
// .unlockedBy("has_builders_workbench", provider.has(ModBlocks.BUILDERS_WORKBENCH_BLOCK.asItem()))
// .bookCategory(ModRecipeDisplay.BUILDER_SEARCH)
// .save(exporter, "builders_workbench_rope");

// TODO: BUILDERS_WORKBENCH_BLOCK & ROPE_DIAGONAL Not Implemented
// new WorkbenchRecipeBuilder(ModRecipes.BUILDER_TYPE, ModRecipes.BUILDER_SERIALIZER)
// .input(ModItems.PLANT_FIBER)
// .output(ModBlocks.ROPE_DIAGONAL.asItem())
// .time(3)
// .unlockedBy("has_builders_workbench", provider.has(ModBlocks.BUILDERS_WORKBENCH_BLOCK.asItem()))
// .bookCategory(ModRecipeDisplay.BUILDER_SEARCH)
// .save(exporter, "builders_workbench_rope_diagonal");
}
}
Loading