From f42074a4bb206e8929611489f8124fc714c5b5cf Mon Sep 17 00:00:00 2001
From: Andy Wilson
Date: Thu, 26 Feb 2026 07:59:32 +0000
Subject: [PATCH 1/4] feat: add most recipes from farmers workbench
---
.../java/com/tcm/MineTale/MineTaleClient.java | 2 +
.../screen/ArmorersWorkbenchScreen.java | 40 -
.../screen/FarmersWorkbenchScreen.java | 302 +++++++
.../screen/WorkbenchWorkbenchScreen.java | 57 +-
.../datagen/recipes/FarmerRecipes.java | 850 +++++++++---------
.../datagen/recipes/WorkbenchRecipes.java | 25 +-
src/main/java/com/tcm/MineTale/MineTale.java | 66 +-
.../block/workbenches/FarmersWorkbench.java | 98 ++
.../entity/FarmersWorkbenchEntity.java | 176 ++++
.../menu/AbstractWorkbenchContainerMenu.java | 11 +-
.../menu/ArmorersWorkbenchMenu.java | 26 +-
.../menu/CampfireWorkbenchMenu.java | 3 +-
.../menu/FarmersWorkbenchMenu.java | 122 +++
.../menu/FurnaceWorkbenchMenu.java | 3 +-
.../menu/WorkbenchWorkbenchMenu.java | 31 +-
.../MineTale/registry/ModBlockEntities.java | 7 +
.../com/tcm/MineTale/registry/ModBlocks.java | 73 ++
.../com/tcm/MineTale/registry/ModItems.java | 1 -
.../tcm/MineTale/registry/ModMenuTypes.java | 6 +
.../MineTale/registry/ModRecipeDisplay.java | 4 +
.../com/tcm/MineTale/registry/ModRecipes.java | 8 +-
21 files changed, 1288 insertions(+), 623 deletions(-)
create mode 100644 src/client/java/com/tcm/MineTale/block/workbenches/screen/FarmersWorkbenchScreen.java
create mode 100644 src/main/java/com/tcm/MineTale/block/workbenches/FarmersWorkbench.java
create mode 100644 src/main/java/com/tcm/MineTale/block/workbenches/entity/FarmersWorkbenchEntity.java
create mode 100644 src/main/java/com/tcm/MineTale/block/workbenches/menu/FarmersWorkbenchMenu.java
diff --git a/src/client/java/com/tcm/MineTale/MineTaleClient.java b/src/client/java/com/tcm/MineTale/MineTaleClient.java
index 0fc17a7..9ba1872 100644
--- a/src/client/java/com/tcm/MineTale/MineTaleClient.java
+++ b/src/client/java/com/tcm/MineTale/MineTaleClient.java
@@ -9,6 +9,7 @@
import com.tcm.MineTale.block.workbenches.menu.AbstractWorkbenchContainerMenu;
import com.tcm.MineTale.block.workbenches.screen.ArmorersWorkbenchScreen;
import com.tcm.MineTale.block.workbenches.screen.CampfireWorkbenchScreen;
+import com.tcm.MineTale.block.workbenches.screen.FarmersWorkbenchScreen;
import com.tcm.MineTale.registry.ModBlocks;
import com.tcm.MineTale.registry.ModMenuTypes;
@@ -36,6 +37,7 @@ public void onInitializeClient() {
MenuScreens.register(ModMenuTypes.CAMPFIRE_WORKBENCH_MENU, CampfireWorkbenchScreen::new);
MenuScreens.register(ModMenuTypes.WORKBENCH_WORKBENCH_MENU, WorkbenchWorkbenchScreen::new);
MenuScreens.register(ModMenuTypes.ARMORERS_WORKBENCH_MENU, ArmorersWorkbenchScreen::new);
+ MenuScreens.register(ModMenuTypes.FARMERS_WORKBENCH_MENU, FarmersWorkbenchScreen::new);
BlockRenderLayerMap.putBlock(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1, ChunkSectionLayer.CUTOUT);
BlockRenderLayerMap.putBlock(ModBlocks.FURNACE_WORKBENCH_BLOCK_T2, ChunkSectionLayer.CUTOUT);
diff --git a/src/client/java/com/tcm/MineTale/block/workbenches/screen/ArmorersWorkbenchScreen.java b/src/client/java/com/tcm/MineTale/block/workbenches/screen/ArmorersWorkbenchScreen.java
index d90484e..c57a965 100644
--- a/src/client/java/com/tcm/MineTale/block/workbenches/screen/ArmorersWorkbenchScreen.java
+++ b/src/client/java/com/tcm/MineTale/block/workbenches/screen/ArmorersWorkbenchScreen.java
@@ -118,46 +118,6 @@ protected void init() {
}).bounds(defaultLeft + 40, defaultTop + 22, 35, 20).build());
}
- /**
- * Sends a crafting request for the currently selected recipe in the integrated recipe book.
- *
- * Locates the last recipe collection and last selected recipe ID from the recipe book component,
- * resolves the recipe's result item, and sends a CraftRequestPayload to the server containing that
- * item and the requested amount.
- *
- * @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;
-
- // 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 results = entry.resultItems(SlotDisplayContext.fromLevel(this.minecraft.level));
-
- // if (!results.isEmpty()) {
- // ItemStack resultStack = results.get(0);
-
- // // 4. LOG FOR DEBUGGING
- // System.out.println("Sending craft request for: " + resultStack + " amount: " + amount);
-
- // 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) {
diff --git a/src/client/java/com/tcm/MineTale/block/workbenches/screen/FarmersWorkbenchScreen.java b/src/client/java/com/tcm/MineTale/block/workbenches/screen/FarmersWorkbenchScreen.java
new file mode 100644
index 0000000..b34db37
--- /dev/null
+++ b/src/client/java/com/tcm/MineTale/block/workbenches/screen/FarmersWorkbenchScreen.java
@@ -0,0 +1,302 @@
+package com.tcm.MineTale.block.workbenches.screen;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import com.tcm.MineTale.MineTale;
+import com.tcm.MineTale.block.workbenches.menu.AbstractWorkbenchContainerMenu;
+import com.tcm.MineTale.block.workbenches.menu.FarmersWorkbenchMenu;
+import com.tcm.MineTale.mixin.client.ClientRecipeBookAccessor;
+import com.tcm.MineTale.mixin.client.RecipeBookComponentAccessor;
+import com.tcm.MineTale.network.CraftRequestPayload;
+import com.tcm.MineTale.recipe.MineTaleRecipeBookComponent;
+import com.tcm.MineTale.registry.ModBlocks;
+import com.tcm.MineTale.registry.ModRecipeDisplay;
+import com.tcm.MineTale.registry.ModRecipes;
+
+import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
+import net.minecraft.client.ClientRecipeBook;
+import net.minecraft.client.gui.GuiGraphics;
+import net.minecraft.client.gui.components.Button;
+import net.minecraft.client.gui.navigation.ScreenPosition;
+import net.minecraft.client.gui.screens.inventory.AbstractRecipeBookScreen;
+import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent;
+import net.minecraft.client.gui.screens.recipebook.RecipeCollection;
+import net.minecraft.client.renderer.RenderPipelines;
+import net.minecraft.core.Holder;
+import net.minecraft.resources.Identifier;
+import net.minecraft.world.entity.player.Inventory;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.item.Item;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.crafting.Ingredient;
+import net.minecraft.world.item.crafting.display.RecipeDisplayEntry;
+import net.minecraft.world.item.crafting.display.RecipeDisplayId;
+import net.minecraft.world.item.crafting.display.SlotDisplayContext;
+import net.minecraft.network.chat.Component;
+
+public class FarmersWorkbenchScreen extends AbstractRecipeBookScreen {
+ private static final Identifier TEXTURE =
+ Identifier.fromNamespaceAndPath(MineTale.MOD_ID, "textures/gui/container/workbench_workbench.png");
+
+ private final MineTaleRecipeBookComponent mineTaleRecipeBook;
+
+ private RecipeDisplayId lastKnownSelectedId = null;
+
+ private Button craftOneBtn;
+ private Button craftTenBtn;
+ private Button craftAllBtn;
+
+ /**
+ * Initialize a workbench GUI screen using the provided container menu, player inventory, and title.
+ *
+ * @param menu the menu supplying slots and synchronized state for this screen
+ * @param inventory the player's inventory to display and interact with
+ * @param title the title component shown at the top of the screen
+ */
+ public FarmersWorkbenchScreen(FarmersWorkbenchMenu menu, Inventory inventory, Component title) {
+ this(menu, inventory, title, createRecipeBookComponent(menu));
+ }
+
+ /**
+ * Creates a FarmersWorkbenchScreen bound to the given menu, player inventory, title, and recipe book component.
+ *
+ * @param menu the menu backing this screen
+ * @param inventory the player's inventory shown in the screen
+ * @param title the screen title component
+ * @param recipeBook the MineTaleRecipeBookComponent used to display and manage recipes in this screen
+ */
+ private FarmersWorkbenchScreen(FarmersWorkbenchMenu menu, Inventory inventory, Component title, MineTaleRecipeBookComponent recipeBook) {
+ super(menu, recipeBook, inventory, title);
+ this.mineTaleRecipeBook = recipeBook;
+ }
+
+ /**
+ * Create a MineTaleRecipeBookComponent configured for the workbench screen.
+ *
+ * @param menu the workbench menu used to initialize the recipe book component
+ * @return a MineTaleRecipeBookComponent containing the workbench tab and associated recipe category
+ */
+ private static MineTaleRecipeBookComponent createRecipeBookComponent(FarmersWorkbenchMenu menu) {
+ ItemStack tabIcon = new ItemStack(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem());
+
+ List tabs = List.of(
+ new RecipeBookComponent.TabInfo(tabIcon.getItem(), ModRecipeDisplay.FARMERS_SEARCH)
+ );
+
+ return new MineTaleRecipeBookComponent(menu, tabs, ModRecipes.FARMERS_TYPE);
+ }
+
+ /**
+ * Configure the screen's GUI dimensions and initialize widgets.
+ *
+ * Sets the layout size (imageWidth = 176, imageHeight = 166), delegates remaining
+ * layout initialization to the superclass, and creates the three craft buttons
+ * ("1", "10", "All") wired to their respective handlers.
+ */
+ @Override
+ protected void init() {
+ // Important: Set your GUI size before super.init()
+ this.imageWidth = 176;
+ this.imageHeight = 166;
+
+ super.init();
+
+ int defaultLeft = this.leftPos + 90;
+ int defaultTop = this.topPos + 25;
+
+ this.craftOneBtn = addRenderableWidget(Button.builder(Component.literal("Craft"), (button) -> {
+ handleCraftRequest(1);
+ }).bounds(defaultLeft, defaultTop, 75, 20).build());
+
+ this.craftTenBtn = addRenderableWidget(Button.builder(Component.literal("x10"), (button) -> {
+ handleCraftRequest(10);
+ }).bounds(defaultLeft, defaultTop + 22, 35, 20).build());
+
+ this.craftAllBtn = addRenderableWidget(Button.builder(Component.literal("All"), (button) -> {
+ handleCraftRequest(-1); // -1 represents "All" logic
+ }).bounds(defaultLeft + 40, defaultTop + 22, 35, 20).build());
+ }
+
+ /**
+ * Sends a crafting request for the currently selected recipe in the integrated recipe book.
+ *
+ * Locates the last recipe collection and last selected recipe ID from the recipe book component,
+ * resolves the recipe's result item, and sends a CraftRequestPayload to the server containing that
+ * item and the requested amount.
+ *
+ * @param amount the quantity to craft; use -1 to request crafting of the full available stack ("All")
+ */
+ 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 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;
+ }
+ }
+ }
+ System.out.println("Request failed: No recipe was ever selected!");
+ }
+
+ /**
+ * Draws the workbench GUI background texture at the screen's top-left corner.
+ *
+ * @param guiGraphics the graphics context used to draw GUI elements
+ * @param f partial tick time for interpolation
+ * @param i current mouse x coordinate relative to the window
+ * @param j current mouse y coordinate relative to the window
+ */
+ protected void renderBg(GuiGraphics guiGraphics, float f, int i, int j) {
+ int k = this.leftPos;
+ int l = this.topPos;
+ guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, k, l, 0.0F, 0.0F, this.imageWidth, this.imageHeight, 256, 256);
+ }
+
+ @Override
+ public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
+ renderBackground(graphics, mouseX, mouseY, delta);
+ super.render(graphics, mouseX, mouseY, delta);
+
+ // 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;
+ }
+
+ // 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();
+ selectedEntry = ((ClientRecipeBookAccessor) book).getKnown().get(this.lastKnownSelectedId);
+ }
+
+ // 2. Button Activation Logic
+ if (selectedEntry != null) {
+ // We use the entry directly. It contains the 15 ingredients needed!
+ boolean canCraftOne = canCraft(this.minecraft.player, selectedEntry, 1);
+ boolean canCraftMoreThanOne = canCraft(this.minecraft.player, selectedEntry, 2);
+ boolean canCraftTen = canCraft(this.minecraft.player, selectedEntry, 10);
+
+ this.craftOneBtn.active = canCraftOne;
+ this.craftTenBtn.active = canCraftTen;
+ this.craftAllBtn.active = canCraftMoreThanOne;
+ } else {
+ this.craftOneBtn.active = false;
+ this.craftTenBtn.active = false;
+ this.craftAllBtn.active = false;
+ }
+
+ renderTooltip(graphics, mouseX, mouseY);
+ }
+
+ /**
+ * Determines whether the player has enough ingredients to craft the given recipe the specified number of times.
+ *
+ * @param player the player whose inventory (and networked nearby items) will be checked; may be null
+ * @param entry the recipe display entry providing crafting requirements; may be null
+ * @param craftCount the multiplier for required ingredient quantities (e.g., 1, 10, or -1 is not specially handled here)
+ * @return `true` if the player has at least the required quantity of each ingredient multiplied by `craftCount`, `false` otherwise (also returns `false` if `player` or `entry` is null or the recipe has no requirements)
+ */
+ private boolean canCraft(Player player, RecipeDisplayEntry entry, int craftCount) {
+ if (player == null || entry == null) return false;
+
+ Optional> reqs = entry.craftingRequirements();
+ if (reqs.isEmpty()) return false;
+
+ // 1. Group ingredients by their underlying Item Holders.
+ // Using List> as the key ensures structural equality (content-based hashing).
+ Map>, Integer> aggregatedRequirements = new HashMap<>();
+ Map>, Ingredient> holderToIngredient = new HashMap<>();
+
+ for (Ingredient ing : reqs.get()) {
+ // Collect holders into a List to get a stable hashCode() and equals()
+ @SuppressWarnings("deprecation")
+ List> key = ing.items().toList();
+
+ // Aggregate the counts (how many of this specific ingredient set are required)
+ aggregatedRequirements.put(key, aggregatedRequirements.getOrDefault(key, 0) + 1);
+
+ // Map the list back to the original ingredient for use in hasIngredientAmount
+ holderToIngredient.putIfAbsent(key, ing);
+ }
+
+ // 2. Check the player's inventory against the aggregated totals
+ Inventory inv = player.getInventory();
+ for (Map.Entry>, Integer> entryReq : aggregatedRequirements.entrySet()) {
+ List> key = entryReq.getKey();
+ int totalNeeded = entryReq.getValue() * craftCount;
+
+ // Retrieve the original Ingredient object associated with this list of holders
+ Ingredient originalIng = holderToIngredient.get(key);
+
+ if (!hasIngredientAmount(inv, originalIng, totalNeeded)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean hasIngredientAmount(Inventory inventory, Ingredient ingredient, int totalRequired) {
+ System.out.println("DEBUG: Searching inventory + nearby for " + totalRequired + "...");
+ if (totalRequired <= 0) return true;
+
+ int found = 0;
+
+ // 1. Check Player Inventory
+ for (int i = 0; i < inventory.getContainerSize(); i++) {
+ ItemStack stack = inventory.getItem(i);
+ if (!stack.isEmpty() && ingredient.test(stack)) {
+ found += stack.getCount();
+ }
+ }
+
+ // 2. CHECK THE NETWORKED ITEMS FROM CHESTS
+ // This is the list we sent via the packet!
+ if (this.menu instanceof AbstractWorkbenchContainerMenu workbenchMenu) {
+ for (ItemStack stack : workbenchMenu.getNetworkedNearbyItems()) {
+ if (!stack.isEmpty() && ingredient.test(stack)) {
+ found += stack.getCount();
+ System.out.println("DEBUG: Found " + stack.getCount() + " in nearby networked list. Total: " + found);
+ }
+ }
+ }
+
+ if (found >= totalRequired) {
+ System.out.println("DEBUG: Requirement MET with " + found + "/" + totalRequired);
+ return true;
+ }
+
+ System.out.println("DEBUG: FAILED. Only found: " + found + "/" + totalRequired);
+ return false;
+ }
+
+ /**
+ * Computes the on-screen position for the recipe book toggle button for this GUI.
+ *
+ * @return the screen position placed 5 pixels from the GUI's left edge and 49 pixels above the GUI's vertical center
+ */
+ @Override
+ protected ScreenPosition getRecipeBookButtonPosition() {
+ // 1. Calculate the start (left) of your workbench GUI
+ int guiLeft = (this.width - this.imageWidth) / 2;
+
+ // 2. Calculate the top of your workbench GUI
+ int guiTop = (this.height - this.imageHeight) / 2;
+
+ // 3. Standard Vanilla positioning:
+ // Usually 5 pixels in from the left and 49 pixels up from the center
+ return new ScreenPosition(guiLeft + 5, guiTop + this.imageHeight / 2 - 49);
+ }
+}
\ No newline at end of file
diff --git a/src/client/java/com/tcm/MineTale/block/workbenches/screen/WorkbenchWorkbenchScreen.java b/src/client/java/com/tcm/MineTale/block/workbenches/screen/WorkbenchWorkbenchScreen.java
index e628c65..4bcf340 100644
--- a/src/client/java/com/tcm/MineTale/block/workbenches/screen/WorkbenchWorkbenchScreen.java
+++ b/src/client/java/com/tcm/MineTale/block/workbenches/screen/WorkbenchWorkbenchScreen.java
@@ -43,6 +43,8 @@ public class WorkbenchWorkbenchScreen extends AbstractRecipeBookScreen results = entry.resultItems(SlotDisplayContext.fromLevel(this.minecraft.level));
-
- if (!results.isEmpty()) {
- ItemStack resultStack = results.get(0);
-
- // 4. LOG FOR DEBUGGING
- System.out.println("Sending craft request for: " + resultStack + " amount: " + amount);
-
- ClientPlayNetworking.send(new CraftRequestPayload(resultStack, amount));
- }
- break;
+ // 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 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!");
}
/**
@@ -177,15 +166,19 @@ 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 knownRecipes = ((ClientRecipeBookAccessor) book).getKnown();
- selectedEntry = knownRecipes.get(displayId);
+ selectedEntry = ((ClientRecipeBookAccessor) book).getKnown().get(this.lastKnownSelectedId);
}
// 2. Button Activation Logic
diff --git a/src/client/java/com/tcm/MineTale/datagen/recipes/FarmerRecipes.java b/src/client/java/com/tcm/MineTale/datagen/recipes/FarmerRecipes.java
index eb0f05e..8279ba7 100644
--- a/src/client/java/com/tcm/MineTale/datagen/recipes/FarmerRecipes.java
+++ b/src/client/java/com/tcm/MineTale/datagen/recipes/FarmerRecipes.java
@@ -1,24 +1,31 @@
package com.tcm.MineTale.datagen.recipes;
+import com.tcm.MineTale.datagen.builders.WorkbenchRecipeBuilder;
+import com.tcm.MineTale.registry.ModBlocks;
+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.tags.ItemTags;
+import net.minecraft.world.level.block.Blocks;
public class FarmerRecipes {
public static void buildRecipes(RecipeProvider provider, RecipeOutput exporter, HolderLookup.Provider lookup) {
- // TODO: ChickenCoop Not Implemented
// new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
// .input(ItemTags.PLANKS, lookup, 20)
// .input(ModItems.ESSENCE_OF_LIFE, 50)
// .input(ModItems.PLANT_FIBER, 6)
- // .output(ModBlocks.CHICKEN_COOP.asItem())
+ // .output(ModBlocks.CHICKEN_COOP)
// .time(2)
- // .unlockedBy("has_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH.asItem()))
+ // .unlockedBy("has_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
// .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
// .save(exporter, "CHICKEN_COOP");
// TODO: LOTS Not Implemented
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
+ // new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
// .input(ModItems.GREATER_ESSENCE_OF_LIFE, 100)
// .input(ModItems.WILD_BERRY, 100)
// .input(Items.WHEAT, 100)
@@ -47,437 +54,428 @@ public static void buildRecipes(RecipeProvider provider, RecipeOutput exporter,
// .input(ModItems.PLANT_FIBER, 100)
// .input(ItemTags.MILK_BUCKET, 8)
// .input(ItemTags.MOSS_HORN_MILK_BUCKET, 8)
- // .output(ModBlocks.HARVEST_TROPHY.asItem())
+ // .output(ModBlocks.HARVEST_TROPHY)
// .time(10)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
+ // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ // .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
// .save(exporter, "farmers_workbench_harvest_trophy");
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModItems.PLANT_FIBER, 20)
- // .input(ItemTags.SOIL, 2)
- // .input(ModItems.ESSENCE_OF_LIFE, 10)
- // .input(ModBlocks.BAMBOO_LOG)
- // .output(ModBlocks.BAMBOO_PLANTER.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "BAMBOO_PLANTER");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.BLUE_MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.BLUE_MOSS_BLOCK.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "BLUE_MOSS_BLOCK");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.DARK_GREEN_MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.DARK_GREEN_MOSS_BLOCK.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "DARK_GREEN_MOSS_BLOCK");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.GREEN_MOSS_RUG.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "GREEN_MOSS_RUG");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.RED_MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.RED_MOSS_BLOCK.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "RED_MOSS_BLOCK");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.BLUE_MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.BLUE_MOSS_RUG.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "BLUE_MOSS_RUG");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.RED_MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.RED_HANGING_MOSS.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "RED_HANGING_MOSS");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.DARK_GREEN_MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.DARK_GREEN_HANGING_MOSS.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "DARK_GREEN_HANGING_MOSS");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.BLUE_MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.BLUE_HANGING_MOSS.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "BLUE_HANGING_MOSS");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.YELLOW_MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.YELLOW_MOSS_BLOCK.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "YELLOW_MOSS_BLOCK");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.YELLOW_MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.YELLOW_HANGING_MOSS.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "YELLOW_HANGING_MOSS");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.GREEN_MOSS_BLOCK.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "GREEN_MOSS_BLOCK");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.GREEN_HANGING_MOSS.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "GREEN_HANGING_MOSS");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.DARK_GREEN_MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.DARK_GREEN_MOSS_RUG.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "DARK_GREEN_MOSS_RUG");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.YELLOW_MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.YELLOW_MOSS_RUG.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "YELLOW_MOSS_RUG");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.SORREL_RUG.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "SORREL_RUG");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.RED_MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.RED_MOSS_RUG.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "RED_MOSS_RUG");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.RED_MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.SHORT_RED_MOSS.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "SHORT_RED_MOSS");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.YELLOW_MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.SHORT_YELLOW_MOSS.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "SHORT_YELLOW_MOSS");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.BLUE_MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.SHORT_BLUE_MOSS.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "SHORT_BLUE_MOSS");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.DARK_GREEN_MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.SHORT_DARK_GREEN_MOSS.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "SHORT_DARK_GREEN_MOSS");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.SHORT_MOSS.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "SHORT_MOSS");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModBlocks.BLUE_MOSS, 4)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.SHORT_BLUE_MOSS.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "SHORT_BLUE_MOSS");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModItems.ESSENCE_OF_LIFE)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.LIANA.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "LIANA");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModItems.ESSENCE_OF_LIFE)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.VINE.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "VINE");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModItems.ESSENCE_OF_LIFE)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.DRY_VINE.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "DRY_VINE");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModItems.ESSENCE_OF_LIFE)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.POISONED_IVY.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "POISONED_IVY");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModItems.ESSENCE_OF_LIFE)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.WALL_IVY.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "WALL__IVY");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModItems.ESSENCE_OF_LIFE)
- // .input(ModItems.PLANT_FIBER, 2)
- // .output(ModBlocks.IVY.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "IVY");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(ModItems.ESSENCE_OF_LIFE)
- // .input(ModItems.PLANT_FIBER, 4)
- // .output(ModBlocks.VINE_RUG.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "VINE_RUG");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(BlockTags.SOIL)
- // .input(ModItems.PLANT_FIBER)
- // .output(ModBlocks.COLD_GRASS.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "COLD_GRASS");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(BlockTags.SOIL)
- // .input(ModItems.PLANT_FIBER)
- // .output(ModBlocks.BURNT_GRASS.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "BURNT_GRASS");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(BlockTags.SOIL)
- // .input(ModItems.PLANT_FIBER)
- // .output(ModBlocks.POISONED_DIRT.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "POISONED_DIRT");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(BlockTags.SOIL)
- // .input(ModItems.PLANT_FIBER)
- // .output(ModBlocks.DEEP_GRASS.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "DEEP_GRASS");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(BlockTags.SOIL)
- // .input(ModItems.PLANT_FIBER)
- // .output(ModBlocks.DRY_DIRT.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "DRY_DIRT");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(BlockTags.SOIL)
- // .input(ModItems.PLANT_FIBER)
- // .output(ModBlocks.DRY_GRASS.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "DRY_GRASS");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(BlockTags.SOIL)
- // .input(ModItems.PLANT_FIBER)
- // .output(ModBlocks.WET_GRASS.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "WET_GRASS");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(BlockTags.SOIL)
- // .input(ModItems.PLANT_FIBER)
- // .output(ModBlocks.SUMMER_GRASS.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "SUMMER_GRASS");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(BlockTags.SOIL)
- // .input(ModItems.PLANT_FIBER)
- // .output(Blocks.GRASS_BLOCK.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "GRASS_BLOCK");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(BlockTags.SOIL)
- // .input(ModItems.PLANT_FIBER)
- // .output(ModBlocks.COLD_DIRT.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "COLD_DIRT");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(BlockTags.SOIL)
- // .input(ModItems.PLANT_FIBER)
- // .output(ModBlocks.FULL_GRASS.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "FULL_GRASS");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(BlockTags.SOIL)
- // .input(ModItems.PLANT_FIBER)
- // .output(ModBlocks.NEEDLED_SOIL.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "NEEDLED_SOIL");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(BlockTags.SOIL)
- // .input(ModItems.PLANT_FIBER)
- // .output(ModBlocks.SOIL_PATHWAY.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "SOIL_PATHWAY");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(BlockTags.SOIL)
- // .input(ModItems.PLANT_FIBER)
- // .output(Blocks.MUD.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "MUD");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(BlockTags.SOIL)
- // .input(ModItems.PLANT_FIBER)
- // .output(ModBlocks.LEAFY_SOIL.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "LEAFY_SOIL");
-
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
- // .input(BlockTags.SOIL)
- // .input(ModItems.PLANT_FIBER)
- // .output(ModBlocks.DRY_MUD.asItem())
- // .time(2)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
- // .save(exporter, "DRY_MUD");
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModItems.PLANT_FIBER, 20)
+ .input(ItemTags.DIRT, lookup, 2)
+ .input(ModItems.ESSENCE_OF_LIFE, 10)
+ .input(ModBlocks.BAMBOO_LOG)
+ .output(ModBlocks.BAMBOO_PLANTER)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "bamboo_planter");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModBlocks.BLUE_MOSS, 4)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.BLUE_MOSS_BLOCK)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "blue_moss_block");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModBlocks.DARK_GREEN_MOSS, 4)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.DARK_GREEN_MOSS_BLOCK)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "dark_green_moss_block");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModBlocks.MOSS, 4)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.GREEN_MOSS_RUG)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "green_moss_rug");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModBlocks.RED_MOSS, 4)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.RED_MOSS_BLOCK)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "red_moss_block");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModBlocks.BLUE_MOSS, 4)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.BLUE_MOSS_RUG)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "blue_moss_rug");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModBlocks.RED_MOSS, 4)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.RED_HANGING_MOSS)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "red_hanging_moss");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModBlocks.DARK_GREEN_MOSS, 4)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.DARK_GREEN_HANGING_MOSS)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "dark_green_hanging_moss");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModBlocks.BLUE_MOSS, 4)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.BLUE_HANGING_MOSS)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "blue_hanging_moss");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModBlocks.YELLOW_MOSS, 4)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.YELLOW_MOSS_BLOCK)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "yellow_moss_block");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModBlocks.YELLOW_MOSS, 4)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.YELLOW_HANGING_MOSS)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "yellow_hanging_moss");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModBlocks.MOSS, 4)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.GREEN_MOSS_BLOCK)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "green_moss_block");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModBlocks.MOSS, 4)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.GREEN_HANGING_MOSS)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "green_hanging_moss");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModBlocks.DARK_GREEN_MOSS, 4)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.DARK_GREEN_MOSS_RUG)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "dark_green_moss_rug");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModBlocks.YELLOW_MOSS, 4)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.YELLOW_MOSS_RUG)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "yellow_moss_rug");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModBlocks.MOSS, 4)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.SORREL_RUG)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "sorrel_rug");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModBlocks.RED_MOSS, 4)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.RED_MOSS_RUG)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "red_moss_rug");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModBlocks.RED_MOSS, 4)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.SHORT_RED_MOSS)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "short_red_moss");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModBlocks.YELLOW_MOSS, 4)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.SHORT_YELLOW_MOSS)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "short_yellow_moss");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModBlocks.BLUE_MOSS, 4)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.SHORT_BLUE_MOSS)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "short_blue_moss");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModBlocks.DARK_GREEN_MOSS, 4)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.SHORT_DARK_GREEN_MOSS)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "short_dark_green_moss");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModBlocks.MOSS, 4)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.SHORT_MOSS)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "short_moss");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModItems.ESSENCE_OF_LIFE)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.LIANA)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "liana");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModItems.ESSENCE_OF_LIFE)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.VINE)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "vine");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModItems.ESSENCE_OF_LIFE)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.DRY_VINE)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "dry_vine");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModItems.ESSENCE_OF_LIFE)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.POISONED_IVY)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "poisoned_ivy");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModItems.ESSENCE_OF_LIFE)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.WALL_IVY)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "wall_ivy");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModItems.ESSENCE_OF_LIFE)
+ .input(ModItems.PLANT_FIBER, 2)
+ .output(ModBlocks.IVY)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "ivy");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ModItems.ESSENCE_OF_LIFE)
+ .input(ModItems.PLANT_FIBER, 4)
+ .output(ModBlocks.VINE_RUG)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "vine_rug");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ItemTags.DIRT, lookup)
+ .input(ModItems.PLANT_FIBER)
+ .output(ModBlocks.COLD_GRASS)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "cold_grass");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ItemTags.DIRT, lookup)
+ .input(ModItems.PLANT_FIBER)
+ .output(ModBlocks.BURNT_GRASS)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "burnt_grass");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ItemTags.DIRT, lookup)
+ .input(ModItems.PLANT_FIBER)
+ .output(ModBlocks.POISONED_DIRT)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "poisoned_dirt");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ItemTags.DIRT, lookup)
+ .input(ModItems.PLANT_FIBER)
+ .output(ModBlocks.DEEP_GRASS)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "deep_grass");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ItemTags.DIRT, lookup)
+ .input(ModItems.PLANT_FIBER)
+ .output(ModBlocks.DRY_DIRT)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "dry_dirt");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ItemTags.DIRT, lookup)
+ .input(ModItems.PLANT_FIBER)
+ .output(ModBlocks.DRY_GRASS)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "dry_grass");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ItemTags.DIRT, lookup)
+ .input(ModItems.PLANT_FIBER)
+ .output(ModBlocks.WET_GRASS)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "wet_grass");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ItemTags.DIRT, lookup)
+ .input(ModItems.PLANT_FIBER)
+ .output(ModBlocks.SUMMER_GRASS)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "summer_grass");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ItemTags.DIRT, lookup)
+ .input(ModItems.PLANT_FIBER)
+ .output(Blocks.GRASS_BLOCK)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "grass_block");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ItemTags.DIRT, lookup)
+ .input(ModItems.PLANT_FIBER)
+ .output(ModBlocks.COLD_DIRT)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "cold_dirt");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ItemTags.DIRT, lookup)
+ .input(ModItems.PLANT_FIBER)
+ .output(ModBlocks.FULL_GRASS)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "full_grass");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ItemTags.DIRT, lookup)
+ .input(ModItems.PLANT_FIBER)
+ .output(ModBlocks.NEEDLED_SOIL)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "needled_soil");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ItemTags.DIRT, lookup)
+ .input(ModItems.PLANT_FIBER)
+ .output(ModBlocks.SOIL_PATHWAY)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "soil_pathway");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ItemTags.DIRT, lookup)
+ .input(ModItems.PLANT_FIBER)
+ .output(Blocks.MUD)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "mud");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ItemTags.DIRT, lookup)
+ .input(ModItems.PLANT_FIBER)
+ .output(ModBlocks.LEAFY_SOIL)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "leafy_soil");
+
+ new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
+ .input(ItemTags.DIRT, lookup)
+ .input(ModItems.PLANT_FIBER)
+ .output(ModBlocks.DRY_MUD)
+ .time(2)
+ .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
+ .save(exporter, "dry_mud");
- // new WorkbenchRecipeBuilder(ModRecipes.FARMER_TYPE, ModRecipes.FARMER_SERIALIZER)
+ // new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
// .input(ItemTags.LOGS, lookup, 10)
// .input(ModItems.ESSENCE_OF_LIFE, 50)
// .input(ModItems.PLANT_FIBER, 20)
// .input(Items.IRON_INGOT)
- // .output(ModBlocks.FISHING_TRAP.asItem())
+ // .output(ModBlocks.FISHING_TRAP)
// .time(10)
- // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.FARMER_SEARCH)
+ // .unlockedBy("has_farmers_workbench", provider.has(ModBlocks.FARMERS_WORKBENCH_BLOCK))
+ // .bookCategory(ModRecipeDisplay.FARMERS_SEARCH)
// .save(exporter, "FISHING_TRAP");
}
}
diff --git a/src/client/java/com/tcm/MineTale/datagen/recipes/WorkbenchRecipes.java b/src/client/java/com/tcm/MineTale/datagen/recipes/WorkbenchRecipes.java
index 63ce4ba..c0ff6b3 100644
--- a/src/client/java/com/tcm/MineTale/datagen/recipes/WorkbenchRecipes.java
+++ b/src/client/java/com/tcm/MineTale/datagen/recipes/WorkbenchRecipes.java
@@ -2,6 +2,7 @@
import com.tcm.MineTale.datagen.builders.WorkbenchRecipeBuilder;
import com.tcm.MineTale.registry.ModBlocks;
+import com.tcm.MineTale.registry.ModItems;
import com.tcm.MineTale.registry.ModRecipeDisplay;
import com.tcm.MineTale.registry.ModRecipes;
@@ -17,30 +18,30 @@ public static void buildRecipes(RecipeProvider provider, RecipeOutput exporter,
.input(Items.COPPER_INGOT, 2)
.input(ItemTags.LOGS, lookup, 10)
.input(ItemTags.STONE_TOOL_MATERIALS, lookup, 5)
- .output(ModBlocks.ARMORERS_WORKBENCH_BLOCK.asItem())
+ .output(ModBlocks.ARMORERS_WORKBENCH_BLOCK)
.time(3)
- .unlockedBy("has_workbench", provider.has(ModBlocks.WORKBENCH_WORKBENCH_BLOCK.asItem()))
+ .unlockedBy("has_workbench", provider.has(ModBlocks.WORKBENCH_WORKBENCH_BLOCK))
.bookCategory(ModRecipeDisplay.WORKBENCH_SEARCH)
.save(exporter, "workbench_armorers_workbench");
new WorkbenchRecipeBuilder(ModRecipes.WORKBENCH_TYPE, ModRecipes.WORKBENCH_SERIALIZER)
.input(ItemTags.LOGS, lookup, 6)
.input(ItemTags.STONE_TOOL_MATERIALS, lookup, 6)
- .output(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1.asItem())
+ .output(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1)
.time(3)
- .unlockedBy("has_workbench", provider.has(ModBlocks.WORKBENCH_WORKBENCH_BLOCK.asItem()))
+ .unlockedBy("has_workbench", provider.has(ModBlocks.WORKBENCH_WORKBENCH_BLOCK))
.bookCategory(ModRecipeDisplay.WORKBENCH_SEARCH)
.save(exporter, "workbench_furnace_workbench_t1");
// TODO: FarmersWorkbench Not Implemented
- // new WorkbenchRecipeBuilder(ModRecipes.WORKBENCH_TYPE, ModRecipes.WORKBENCH_SERIALIZER)
- // .input(ItemTags.LOGS, lookup, 6)
- // .input(ModItems.PLANT_FIBER, 20)
- // .output(ModBlocks.FARMERS_WORKBENCH)
- // .time(3)
- // .unlockedBy("has_workbench", provider.has(ModBlocks.WORKBENCH_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.WORKBENCH_SEARCH)
- // .save(exporter, "workbench_farmers_workbench");
+ new WorkbenchRecipeBuilder(ModRecipes.WORKBENCH_TYPE, ModRecipes.WORKBENCH_SERIALIZER)
+ .input(ItemTags.LOGS, lookup, 6)
+ .input(ModItems.PLANT_FIBER, 20)
+ .output(ModBlocks.FARMERS_WORKBENCH_BLOCK)
+ .time(3)
+ .unlockedBy("has_workbench", provider.has(ModBlocks.WORKBENCH_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.WORKBENCH_SEARCH)
+ .save(exporter, "workbench_farmers_workbench");
// TODO: Builder's Workbench Not Implemented
// new WorkbenchRecipeBuilder(ModRecipes.WORKBENCH_TYPE, ModRecipes.WORKBENCH_SERIALIZER)
diff --git a/src/main/java/com/tcm/MineTale/MineTale.java b/src/main/java/com/tcm/MineTale/MineTale.java
index 44dc877..fecd8f6 100644
--- a/src/main/java/com/tcm/MineTale/MineTale.java
+++ b/src/main/java/com/tcm/MineTale/MineTale.java
@@ -19,7 +19,7 @@
import com.tcm.MineTale.block.workbenches.entity.AbstractWorkbenchEntity;
import com.tcm.MineTale.block.workbenches.menu.AbstractWorkbenchContainerMenu;
import com.tcm.MineTale.block.workbenches.menu.ArmorersWorkbenchMenu;
-import com.tcm.MineTale.block.workbenches.menu.WorkbenchWorkbenchMenu;
+import com.tcm.MineTale.block.workbenches.menu.FarmersWorkbenchMenu;
import com.tcm.MineTale.network.ClientboundNearbyInventorySyncPacket;
import com.tcm.MineTale.network.CraftRequestPayload;
import com.tcm.MineTale.recipe.WorkbenchRecipe;
@@ -81,80 +81,26 @@ public void onInitialize() {
PayloadTypeRegistry.playS2C().register(ClientboundNearbyInventorySyncPacket.TYPE, ClientboundNearbyInventorySyncPacket.STREAM_CODEC);
- // Register the server-side receiver using .TYPE
- // ServerPlayNetworking.registerGlobalReceiver(CraftRequestPayload.TYPE, (payload, context) -> {
- // context.server().execute(() -> {
- // ServerPlayer player = context.player();
-
- // // --- SECURITY GUARD ---
- // // Ensure the player actually has the Workbench UI open before processing the craft
- // if (!(player.containerMenu instanceof WorkbenchWorkbenchMenu)) {
- // return;
- // }
-
- // ItemStack requestedResult = payload.resultItem();
- // int amount = payload.amount();
-
- // // 1. Get the RecipeManager from the server level
- // RecipeManager recipeManager = player.level().recipeAccess();
-
- // // 2. Find the recipe by matching the output ItemStack
- // Optional> recipeOpt = recipeManager.getAllOfType(ModRecipes.WORKBENCH_TYPE).stream()
- // .filter(holder -> {
- // // Guard against recipes with no results before accessing index 0
- // if (holder.value().results().isEmpty()) {
- // return false;
- // }
-
- // // Compare the first result of the workbench recipe to the requested item
- // ItemStack result = holder.value().results().get(0);
- // return ItemStack.isSameItem(result, requestedResult);
- // })
- // .findFirst();
-
- // if (recipeOpt.isPresent()) {
- // WorkbenchRecipe recipe = recipeOpt.get().value();
-
- // // 2. Determine craft limit (Handle "All" logic)
- // int limit = (amount == -1) ? 64 : Math.min(Math.max(amount, 0), 64);
-
- // for (int i = 0; i < limit; i++) {
- // if (hasIngredients(player, recipe)) {
- // consumeIngredients(player, recipe);
- // player.getInventory().add(recipe.results().get(0).copy());
- // } else {
- // break;
- // }
- // }
-
- // // 3. Sync inventory changes to the client screen
- // player.containerMenu.broadcastChanges();
- // }
- // });
- // });
-
ServerPlayNetworking.registerGlobalReceiver(CraftRequestPayload.TYPE, (payload, context) -> {
context.server().execute(() -> {
ServerPlayer player = context.player();
// --- SELECTIVE SECURITY GUARD ---
// Only proceed if the menu is one of the two specific workbenches
- boolean isWorkbench = player.containerMenu instanceof WorkbenchWorkbenchMenu;
- boolean isArmorers = player.containerMenu instanceof ArmorersWorkbenchMenu;
+ boolean isWorkbench = player.containerMenu instanceof AbstractWorkbenchContainerMenu;
- if (!isWorkbench && !isArmorers) {
+ if (!isWorkbench) {
return; // Reject packets from Campfires, Furnaces, or other menus
}
+ AbstractWorkbenchContainerMenu instanceContainerMenu = (AbstractWorkbenchContainerMenu) player.containerMenu;
+
ItemStack requestedResult = payload.resultItem();
int amount = payload.amount();
RecipeManager recipeManager = player.level().recipeAccess();
- // 1. Determine which Recipe Type to search based on the open menu
- var targetType = isWorkbench ? ModRecipes.WORKBENCH_TYPE : ModRecipes.ARMORERS_TYPE;
-
// 2. Find the recipe within that specific type
- Optional> recipeOpt = recipeManager.getAllOfType(targetType).stream()
+ Optional> recipeOpt = recipeManager.getAllOfType(instanceContainerMenu.getRecipeType()).stream()
.filter(holder -> {
if (holder.value().results().isEmpty()) return false;
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/FarmersWorkbench.java b/src/main/java/com/tcm/MineTale/block/workbenches/FarmersWorkbench.java
new file mode 100644
index 0000000..b1b0c54
--- /dev/null
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/FarmersWorkbench.java
@@ -0,0 +1,98 @@
+package com.tcm.MineTale.block.workbenches;
+
+import java.util.function.Supplier;
+
+import org.jetbrains.annotations.Nullable;
+
+import com.mojang.serialization.MapCodec;
+import com.tcm.MineTale.block.workbenches.entity.AbstractWorkbenchEntity;
+import com.tcm.MineTale.block.workbenches.entity.FarmersWorkbenchEntity;
+import com.tcm.MineTale.registry.ModBlockEntities;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.level.BlockGetter;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.RenderShape;
+import net.minecraft.world.level.block.entity.BlockEntity;
+import net.minecraft.world.level.block.entity.BlockEntityTicker;
+import net.minecraft.world.level.block.entity.BlockEntityType;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.phys.shapes.CollisionContext;
+import net.minecraft.world.phys.shapes.VoxelShape;
+
+// ChestBlock
+
+public class FarmersWorkbench extends AbstractWorkbench {
+ public static final boolean IS_WIDE = true;
+ public static final boolean IS_TALL = false;
+
+ public static final MapCodec CODEC = simpleCodec(FarmersWorkbench::new);
+
+ /**
+ * Constructs a FarmersWorkbench that uses the mod's FARMERS_WORKBENCH_BE block entity type.
+ *
+ * @param properties block properties for this workbench
+ */
+ public FarmersWorkbench(Properties properties) {
+ // Hardcode the supplier and sounds here if they never change
+ super(properties, () -> ModBlockEntities.FARMERS_WORKBENCH_BE, IS_WIDE, IS_TALL, 1);
+ }
+
+ /**
+ * Constructs a FarmersWorkbench using the provided block properties and block-entity type supplier.
+ *
+ * @param properties block properties to apply to this workbench
+ * @param supplier supplier that provides the BlockEntityType for the FarmersWorkbenchEntity
+ */
+ public FarmersWorkbench(Properties properties, Supplier> supplier) {
+ super(properties, supplier, IS_WIDE, IS_TALL, 1);
+ }
+
+ /**
+ * Provides a ticker for workbench block entities when the supplied block entity type matches this block's entity type.
+ *
+ * @param type the block entity type to match against this block's workbench entity type
+ * @return a BlockEntityTicker that updates matching workbench block entities, or {@code null} if the types do not match
+ */
+ @Nullable
+ @Override
+ public BlockEntityTicker getTicker(Level level, BlockState state, BlockEntityType type) {
+ // This connects the Level's ticking system to your static tick method
+ return createTickerHelper(type, ModBlockEntities.FARMERS_WORKBENCH_BE, AbstractWorkbenchEntity::tick);
+ }
+
+ /**
+ * Provides the MapCodec used to serialize and deserialize this workbench.
+ *
+ * @return the MapCodec for this FarmersWorkbench
+ */
+ @Override
+ protected MapCodec extends FarmersWorkbench> codec() {
+ return CODEC;
+ }
+
+ /**
+ * Specifies that this block is rendered using its block model.
+ *
+ * @return RenderShape.MODEL to render the block using its JSON/model representation.
+ */
+ @Override
+ public RenderShape getRenderShape(BlockState state) {
+ // BaseEntityBlock defaults to INVISIBLE.
+ // We set it to MODEL so the JSON model is rendered.
+ return RenderShape.MODEL;
+ }
+
+ private static final VoxelShape SHAPE = Block.box(0, 0, 0, 16, 16, 16);
+
+ /**
+ * The block's collision and interaction shape as a 1×1 footprint (x 0–16, y 0–16, z 0–16).
+ *
+ * @return the voxel shape used for collision and interaction
+ */
+ @Override
+ public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
+ return SHAPE;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/entity/FarmersWorkbenchEntity.java b/src/main/java/com/tcm/MineTale/block/workbenches/entity/FarmersWorkbenchEntity.java
new file mode 100644
index 0000000..8b25b57
--- /dev/null
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/entity/FarmersWorkbenchEntity.java
@@ -0,0 +1,176 @@
+package com.tcm.MineTale.block.workbenches.entity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jspecify.annotations.Nullable;
+
+import com.mojang.serialization.Codec;
+import com.tcm.MineTale.block.workbenches.menu.FarmersWorkbenchMenu;
+import com.tcm.MineTale.recipe.WorkbenchRecipe;
+import com.tcm.MineTale.registry.ModBlockEntities;
+import com.tcm.MineTale.registry.ModRecipes;
+import com.tcm.MineTale.util.Constants;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.entity.player.Inventory;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerData;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.crafting.RecipeType;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.storage.ValueInput;
+import net.minecraft.world.level.storage.ValueOutput;
+
+public class FarmersWorkbenchEntity extends AbstractWorkbenchEntity {
+ protected final ContainerData data = new ContainerData() {
+ /**
+ * Retrieves an internal data value by index for UI synchronization.
+ *
+ * @param index There is no cook time or anything for this block as it doesnt use it
+ * @return the value associated with {@code index}, or 0 for any other index
+ */
+ @Override
+ public int get(int index) {
+ return switch (index) {
+ default -> 0;
+ };
+ }
+
+ /**
+ * No-op for this workbench; data is server-driven and not set client-side.
+ *
+ * `@param` index the data index to set
+ * `@param` value the value to assign (ignored)
+ */
+ @Override
+ public void set(int index, int value) {
+ // Not required on WorkbenchEntity
+ }
+
+ /**
+ * The number of data values exposed by this ContainerData.
+ *
+ * @return the number of data entries (4)
+ */
+ @Override
+ public int getCount() {
+ return 4;
+ }
+ };
+
+ /**
+ * Creates a FarmersWorkbenchEntity for the specified world position and block state.
+ *
+ * Sets the entity's scanRadius to 0.0 and tier to 1.
+ *
+ * @param blockPos the world position of this block entity
+ * @param blockState the block state for this block entity
+ */
+ public FarmersWorkbenchEntity(BlockPos blockPos, BlockState blockState) {
+ super(ModBlockEntities.FARMERS_WORKBENCH_BE, blockPos, blockState);
+
+ this.tier = 1;
+ this.canPullFromNearby = true;
+ }
+
+ /**
+ * Persist this workbench's state to the given ValueOutput.
+ *
+ * Stores "WorkbenchTier" (int), "ScanRadius" (double), and the full inventory under "Inventory"
+ * using type-safe Codecs.
+ *
+ * @param valueOutput the writer used to serialize this entity's fields
+ */
+ @Override
+ protected void saveAdditional(ValueOutput valueOutput) {
+ super.saveAdditional(valueOutput);
+ // store() uses Codecs for type safety
+ valueOutput.store("WorkbenchTier", Codec.INT, this.tier);
+ valueOutput.store("ScanRadius", Codec.DOUBLE, this.scanRadius);
+
+ // Convert the SimpleContainer to a List of ItemStacks for the Codec
+ // Or use the built-in NBT helper if your framework supports it
+ List stacks = new ArrayList<>();
+ for (int i = 0; i < inventory.getContainerSize(); i++) {
+ stacks.add(inventory.getItem(i));
+ }
+
+ // CHANGE: Use OPTIONAL_CODEC instead of CODEC
+ valueOutput.store("Inventory", ItemStack.OPTIONAL_CODEC.listOf(), stacks);
+ }
+
+ /**
+ * Restores workbench-specific state from persistent storage, applying defaults when keys are absent.
+ *
+ * Delegates to the superclass load logic, then:
+ * - reads "WorkbenchTier" (int) into {@code tier}, defaulting to {@code 1} if missing;
+ * - reads "ScanRadius" (double) into {@code scanRadius}, defaulting to {@code 0.0} if missing;
+ * - reads "Inventory" as a list of {@code ItemStack} and populates the internal inventory up to its capacity.
+ */
+ @Override
+ protected void loadAdditional(ValueInput valueInput) {
+ super.loadAdditional(valueInput);
+ // read() returns an Optional
+ this.tier = valueInput.read("WorkbenchTier", Codec.INT).orElse(1);
+ this.scanRadius = valueInput.read("ScanRadius", Codec.DOUBLE).orElse(0.0);
+
+ // Read the inventory list back
+ valueInput.read("Inventory", ItemStack.OPTIONAL_CODEC.listOf()).ifPresent(stacks -> {
+ for (int i = 0; i < stacks.size() && i < inventory.getContainerSize(); i++) {
+ inventory.setItem(i, stacks.get(i));
+ }
+ });
+ }
+
+ /**
+ * Creates the server-side container menu for this workbench's UI.
+ *
+ * @param syncId the window id used to synchronize the menu with the client
+ * @param playerInventory the opening player's inventory
+ * @param player the player who opened the menu
+ * @return a FarmersWorkbenchMenu bound to this workbench's inventory and synced data
+ */
+ @Override
+ public @Nullable AbstractContainerMenu createMenu(int syncId, Inventory playerInventory, Player player) {
+ // 1. Trigger the sync to the client before returning the menu
+ if (player instanceof ServerPlayer serverPlayer) {
+ this.syncNearbyToPlayer(serverPlayer);
+ }
+
+ // // 2. Return the menu as usual
+ return new FarmersWorkbenchMenu(syncId, playerInventory, this.data, this);
+ }
+
+ /**
+ * Identifies the recipe type used to find and match recipes for this workbench.
+ *
+ * @return the RecipeType for workbench recipes
+ */
+ @Override
+ public RecipeType getWorkbenchRecipeType() {
+ return ModRecipes.FARMERS_TYPE;
+ }
+
+ /**
+ * Determines whether the workbench currently has fuel available.
+ *
+ * Checks that the entity is in a loaded level and that the configured fuel slot contains an item.
+ *
+ * @return `true` if the entity is in a loaded level and the fuel slot contains an item, `false` otherwise.
+ */
+ @Override
+ protected boolean hasFuel() {
+ if (this.level == null) return false;
+
+ // Check if block is lit
+ // BlockState state = this.level.getBlockState(this.worldPosition);
+ // boolean isLit = state.hasProperty(BlockStateProperties.LIT) && state.getValue(BlockStateProperties.LIT);
+
+ boolean hasFuelItem = !this.getItem(Constants.FUEL_SLOT).isEmpty();
+
+ return hasFuelItem;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/menu/AbstractWorkbenchContainerMenu.java b/src/main/java/com/tcm/MineTale/block/workbenches/menu/AbstractWorkbenchContainerMenu.java
index d5a5007..aeccbe9 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/menu/AbstractWorkbenchContainerMenu.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/menu/AbstractWorkbenchContainerMenu.java
@@ -27,6 +27,7 @@
import net.minecraft.world.item.Items;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeHolder;
+import net.minecraft.world.item.crafting.RecipeType;
public abstract class AbstractWorkbenchContainerMenu extends RecipeBookMenu implements StackedContentsCompatible {
protected final Container container;
@@ -37,6 +38,9 @@ public abstract class AbstractWorkbenchContainerMenu extends RecipeBookMenu impl
protected final Inventory playerInventory;
+ @Nullable
+ private final RecipeType recipeType;
+
private List networkedNearbyItems = new ArrayList<>();
/**
@@ -54,6 +58,10 @@ public List getNetworkedNearbyItems() {
return this.networkedNearbyItems;
}
+ public RecipeType getRecipeType() {
+ return this.recipeType;
+ }
+
/**
* Constructs a workbench container menu, initializes inventory and sync state, and opens the container for the player.
*
@@ -72,9 +80,10 @@ public List getNetworkedNearbyItems() {
* @param outputEnd index (inclusive) of the last output slot in the container; if negative or container is empty,
* the menu is treated as slotless and slot/data initialization is skipped
*/
- public AbstractWorkbenchContainerMenu(@Nullable MenuType> menuType, int syncId, Container container, ContainerData data, int containerDataSize, Inventory playerInventory, int inputEnd, int outputEnd) {
+ public AbstractWorkbenchContainerMenu(@Nullable MenuType> menuType, int syncId, Container container, ContainerData data, int containerDataSize, Inventory playerInventory, int inputEnd, int outputEnd, RecipeType recipeType) {
super(menuType, syncId);
+ this.recipeType = recipeType;
this.outputEnd = outputEnd;
this.inputEnd = inputEnd;
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/menu/ArmorersWorkbenchMenu.java b/src/main/java/com/tcm/MineTale/block/workbenches/menu/ArmorersWorkbenchMenu.java
index 313423a..732c963 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/menu/ArmorersWorkbenchMenu.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/menu/ArmorersWorkbenchMenu.java
@@ -7,6 +7,7 @@
import com.tcm.MineTale.block.workbenches.entity.AbstractWorkbenchEntity;
import com.tcm.MineTale.recipe.WorkbenchRecipeInput;
import com.tcm.MineTale.registry.ModMenuTypes;
+import com.tcm.MineTale.registry.ModRecipes;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.player.Inventory;
@@ -56,7 +57,8 @@ public ArmorersWorkbenchMenu(int syncId, Inventory playerInventory, ContainerDat
DATA_SIZE,
playerInventory,
EMPTY_SIZE,
- EMPTY_SIZE
+ EMPTY_SIZE,
+ ModRecipes.ARMORERS_TYPE
);
this.blockEntity = blockEntity;
this.playerInventory = playerInventory;
@@ -72,28 +74,6 @@ public ArmorersWorkbenchMenu(int syncId, Inventory playerInventory, ContainerDat
return this.blockEntity;
}
- /**
- * Populate the given StackedItemContents with the items available through this menu for recipe-book calculations.
- *
- * @param stackedItemContents container to receive consolidated item counts from the menu's inventories
- */
- // @Override
- // public void fillCraftSlotsStackedContents(StackedItemContents stackedItemContents) {
- // // 1. Tell the book the player has items in their pockets
- // this.playerInventory.fillStackedContents(stackedItemContents);
-
- // // 2. Tell the book the "Nearby Chests" items also count
- // AbstractWorkbenchEntity be = this.getBlockEntity();
- // if (be != null && be.isCanPullFromNearby()) {
- // // This runs on the CLIENT UI, making the icons turn WHITE
- // for (Container nearby : be.getNearbyInventories()) {
- // for (int i = 0; i < nearby.getContainerSize(); i++) {
- // stackedItemContents.accountStack(nearby.getItem(i));
- // }
- // }
- // }
- // }
-
@Override
public void fillCraftSlotsStackedContents(StackedItemContents contents) {
// 1. Account for items in the player's pockets
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/menu/CampfireWorkbenchMenu.java b/src/main/java/com/tcm/MineTale/block/workbenches/menu/CampfireWorkbenchMenu.java
index 0245b26..134a03b 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/menu/CampfireWorkbenchMenu.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/menu/CampfireWorkbenchMenu.java
@@ -5,6 +5,7 @@
import com.tcm.MineTale.block.workbenches.entity.AbstractWorkbenchEntity;
import com.tcm.MineTale.recipe.WorkbenchRecipeInput;
import com.tcm.MineTale.registry.ModMenuTypes;
+import com.tcm.MineTale.registry.ModRecipes;
import com.tcm.MineTale.util.Constants;
import net.minecraft.world.Container;
@@ -44,7 +45,7 @@ public CampfireWorkbenchMenu(int syncId, Inventory playerInventory) {
* @param data the container data used for syncing additional numeric state
*/
public CampfireWorkbenchMenu(int syncId, Inventory playerInventory, Container container, ContainerData data, @Nullable AbstractWorkbenchEntity blockEntity) {
- super(ModMenuTypes.CAMPFIRE_WORKBENCH_MENU, syncId, container, data, containerDataSize, playerInventory, Constants.INPUT_START + 1, 6);
+ super(ModMenuTypes.CAMPFIRE_WORKBENCH_MENU, syncId, container, data, containerDataSize, playerInventory, Constants.INPUT_START + 1, 6, ModRecipes.CAMPFIRE_TYPE);
this.blockEntity = blockEntity;
}
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/menu/FarmersWorkbenchMenu.java b/src/main/java/com/tcm/MineTale/block/workbenches/menu/FarmersWorkbenchMenu.java
new file mode 100644
index 0000000..79b791c
--- /dev/null
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/menu/FarmersWorkbenchMenu.java
@@ -0,0 +1,122 @@
+package com.tcm.MineTale.block.workbenches.menu;
+
+import java.util.List;
+
+import org.jspecify.annotations.Nullable;
+
+import com.tcm.MineTale.block.workbenches.entity.AbstractWorkbenchEntity;
+import com.tcm.MineTale.recipe.WorkbenchRecipeInput;
+import com.tcm.MineTale.registry.ModMenuTypes;
+import com.tcm.MineTale.registry.ModRecipes;
+
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Inventory;
+import net.minecraft.world.entity.player.StackedItemContents;
+import net.minecraft.world.inventory.ContainerData;
+import net.minecraft.world.inventory.RecipeBookType;
+import net.minecraft.world.inventory.SimpleContainerData;
+import net.minecraft.world.item.ItemStack;
+
+public class FarmersWorkbenchMenu extends AbstractWorkbenchContainerMenu {
+ // No internal inventory needed anymore, but we pass an empty container to the super
+ private static final int EMPTY_SIZE = 0;
+ private static final int DATA_SIZE = 0;
+
+ @Nullable
+ private final AbstractWorkbenchEntity blockEntity;
+ private final Inventory playerInventory;
+
+ /**
+ * Creates a client-side menu instance when the workbench UI is opened.
+ *
+ * @param syncId the synchronization id used to match this menu with the server
+ * @param playerInventory the player's inventory bound to this menu
+ */
+ public FarmersWorkbenchMenu(int syncId, Inventory playerInventory) {
+ this(syncId, playerInventory, new SimpleContainerData(EMPTY_SIZE), null);
+ }
+
+ /**
+ * Creates a workbench menu associated with the given player inventory and optional block entity.
+ *
+ * Uses an empty internal container (size 0) and the class's data size for syncing numeric state.
+ *
+ * @param syncId synchronization id for this menu
+ * @param playerInventory the player's inventory used for slot access and recipe-book integration
+ * @param data container data used to sync numeric state between server and client
+ * @param blockEntity nullable block entity this menu is bound to, or {@code null} if not bound
+ */
+ public FarmersWorkbenchMenu(int syncId, Inventory playerInventory, ContainerData data, @Nullable AbstractWorkbenchEntity blockEntity) {
+ // Note: The order of arguments depends on your AbstractWorkbenchContainerMenu,
+ // but the 'expectedSize' parameter MUST be 0.
+ super(
+ ModMenuTypes.FARMERS_WORKBENCH_MENU,
+ syncId,
+ new SimpleContainer(EMPTY_SIZE),
+ data,
+ DATA_SIZE,
+ playerInventory,
+ EMPTY_SIZE,
+ EMPTY_SIZE,
+ ModRecipes.FARMERS_TYPE
+ );
+ this.blockEntity = blockEntity;
+ this.playerInventory = playerInventory;
+ }
+
+ /**
+ * Accesses the block entity bound to this menu, if present.
+ *
+ * @return the bound AbstractWorkbenchEntity, or {@code null} if this menu is not bound to a block entity
+ */
+ @Override
+ public @Nullable AbstractWorkbenchEntity getBlockEntity() {
+ return this.blockEntity;
+ }
+
+ @Override
+ public void fillCraftSlotsStackedContents(StackedItemContents contents) {
+ // 1. Account for items in the player's pockets
+ this.playerInventory.fillStackedContents(contents);
+
+ // 2. Account for items sitting in the Workbench slots (if any)
+ for (int i = 0; i < this.container.getContainerSize(); i++) {
+ contents.accountStack(this.container.getItem(i));
+ }
+
+ // 3. THE FIX: Use the list provided by the Packet (Networked Items)
+ // We stop calling be.getNearbyInventories() here because it returns empty on Client
+ List nearbyItems = this.getNetworkedNearbyItems();
+
+ if (!nearbyItems.isEmpty() && this.playerInventory.player.level().isClientSide()) {
+ System.out.println("DEBUG: Recipe Book is now accounting for " + nearbyItems.size() + " stacks from the packet!");
+ }
+
+ for (ItemStack stack : nearbyItems) {
+ contents.accountStack(stack);
+ }
+ }
+
+ /**
+ * Selects the crafting recipe-book category for this menu.
+ *
+ * @return {@code RecipeBookType.CRAFTING}
+ */
+ @Override
+ public RecipeBookType getRecipeBookType() {
+ // This keeps the Crafting-style recipe book available on the UI
+ return RecipeBookType.CRAFTING;
+ }
+
+ /**
+ * Create the recipe input used by this menu's crafting UI; this implementation provides an empty input.
+ *
+ * @return a WorkbenchRecipeInput with both input stacks set to ItemStack.EMPTY
+ */
+ @Override
+ public WorkbenchRecipeInput createRecipeInput() {
+ // Since there are no slots, we return an empty input.
+ // The actual crafting logic will scan the player inventory directly when a button is clicked.
+ return new WorkbenchRecipeInput(ItemStack.EMPTY, ItemStack.EMPTY);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/menu/FurnaceWorkbenchMenu.java b/src/main/java/com/tcm/MineTale/block/workbenches/menu/FurnaceWorkbenchMenu.java
index b12b33f..b760ecd 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/menu/FurnaceWorkbenchMenu.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/menu/FurnaceWorkbenchMenu.java
@@ -5,6 +5,7 @@
import com.tcm.MineTale.block.workbenches.entity.AbstractFurnaceWorkbenchEntity;
import com.tcm.MineTale.recipe.WorkbenchRecipeInput;
import com.tcm.MineTale.registry.ModMenuTypes;
+import com.tcm.MineTale.registry.ModRecipes;
import com.tcm.MineTale.util.Constants;
import net.minecraft.world.Container;
@@ -33,7 +34,7 @@ public FurnaceWorkbenchMenu(int syncId, Inventory playerInventory) {
}
public FurnaceWorkbenchMenu(int syncId, Inventory playerInventory, Container container, ContainerData data, @Nullable AbstractFurnaceWorkbenchEntity blockEntity) {
- super(ModMenuTypes.FURNACE_WORKBENCH_MENU, syncId, container, data, containerDataSize, playerInventory, Constants.INPUT_START + 1, 6);
+ super(ModMenuTypes.FURNACE_WORKBENCH_MENU, syncId, container, data, containerDataSize, playerInventory, Constants.INPUT_START + 1, 6, ModRecipes.FURNACE_T1_TYPE);
this.blockEntity = blockEntity;
}
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/menu/WorkbenchWorkbenchMenu.java b/src/main/java/com/tcm/MineTale/block/workbenches/menu/WorkbenchWorkbenchMenu.java
index 7b8fe06..981f8dc 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/menu/WorkbenchWorkbenchMenu.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/menu/WorkbenchWorkbenchMenu.java
@@ -5,8 +5,10 @@
import org.jspecify.annotations.Nullable;
import com.tcm.MineTale.block.workbenches.entity.AbstractWorkbenchEntity;
+import com.tcm.MineTale.recipe.WorkbenchRecipe;
import com.tcm.MineTale.recipe.WorkbenchRecipeInput;
import com.tcm.MineTale.registry.ModMenuTypes;
+import com.tcm.MineTale.registry.ModRecipes;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.player.Inventory;
@@ -15,6 +17,7 @@
import net.minecraft.world.inventory.RecipeBookType;
import net.minecraft.world.inventory.SimpleContainerData;
import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.crafting.RecipeType;
public class WorkbenchWorkbenchMenu extends AbstractWorkbenchContainerMenu {
// No internal inventory needed anymore, but we pass an empty container to the super
@@ -45,8 +48,7 @@ public WorkbenchWorkbenchMenu(int syncId, Inventory playerInventory) {
* @param data container data used to sync numeric state between server and client
* @param blockEntity nullable block entity this menu is bound to, or {@code null} if not bound
*/
- public WorkbenchWorkbenchMenu(int syncId, Inventory playerInventory, ContainerData data, @Nullable AbstractWorkbenchEntity blockEntity) {
- // Note: The order of arguments depends on your AbstractWorkbenchContainerMenu,
+ public WorkbenchWorkbenchMenu(int syncId, Inventory playerInventory, ContainerData data, @Nullable AbstractWorkbenchEntity blockEntity) { // Note: The order of arguments depends on your AbstractWorkbenchContainerMenu,
// but the 'expectedSize' parameter MUST be 0.
super(
ModMenuTypes.WORKBENCH_WORKBENCH_MENU,
@@ -56,7 +58,8 @@ public WorkbenchWorkbenchMenu(int syncId, Inventory playerInventory, ContainerDa
DATA_SIZE,
playerInventory,
EMPTY_SIZE,
- EMPTY_SIZE
+ EMPTY_SIZE,
+ ModRecipes.WORKBENCH_TYPE
);
this.blockEntity = blockEntity;
this.playerInventory = playerInventory;
@@ -72,28 +75,6 @@ public WorkbenchWorkbenchMenu(int syncId, Inventory playerInventory, ContainerDa
return this.blockEntity;
}
- /**
- * Populate the given StackedItemContents with the items available through this menu for recipe-book calculations.
- *
- * @param stackedItemContents container to receive consolidated item counts from the menu's inventories
- */
- // @Override
- // public void fillCraftSlotsStackedContents(StackedItemContents stackedItemContents) {
- // // 1. Tell the book the player has items in their pockets
- // this.playerInventory.fillStackedContents(stackedItemContents);
-
- // // 2. Tell the book the "Nearby Chests" items also count
- // AbstractWorkbenchEntity be = this.getBlockEntity();
- // if (be != null && be.isCanPullFromNearby()) {
- // // This runs on the CLIENT UI, making the icons turn WHITE
- // for (Container nearby : be.getNearbyInventories()) {
- // for (int i = 0; i < nearby.getContainerSize(); i++) {
- // stackedItemContents.accountStack(nearby.getItem(i));
- // }
- // }
- // }
- // }
-
@Override
public void fillCraftSlotsStackedContents(StackedItemContents contents) {
// 1. Account for items in the player's pockets
diff --git a/src/main/java/com/tcm/MineTale/registry/ModBlockEntities.java b/src/main/java/com/tcm/MineTale/registry/ModBlockEntities.java
index 3726b3c..1f69384 100644
--- a/src/main/java/com/tcm/MineTale/registry/ModBlockEntities.java
+++ b/src/main/java/com/tcm/MineTale/registry/ModBlockEntities.java
@@ -5,6 +5,7 @@
import com.tcm.MineTale.block.workbenches.entity.CampfireWorkbenchEntity;
import com.tcm.MineTale.block.workbenches.entity.FurnaceWorkbenchEntity;
import com.tcm.MineTale.block.workbenches.entity.WorkbenchWorkbenchEntity;
+import com.tcm.MineTale.block.workbenches.entity.FarmersWorkbenchEntity;
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
import net.minecraft.core.Registry;
@@ -32,6 +33,12 @@ public class ModBlockEntities {
ArmorersWorkbenchEntity::new,
ModBlocks.ARMORERS_WORKBENCH_BLOCK
);
+
+ public static final BlockEntityType FARMERS_WORKBENCH_BE = register(
+ "farmers_workbench_be",
+ FarmersWorkbenchEntity::new,
+ ModBlocks.FARMERS_WORKBENCH_BLOCK
+ );
/**
* Register a BlockEntityType for the given furnace tier and store it in the tier map.
diff --git a/src/main/java/com/tcm/MineTale/registry/ModBlocks.java b/src/main/java/com/tcm/MineTale/registry/ModBlocks.java
index f2096e6..3516eff 100644
--- a/src/main/java/com/tcm/MineTale/registry/ModBlocks.java
+++ b/src/main/java/com/tcm/MineTale/registry/ModBlocks.java
@@ -7,6 +7,7 @@
import com.tcm.MineTale.MineTale;
import com.tcm.MineTale.block.workbenches.ArmorersWorkbench;
import com.tcm.MineTale.block.workbenches.CampfireWorkbench;
+import com.tcm.MineTale.block.workbenches.FarmersWorkbench;
import com.tcm.MineTale.block.workbenches.FurnaceWorkbench;
import com.tcm.MineTale.block.workbenches.WorkbenchWorkbench;
import com.tcm.MineTale.item.ModCreativeTab;
@@ -75,6 +76,13 @@ public class ModBlocks {
true
);
+ public static final Block FARMERS_WORKBENCH_BLOCK = register(
+ "farmers_workbench",
+ FarmersWorkbench::new,
+ BlockBehaviour.Properties.of().sound(SoundType.WOOD),
+ true
+ );
+
//Logs
public static final Block AMBER_LOG = register("amber_log", RotatedPillarBlock::new, logProperties(MapColor.COLOR_ORANGE, MapColor.PODZOL, SoundType.WOOD), true);
public static final Block BAMBOO_LOG = register("bamboo_log", RotatedPillarBlock::new, logProperties(MapColor.COLOR_GREEN, MapColor.GRASS, SoundType.WOOD), true);
@@ -101,6 +109,71 @@ public class ModBlocks {
public static final Block WILD_WISTERIA_LOG = register("wild_wisteria_log", RotatedPillarBlock::new, logProperties(MapColor.SAND, MapColor.DIRT, SoundType.WOOD), true);
public static final Block WILD_WISTERIA_WOOD = register("wild_wisteria_wood", RotatedPillarBlock::new, BlockBehaviour.Properties.of().mapColor(MapColor.DIRT).instrument(NoteBlockInstrument.BASS).strength(2.0F).sound(SoundType.WOOD).ignitedByLava(), true);
+ // Base Moss (The raw materials used in recipes)
+ public static final Block MOSS = register("moss", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MOSS), true);
+ public static final Block BLUE_MOSS = register("blue_moss", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MOSS), true);
+ public static final Block RED_MOSS = register("red_moss", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MOSS), true);
+ public static final Block YELLOW_MOSS = register("yellow_moss", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MOSS), true);
+ public static final Block DARK_GREEN_MOSS = register("dark_green_moss", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MOSS), true);
+
+ // World Blocks (Natural Soils and Terrains)
+ public static final Block DRY_MUD = register("dry_mud", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MUD), true);
+ public static final Block LEAFY_SOIL = register("leafy_soil", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+ public static final Block SOIL_PATHWAY = register("soil_pathway", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+ public static final Block NEEDLED_SOIL = register("needled_soil", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+ public static final Block FULL_GRASS = register("full_grass", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+ public static final Block COLD_DIRT = register("cold_dirt", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+ public static final Block SUMMER_GRASS = register("summer_grass", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+ public static final Block WET_GRASS = register("wet_grass", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+ public static final Block DRY_GRASS = register("dry_grass", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+ public static final Block DRY_DIRT = register("dry_dirt", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+ public static final Block DEEP_GRASS = register("deep_grass", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+ public static final Block POISONED_DIRT = register("poisoned_dirt", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+
+ // Moss Blocks
+ public static final Block BLUE_MOSS_BLOCK = register("blue_moss_block", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MOSS), true);
+ public static final Block DARK_GREEN_MOSS_BLOCK = register("dark_green_moss_block", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MOSS), true);
+ public static final Block RED_MOSS_BLOCK = register("red_moss_block", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MOSS), true);
+ public static final Block YELLOW_MOSS_BLOCK = register("yellow_moss_block", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MOSS), true);
+ public static final Block GREEN_MOSS_BLOCK = register("green_moss_block", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MOSS), true);
+
+ // Moss Decoration (Rugs & Hanging)
+ public static final Block GREEN_MOSS_RUG = register("green_moss_rug", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MOSS), true);
+ public static final Block BLUE_MOSS_RUG = register("blue_moss_rug", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MOSS), true);
+ public static final Block DARK_GREEN_MOSS_RUG = register("dark_green_moss_rug", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MOSS), true);
+ public static final Block YELLOW_MOSS_RUG = register("yellow_moss_rug", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MOSS), true);
+ public static final Block RED_MOSS_RUG = register("red_moss_rug", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MOSS), true);
+ public static final Block SORREL_RUG = register("sorrel_rug", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MOSS), true);
+ public static final Block VINE_RUG = register("vine_rug", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+
+ public static final Block RED_HANGING_MOSS = register("red_hanging_moss", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MOSS), true);
+ public static final Block DARK_GREEN_HANGING_MOSS = register("dark_green_hanging_moss", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MOSS), true);
+ public static final Block BLUE_HANGING_MOSS = register("blue_hanging_moss", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MOSS), true);
+ public static final Block YELLOW_HANGING_MOSS = register("yellow_hanging_moss", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MOSS), true);
+ public static final Block GREEN_HANGING_MOSS = register("green_hanging_moss", Block::new, BlockBehaviour.Properties.of().sound(SoundType.MOSS), true);
+
+ // Short Moss & Plants
+ public static final Block SHORT_RED_MOSS = register("short_red_moss", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+ public static final Block SHORT_YELLOW_MOSS = register("short_yellow_moss", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+ public static final Block SHORT_BLUE_MOSS = register("short_blue_moss", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+ public static final Block SHORT_DARK_GREEN_MOSS = register("short_dark_green_moss", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+ public static final Block SHORT_MOSS = register("short_moss", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+
+ // Vines & Ivy
+ public static final Block LIANA = register("liana", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+ public static final Block VINE = register("vine", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+ public static final Block DRY_VINE = register("dry_vine", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+ public static final Block POISONED_IVY = register("poisoned_ivy", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+ public static final Block WALL_IVY = register("wall_ivy", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+ public static final Block IVY = register("ivy", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+
+ // Specialized Grasses
+ public static final Block COLD_GRASS = register("cold_grass", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+ public static final Block BURNT_GRASS = register("burnt_grass", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+
+ // Functional / Crafted
+ public static final Block BAMBOO_PLANTER = register("bamboo_planter", Block::new, BlockBehaviour.Properties.of().sound(SoundType.WOOD), true);
+
// Ores
/// 1. COPPER
diff --git a/src/main/java/com/tcm/MineTale/registry/ModItems.java b/src/main/java/com/tcm/MineTale/registry/ModItems.java
index 47aef66..fbbb289 100644
--- a/src/main/java/com/tcm/MineTale/registry/ModItems.java
+++ b/src/main/java/com/tcm/MineTale/registry/ModItems.java
@@ -33,7 +33,6 @@ public static void initialize() {
public static final Item SAP_GLOB = register("sap_glob", Item::new, new Item.Properties());
public static final Item RUBBLE = register("rubble", Item::new, new Item.Properties());
public static final Item TREE_BARK = register("tree_bark", Item::new, new Item.Properties());
- public static final Item MOSS = register("moss", Item::new, new Item.Properties());
public static final Item BLUE_CRYSTAL_SHARDS = register("blue_crystal_shards", Item::new, new Item.Properties());
public static final Item GREEN_CRYSTAL_SHARDS = register("green_crystal_shards", Item::new, new Item.Properties());
public static final Item YELLOW_CRYSTAL_SHARDS = register("yellow_crystal_shards", Item::new, new Item.Properties());
diff --git a/src/main/java/com/tcm/MineTale/registry/ModMenuTypes.java b/src/main/java/com/tcm/MineTale/registry/ModMenuTypes.java
index ea6f050..ced67ab 100644
--- a/src/main/java/com/tcm/MineTale/registry/ModMenuTypes.java
+++ b/src/main/java/com/tcm/MineTale/registry/ModMenuTypes.java
@@ -3,6 +3,7 @@
import com.tcm.MineTale.MineTale;
import com.tcm.MineTale.block.workbenches.menu.FurnaceWorkbenchMenu;
import com.tcm.MineTale.block.workbenches.menu.WorkbenchWorkbenchMenu;
+import com.tcm.MineTale.block.workbenches.menu.FarmersWorkbenchMenu;
import com.tcm.MineTale.block.workbenches.menu.ArmorersWorkbenchMenu;
import com.tcm.MineTale.block.workbenches.menu.CampfireWorkbenchMenu;
@@ -34,6 +35,11 @@ public class ModMenuTypes {
"armorers_workbench_menu",
ArmorersWorkbenchMenu::new
);
+
+ public static final MenuType FARMERS_WORKBENCH_MENU = register(
+ "farmers_workbench_menu",
+ FarmersWorkbenchMenu::new
+ );
/**
* Triggers static registration of the mod's menu types.
diff --git a/src/main/java/com/tcm/MineTale/registry/ModRecipeDisplay.java b/src/main/java/com/tcm/MineTale/registry/ModRecipeDisplay.java
index 175e0ba..f9a2986 100644
--- a/src/main/java/com/tcm/MineTale/registry/ModRecipeDisplay.java
+++ b/src/main/java/com/tcm/MineTale/registry/ModRecipeDisplay.java
@@ -30,11 +30,15 @@ public class ModRecipeDisplay {
public static final RecipeDisplay.Type ARMORERS_TYPE =
new RecipeDisplay.Type<>(WorkbenchRecipeDisplay.CODEC, STREAM_CODEC);
+ public static final RecipeDisplay.Type FARMERS_TYPE =
+ new RecipeDisplay.Type<>(WorkbenchRecipeDisplay.CODEC, STREAM_CODEC);
+
// 1. Declare the fields but don't assign them yet
public static final RecipeBookCategory CAMPFIRE_SEARCH = registerCategory("campfire_recipe_book_category");
public static final RecipeBookCategory WORKBENCH_SEARCH = registerCategory("workbench_recipe_book_category");
public static final RecipeBookCategory ARMORERS_SEARCH = registerCategory("armorers_recipe_book_category");
public static final RecipeBookCategory FURNACE_T1_SEARCH = registerCategory("furnace_t1_recipe_book_category");
+ public static final RecipeBookCategory FARMERS_SEARCH = registerCategory("farmers_recipe_book_category");
/**
* Registers the workbench recipe display type into the built-in recipe display registry.
diff --git a/src/main/java/com/tcm/MineTale/registry/ModRecipes.java b/src/main/java/com/tcm/MineTale/registry/ModRecipes.java
index 842ca60..2f8cdf1 100644
--- a/src/main/java/com/tcm/MineTale/registry/ModRecipes.java
+++ b/src/main/java/com/tcm/MineTale/registry/ModRecipes.java
@@ -17,6 +17,7 @@ public class ModRecipes {
public static final RecipeType WORKBENCH_TYPE = createType("workbench_recipe_type");
public static final RecipeType ARMORERS_TYPE = createType("armorers_recipe_type");
public static final RecipeType FURNACE_T1_TYPE = createType("furnace_t1_recipe_type");
+ public static final RecipeType FARMERS_TYPE = createType("farmers_recipe_type");
// 2. Define the Serializers (The "How")
// We pass the specific Type into the Serializer's constructor
@@ -29,9 +30,12 @@ public class ModRecipes {
public static final RecipeSerializer WORKBENCH_SERIALIZER =
new WorkbenchRecipe.Serializer(WORKBENCH_TYPE);
- public static final RecipeSerializer ARMORERS_SERIALIZER =
+ public static final RecipeSerializer ARMORERS_SERIALIZER =
new WorkbenchRecipe.Serializer(ARMORERS_TYPE);
+ public static final RecipeSerializer FARMERS_SERIALIZER =
+ new WorkbenchRecipe.Serializer(FARMERS_TYPE);
+
/**
* Registers the mod's recipe types and their serializers into the game's built-in registries.
*
@@ -48,6 +52,8 @@ public static void initialize() {
register(WORKBENCH_TYPE, WORKBENCH_SERIALIZER);
register(ARMORERS_TYPE, ARMORERS_SERIALIZER);
+
+ register(FARMERS_TYPE, FARMERS_SERIALIZER);
}
/**
From 98f12fa8620161568d5e3afbbafb2aae1d62bce5 Mon Sep 17 00:00:00 2001
From: "coderabbitai[bot]"
<136622811+coderabbitai[bot]@users.noreply.github.com>
Date: Thu, 26 Feb 2026 08:02:07 +0000
Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=93=9D=20Add=20docstrings=20to=20`fea?=
=?UTF-8?q?t/add-farmers-workbench`?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Docstrings generation was requested by @The-Code-Monkey.
The following files were modified:
* `src/client/java/com/tcm/MineTale/MineTaleClient.java`
* `src/client/java/com/tcm/MineTale/block/workbenches/screen/ArmorersWorkbenchScreen.java`
* `src/client/java/com/tcm/MineTale/block/workbenches/screen/FarmersWorkbenchScreen.java`
* `src/client/java/com/tcm/MineTale/block/workbenches/screen/WorkbenchWorkbenchScreen.java`
* `src/client/java/com/tcm/MineTale/datagen/recipes/FarmerRecipes.java`
* `src/client/java/com/tcm/MineTale/datagen/recipes/WorkbenchRecipes.java`
* `src/main/java/com/tcm/MineTale/MineTale.java`
* `src/main/java/com/tcm/MineTale/block/workbenches/FarmersWorkbench.java`
* `src/main/java/com/tcm/MineTale/block/workbenches/entity/FarmersWorkbenchEntity.java`
* `src/main/java/com/tcm/MineTale/block/workbenches/menu/AbstractWorkbenchContainerMenu.java`
* `src/main/java/com/tcm/MineTale/block/workbenches/menu/ArmorersWorkbenchMenu.java`
* `src/main/java/com/tcm/MineTale/block/workbenches/menu/CampfireWorkbenchMenu.java`
* `src/main/java/com/tcm/MineTale/block/workbenches/menu/FarmersWorkbenchMenu.java`
* `src/main/java/com/tcm/MineTale/block/workbenches/menu/FurnaceWorkbenchMenu.java`
* `src/main/java/com/tcm/MineTale/block/workbenches/menu/WorkbenchWorkbenchMenu.java`
* `src/main/java/com/tcm/MineTale/registry/ModRecipes.java`
---
.../java/com/tcm/MineTale/MineTaleClient.java | 13 ++--
.../screen/ArmorersWorkbenchScreen.java | 9 +++
.../screen/FarmersWorkbenchScreen.java | 62 ++++++++++++-------
.../screen/WorkbenchWorkbenchScreen.java | 15 +++--
.../datagen/recipes/FarmerRecipes.java | 11 ++++
.../datagen/recipes/WorkbenchRecipes.java | 11 ++++
src/main/java/com/tcm/MineTale/MineTale.java | 7 ++-
.../block/workbenches/FarmersWorkbench.java | 6 +-
.../entity/FarmersWorkbenchEntity.java | 30 ++++-----
.../menu/AbstractWorkbenchContainerMenu.java | 9 ++-
.../menu/ArmorersWorkbenchMenu.java | 8 +++
.../menu/CampfireWorkbenchMenu.java | 5 +-
.../menu/FarmersWorkbenchMenu.java | 9 ++-
.../menu/FurnaceWorkbenchMenu.java | 11 +++-
.../menu/WorkbenchWorkbenchMenu.java | 9 +++
.../com/tcm/MineTale/registry/ModRecipes.java | 6 +-
16 files changed, 162 insertions(+), 59 deletions(-)
diff --git a/src/client/java/com/tcm/MineTale/MineTaleClient.java b/src/client/java/com/tcm/MineTale/MineTaleClient.java
index 9ba1872..ad83d84 100644
--- a/src/client/java/com/tcm/MineTale/MineTaleClient.java
+++ b/src/client/java/com/tcm/MineTale/MineTaleClient.java
@@ -24,12 +24,15 @@
public class MineTaleClient implements ClientModInitializer {
/**
- * Register client-side screen factories for custom workbench menu types.
+ * Initializes client-side handlers for the MineTale mod.
*
- * Binds ModMenuTypes.FURNACE_WORKBENCH_MENU to FurnaceWorkbenchScreen,
- * ModMenuTypes.CAMPFIRE_WORKBENCH_MENU to CampfireWorkbenchScreen, and
- * ModMenuTypes.WORKBENCH_WORKBENCH_MENU to WorkbenchWorkbenchScreen so the client
- * can create the appropriate GUI when those menus open.
+ * Registers screen factories for custom workbench menu types, configures block render
+ * layers for furnace workbenches, and registers a global network receiver that applies
+ * nearby inventory item lists to an open workbench menu when available.
+ *
+ * The network receiver schedules a client task that retries up to 10 frames if the
+ * player's container menu is not yet an instance of the expected workbench menu and
+ * logs a failure message if synchronization could not be applied after retries.
*/
@Override
public void onInitializeClient() {
diff --git a/src/client/java/com/tcm/MineTale/block/workbenches/screen/ArmorersWorkbenchScreen.java b/src/client/java/com/tcm/MineTale/block/workbenches/screen/ArmorersWorkbenchScreen.java
index c57a965..cbc8b5d 100644
--- a/src/client/java/com/tcm/MineTale/block/workbenches/screen/ArmorersWorkbenchScreen.java
+++ b/src/client/java/com/tcm/MineTale/block/workbenches/screen/ArmorersWorkbenchScreen.java
@@ -118,6 +118,15 @@ protected void init() {
}).bounds(defaultLeft + 40, defaultTop + 22, 35, 20).build());
}
+ /**
+ * Sends a craft request for the recipe remembered by this screen's last known selection.
+ *
+ * Resolves the remembered recipe to its resulting item(s) and, if available, sends a network
+ * CraftRequestPayload containing the first result and the requested amount. If no remembered
+ * selection or no results are available, no payload is sent.
+ *
+ * @param amount the quantity to craft; use -1 to request crafting all available units
+ */
private void handleCraftRequest(int amount) {
// Look at our "Memory" instead of the component
if (this.lastKnownSelectedId != null) {
diff --git a/src/client/java/com/tcm/MineTale/block/workbenches/screen/FarmersWorkbenchScreen.java b/src/client/java/com/tcm/MineTale/block/workbenches/screen/FarmersWorkbenchScreen.java
index b34db37..bd56f55 100644
--- a/src/client/java/com/tcm/MineTale/block/workbenches/screen/FarmersWorkbenchScreen.java
+++ b/src/client/java/com/tcm/MineTale/block/workbenches/screen/FarmersWorkbenchScreen.java
@@ -50,7 +50,7 @@ public class FarmersWorkbenchScreen extends AbstractRecipeBookScreenThis method draws the background and the superclass UI, captures the current recipe selection (persisting it to
+ * {@code lastKnownSelectedId} when present), resolves the remembered selection against the client's known recipes
+ * (when a level and player are available), updates the activation state of the craft buttons for counts of 1, 2 and 10
+ * depending on whether the player has sufficient materials, and finally renders any tooltips.
+ */
@Override
public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
renderBackground(graphics, mouseX, mouseY, delta);
diff --git a/src/client/java/com/tcm/MineTale/datagen/recipes/FarmerRecipes.java b/src/client/java/com/tcm/MineTale/datagen/recipes/FarmerRecipes.java
index 8279ba7..22867c9 100644
--- a/src/client/java/com/tcm/MineTale/datagen/recipes/FarmerRecipes.java
+++ b/src/client/java/com/tcm/MineTale/datagen/recipes/FarmerRecipes.java
@@ -13,6 +13,17 @@
import net.minecraft.world.level.block.Blocks;
public class FarmerRecipes {
+ /**
+ * Registers Farmer workbench recipes and writes them to the provided exporter.
+ *
+ * Emits multiple WorkbenchRecipeBuilder entries (bamboo planter, moss variants, rugs,
+ * vines, soil/grass variants, and other farmer-related recipes) and configures their
+ * ingredient lists, times, book categories, and unlock conditions.
+ *
+ * @param provider source of recipe unlock predicates (used to build "has_farmers_workbench" and similar conditions)
+ * @param exporter destination that receives the generated recipe data
+ * @param lookup holder/tag resolver used when recipes specify ingredients by tag
+ */
public static void buildRecipes(RecipeProvider provider, RecipeOutput exporter, HolderLookup.Provider lookup) {
// new WorkbenchRecipeBuilder(ModRecipes.FARMERS_TYPE, ModRecipes.FARMERS_SERIALIZER)
// .input(ItemTags.PLANKS, lookup, 20)
diff --git a/src/client/java/com/tcm/MineTale/datagen/recipes/WorkbenchRecipes.java b/src/client/java/com/tcm/MineTale/datagen/recipes/WorkbenchRecipes.java
index c0ff6b3..f2f5010 100644
--- a/src/client/java/com/tcm/MineTale/datagen/recipes/WorkbenchRecipes.java
+++ b/src/client/java/com/tcm/MineTale/datagen/recipes/WorkbenchRecipes.java
@@ -13,6 +13,17 @@
import net.minecraft.world.item.Items;
public class WorkbenchRecipes {
+ /**
+ * Registers workbench crafting recipes and saves them to the provided exporter.
+ *
+ * Builds and configures multiple WorkbenchRecipeBuilder instances (inputs, outputs, craft time,
+ * unlock conditions, and book category) and persists each recipe using the exporter with a
+ * unique identifier.
+ *
+ * @param provider a RecipeProvider used to query existing items/blocks for unlock conditions and tags
+ * @param exporter the RecipeOutput that receives and writes the generated recipe data
+ * @param lookup a HolderLookup.Provider used to resolve tag holders (e.g., ItemTags) when specifying inputs
+ */
public static void buildRecipes(RecipeProvider provider, RecipeOutput exporter, HolderLookup.Provider lookup) {
new WorkbenchRecipeBuilder(ModRecipes.WORKBENCH_TYPE, ModRecipes.WORKBENCH_SERIALIZER)
.input(Items.COPPER_INGOT, 2)
diff --git a/src/main/java/com/tcm/MineTale/MineTale.java b/src/main/java/com/tcm/MineTale/MineTale.java
index fecd8f6..5f5099a 100644
--- a/src/main/java/com/tcm/MineTale/MineTale.java
+++ b/src/main/java/com/tcm/MineTale/MineTale.java
@@ -42,10 +42,11 @@ public class MineTale implements ModInitializer {
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
/**
- * Initializes and registers the mod's game content and subsystems during Fabric startup.
+ * Initialize and register the mod's game content, network payloads, and runtime subsystems on startup.
*
- * Triggers initialization for blocks, block entities, menu types, entities, items, and entity
- * data serializers so they are registered with the game before gameplay begins.
+ * Registers blocks, items, block entities, entities, menus, recipes, recipe displays, creative tab,
+ * entity data serializers, recipe serializer synchronization, and loot-table modifiers. Also registers
+ * client↔server payload codecs and a global server receiver that processes craft requests from workbench-like menus.
*/
@Override
public void onInitialize() {
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/FarmersWorkbench.java b/src/main/java/com/tcm/MineTale/block/workbenches/FarmersWorkbench.java
index b1b0c54..0612c10 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/FarmersWorkbench.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/FarmersWorkbench.java
@@ -30,7 +30,7 @@ public class FarmersWorkbench extends AbstractWorkbench
public static final MapCodec CODEC = simpleCodec(FarmersWorkbench::new);
/**
- * Constructs a FarmersWorkbench that uses the mod's FARMERS_WORKBENCH_BE block entity type.
+ * Creates a FarmersWorkbench preconfigured to use ModBlockEntities.FARMERS_WORKBENCH_BE as its block entity type.
*
* @param properties block properties for this workbench
*/
@@ -40,10 +40,10 @@ public FarmersWorkbench(Properties properties) {
}
/**
- * Constructs a FarmersWorkbench using the provided block properties and block-entity type supplier.
+ * Create a FarmersWorkbench with the given block properties and a supplier for its block-entity type.
*
* @param properties block properties to apply to this workbench
- * @param supplier supplier that provides the BlockEntityType for the FarmersWorkbenchEntity
+ * @param supplier supplier that provides the specific BlockEntityType to use for FarmersWorkbenchEntity
*/
public FarmersWorkbench(Properties properties, Supplier> supplier) {
super(properties, supplier, IS_WIDE, IS_TALL, 1);
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/entity/FarmersWorkbenchEntity.java b/src/main/java/com/tcm/MineTale/block/workbenches/entity/FarmersWorkbenchEntity.java
index 8b25b57..4993b42 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/entity/FarmersWorkbenchEntity.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/entity/FarmersWorkbenchEntity.java
@@ -27,10 +27,12 @@
public class FarmersWorkbenchEntity extends AbstractWorkbenchEntity {
protected final ContainerData data = new ContainerData() {
/**
- * Retrieves an internal data value by index for UI synchronization.
+ * Provide the container's UI-synchronized integer value for the specified data index.
*
- * @param index There is no cook time or anything for this block as it doesnt use it
- * @return the value associated with {@code index}, or 0 for any other index
+ * This workbench exposes four data slots (indices 0–3); all slots are currently unused and always return 0.
+ *
+ * @param index the data index to read (expected range: 0–3)
+ * @return 0 for the requested index
*/
@Override
public int get(int index) {
@@ -40,10 +42,10 @@ public int get(int index) {
}
/**
- * No-op for this workbench; data is server-driven and not set client-side.
+ * No-op setter: client-side attempts to change workbench UI data are ignored because the server is authoritative.
*
- * `@param` index the data index to set
- * `@param` value the value to assign (ignored)
+ * @param index the data index (ignored)
+ * @param value the value to assign (ignored)
*/
@Override
public void set(int index, int value) {
@@ -62,9 +64,9 @@ public int getCount() {
};
/**
- * Creates a FarmersWorkbenchEntity for the specified world position and block state.
+ * Create a FarmersWorkbenchEntity at the given world position and block state.
*
- * Sets the entity's scanRadius to 0.0 and tier to 1.
+ * Initializes the workbench tier to 1 and enables pulling from nearby inventories.
*
* @param blockPos the world position of this block entity
* @param blockState the block state for this block entity
@@ -77,12 +79,10 @@ public FarmersWorkbenchEntity(BlockPos blockPos, BlockState blockState) {
}
/**
- * Persist this workbench's state to the given ValueOutput.
- *
- * Stores "WorkbenchTier" (int), "ScanRadius" (double), and the full inventory under "Inventory"
- * using type-safe Codecs.
+ * Writes this workbench's persistent state to the provided output, including the workbench tier,
+ * scan radius, and full inventory (stored under the keys "WorkbenchTier", "ScanRadius", and "Inventory").
*
- * @param valueOutput the writer used to serialize this entity's fields
+ * @param valueOutput the output used to serialize this entity's fields
*/
@Override
protected void saveAdditional(ValueOutput valueOutput) {
@@ -126,12 +126,12 @@ protected void loadAdditional(ValueInput valueInput) {
}
/**
- * Creates the server-side container menu for this workbench's UI.
+ * Create the server-side menu for a player to interact with this workbench and synchronize nearby data to that player.
*
* @param syncId the window id used to synchronize the menu with the client
* @param playerInventory the opening player's inventory
* @param player the player who opened the menu
- * @return a FarmersWorkbenchMenu bound to this workbench's inventory and synced data
+ * @return the FarmersWorkbenchMenu bound to this workbench's inventory and synchronization data
*/
@Override
public @Nullable AbstractContainerMenu createMenu(int syncId, Inventory playerInventory, Player player) {
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/menu/AbstractWorkbenchContainerMenu.java b/src/main/java/com/tcm/MineTale/block/workbenches/menu/AbstractWorkbenchContainerMenu.java
index aeccbe9..8e64bee 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/menu/AbstractWorkbenchContainerMenu.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/menu/AbstractWorkbenchContainerMenu.java
@@ -52,12 +52,19 @@ public void setNetworkedNearbyItems(List items) {
}
/**
- * Gets the list of items found in nearby chests.
+ * Accesses the network-synchronized list of nearby item stacks used to simulate available ingredients.
+ *
+ * @return the live list of {@code ItemStack} instances representing nearby items synchronized from the server; entries may be empty
*/
public List getNetworkedNearbyItems() {
return this.networkedNearbyItems;
}
+ /**
+ * Provide the recipe system used by this workbench menu.
+ *
+ * @return the {@code RecipeType} used to match and retrieve recipes for this workbench
+ */
public RecipeType getRecipeType() {
return this.recipeType;
}
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/menu/ArmorersWorkbenchMenu.java b/src/main/java/com/tcm/MineTale/block/workbenches/menu/ArmorersWorkbenchMenu.java
index 732c963..8f5959f 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/menu/ArmorersWorkbenchMenu.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/menu/ArmorersWorkbenchMenu.java
@@ -74,6 +74,14 @@ public ArmorersWorkbenchMenu(int syncId, Inventory playerInventory, ContainerDat
return this.blockEntity;
}
+ /**
+ * Populates the given StackedItemContents with item stacks relevant to crafting lookups.
+ *
+ * Includes stacks from the player's inventory, this menu's internal container slots, and
+ * nearby item stacks supplied via networked data when available.
+ *
+ * @param contents the StackedItemContents to populate with accounted stacks
+ */
@Override
public void fillCraftSlotsStackedContents(StackedItemContents contents) {
// 1. Account for items in the player's pockets
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/menu/CampfireWorkbenchMenu.java b/src/main/java/com/tcm/MineTale/block/workbenches/menu/CampfireWorkbenchMenu.java
index 134a03b..96b5512 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/menu/CampfireWorkbenchMenu.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/menu/CampfireWorkbenchMenu.java
@@ -37,12 +37,13 @@ public CampfireWorkbenchMenu(int syncId, Inventory playerInventory) {
}
/**
- * Creates a CampfireWorkbenchMenu bound to the given player inventory, container, and container data.
+ * Constructs a CampfireWorkbenchMenu connected to the given player inventory, container, and container data.
*
- * @param syncId the synchronization id for this menu (used by the client/server container sync)
+ * @param syncId the synchronization id for this menu used for client/server container sync
* @param playerInventory the player's inventory
* @param container the backing container for the workbench slots
* @param data the container data used for syncing additional numeric state
+ * @param blockEntity the associated workbench block entity, or `null` if not bound to a block
*/
public CampfireWorkbenchMenu(int syncId, Inventory playerInventory, Container container, ContainerData data, @Nullable AbstractWorkbenchEntity blockEntity) {
super(ModMenuTypes.CAMPFIRE_WORKBENCH_MENU, syncId, container, data, containerDataSize, playerInventory, Constants.INPUT_START + 1, 6, ModRecipes.CAMPFIRE_TYPE);
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/menu/FarmersWorkbenchMenu.java b/src/main/java/com/tcm/MineTale/block/workbenches/menu/FarmersWorkbenchMenu.java
index 79b791c..154cbdc 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/menu/FarmersWorkbenchMenu.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/menu/FarmersWorkbenchMenu.java
@@ -74,6 +74,13 @@ public FarmersWorkbenchMenu(int syncId, Inventory playerInventory, ContainerData
return this.blockEntity;
}
+ /**
+ * Populates the provided StackedItemContents with item stacks available for recipe matching.
+ *
+ * This accounts for items from the player's inventory, any items stored in the menu's internal container slots, and nearby item stacks supplied via the menu's networked list (used by clients).
+ *
+ * @param contents accumulator that will receive accounted item stacks for recipe lookup
+ */
@Override
public void fillCraftSlotsStackedContents(StackedItemContents contents) {
// 1. Account for items in the player's pockets
@@ -109,7 +116,7 @@ public RecipeBookType getRecipeBookType() {
}
/**
- * Create the recipe input used by this menu's crafting UI; this implementation provides an empty input.
+ * Creates an empty WorkbenchRecipeInput for this menu's crafting UI.
*
* @return a WorkbenchRecipeInput with both input stacks set to ItemStack.EMPTY
*/
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/menu/FurnaceWorkbenchMenu.java b/src/main/java/com/tcm/MineTale/block/workbenches/menu/FurnaceWorkbenchMenu.java
index b760ecd..d2905dc 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/menu/FurnaceWorkbenchMenu.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/menu/FurnaceWorkbenchMenu.java
@@ -24,7 +24,7 @@ public class FurnaceWorkbenchMenu extends AbstractWorkbenchContainerMenu {
private final AbstractFurnaceWorkbenchEntity blockEntity;
/**
- * Creates a client-side FurnaceWorkbenchMenu with a new internal container and container data.
+ * Constructs a client-side FurnaceWorkbenchMenu backed by a new empty internal container and default container data.
*
* @param syncId the window synchronization id assigned by the client
* @param playerInventory the player's inventory to attach to this menu
@@ -33,6 +33,15 @@ public FurnaceWorkbenchMenu(int syncId, Inventory playerInventory) {
this(syncId, playerInventory, new SimpleContainer(7), new SimpleContainerData(containerDataSize), null);
}
+ /**
+ * Creates a FurnaceWorkbenchMenu bound to the provided player inventory, container, and optional block entity.
+ *
+ * @param syncId window sync id used by the client-server menu sync
+ * @param playerInventory the player's inventory
+ * @param container the backing container holding slot items for this menu
+ * @param data container data used for syncing menu state (e.g., progress fields)
+ * @param blockEntity the associated AbstractFurnaceWorkbenchEntity instance, or {@code null} for client-side construction
+ */
public FurnaceWorkbenchMenu(int syncId, Inventory playerInventory, Container container, ContainerData data, @Nullable AbstractFurnaceWorkbenchEntity blockEntity) {
super(ModMenuTypes.FURNACE_WORKBENCH_MENU, syncId, container, data, containerDataSize, playerInventory, Constants.INPUT_START + 1, 6, ModRecipes.FURNACE_T1_TYPE);
this.blockEntity = blockEntity;
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/menu/WorkbenchWorkbenchMenu.java b/src/main/java/com/tcm/MineTale/block/workbenches/menu/WorkbenchWorkbenchMenu.java
index 981f8dc..17ca965 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/menu/WorkbenchWorkbenchMenu.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/menu/WorkbenchWorkbenchMenu.java
@@ -75,6 +75,15 @@ public WorkbenchWorkbenchMenu(int syncId, Inventory playerInventory, ContainerDa
return this.blockEntity;
}
+ /**
+ * Populate the provided StackedItemContents with all item stacks available to the crafting UI.
+ *
+ * Accounts for items in the player's pockets, items stored in this menu's internal container,
+ * and nearby items delivered by the server (network-synced). When running on the client and
+ * networked nearby items are present, a debug message may be printed.
+ *
+ * @param contents the StackedItemContents to populate with available item stacks
+ */
@Override
public void fillCraftSlotsStackedContents(StackedItemContents contents) {
// 1. Account for items in the player's pockets
diff --git a/src/main/java/com/tcm/MineTale/registry/ModRecipes.java b/src/main/java/com/tcm/MineTale/registry/ModRecipes.java
index 2f8cdf1..b1d6fa5 100644
--- a/src/main/java/com/tcm/MineTale/registry/ModRecipes.java
+++ b/src/main/java/com/tcm/MineTale/registry/ModRecipes.java
@@ -37,10 +37,10 @@ public class ModRecipes {
new WorkbenchRecipe.Serializer(FARMERS_TYPE);
/**
- * Registers the mod's recipe types and their serializers into the game's built-in registries.
+ * Registers the mod's recipe types and their serializers into Minecraft's built-in registries under the mod namespace.
*
- * Specifically registers the furnace (FURNACE_T1_TYPE), campfire (CAMPFIRE_TYPE),
- * and workbench (WORKBENCH_TYPE) recipe types with their corresponding serializers.
+ * Registers the following recipe types and their corresponding serializers: FURNACE_T1_TYPE, CAMPFIRE_TYPE,
+ * WORKBENCH_TYPE, ARMORERS_TYPE, and FARMERS_TYPE.
*/
public static void initialize() {
// Register the Furnace-flavored version
From a595d5cec9d9d09594463d3a02f75fbacb1c9f56 Mon Sep 17 00:00:00 2001
From: Andy Wilson
Date: Thu, 26 Feb 2026 08:54:11 +0000
Subject: [PATCH 3/4] feat: builders workbench added and recipes added
---
.../java/com/tcm/MineTale/MineTaleClient.java | 2 +
.../screen/BuildersWorkbenchScreen.java | 300 ++++++++++++++++++
.../screen/FarmersWorkbenchScreen.java | 2 -
.../screen/WorkbenchWorkbenchScreen.java | 2 -
.../datagen/recipes/BuilderRecipes.java | 36 ++-
.../datagen/recipes/WorkbenchRecipes.java | 18 +-
src/main/java/com/tcm/MineTale/MineTale.java | 2 -
.../block/workbenches/BuildersWorkbench.java | 96 ++++++
.../entity/BuildersWorkbenchEntity.java | 176 ++++++++++
.../menu/BuildersWorkbenchMenu.java | 122 +++++++
.../menu/WorkbenchWorkbenchMenu.java | 2 -
.../MineTale/registry/ModBlockEntities.java | 7 +
.../com/tcm/MineTale/registry/ModBlocks.java | 12 +
.../tcm/MineTale/registry/ModMenuTypes.java | 6 +
.../MineTale/registry/ModRecipeDisplay.java | 4 +
.../com/tcm/MineTale/registry/ModRecipes.java | 6 +
16 files changed, 759 insertions(+), 34 deletions(-)
create mode 100644 src/client/java/com/tcm/MineTale/block/workbenches/screen/BuildersWorkbenchScreen.java
create mode 100644 src/main/java/com/tcm/MineTale/block/workbenches/BuildersWorkbench.java
create mode 100644 src/main/java/com/tcm/MineTale/block/workbenches/entity/BuildersWorkbenchEntity.java
create mode 100644 src/main/java/com/tcm/MineTale/block/workbenches/menu/BuildersWorkbenchMenu.java
diff --git a/src/client/java/com/tcm/MineTale/MineTaleClient.java b/src/client/java/com/tcm/MineTale/MineTaleClient.java
index 9ba1872..8bdfbf4 100644
--- a/src/client/java/com/tcm/MineTale/MineTaleClient.java
+++ b/src/client/java/com/tcm/MineTale/MineTaleClient.java
@@ -8,6 +8,7 @@
import com.tcm.MineTale.block.workbenches.menu.AbstractWorkbenchContainerMenu;
import com.tcm.MineTale.block.workbenches.screen.ArmorersWorkbenchScreen;
+import com.tcm.MineTale.block.workbenches.screen.BuildersWorkbenchScreen;
import com.tcm.MineTale.block.workbenches.screen.CampfireWorkbenchScreen;
import com.tcm.MineTale.block.workbenches.screen.FarmersWorkbenchScreen;
import com.tcm.MineTale.registry.ModBlocks;
@@ -38,6 +39,7 @@ public void onInitializeClient() {
MenuScreens.register(ModMenuTypes.WORKBENCH_WORKBENCH_MENU, WorkbenchWorkbenchScreen::new);
MenuScreens.register(ModMenuTypes.ARMORERS_WORKBENCH_MENU, ArmorersWorkbenchScreen::new);
MenuScreens.register(ModMenuTypes.FARMERS_WORKBENCH_MENU, FarmersWorkbenchScreen::new);
+ MenuScreens.register(ModMenuTypes.BUILDERS_WORKBENCH_MENU, BuildersWorkbenchScreen::new);
BlockRenderLayerMap.putBlock(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1, ChunkSectionLayer.CUTOUT);
BlockRenderLayerMap.putBlock(ModBlocks.FURNACE_WORKBENCH_BLOCK_T2, ChunkSectionLayer.CUTOUT);
diff --git a/src/client/java/com/tcm/MineTale/block/workbenches/screen/BuildersWorkbenchScreen.java b/src/client/java/com/tcm/MineTale/block/workbenches/screen/BuildersWorkbenchScreen.java
new file mode 100644
index 0000000..b7a0960
--- /dev/null
+++ b/src/client/java/com/tcm/MineTale/block/workbenches/screen/BuildersWorkbenchScreen.java
@@ -0,0 +1,300 @@
+package com.tcm.MineTale.block.workbenches.screen;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import com.tcm.MineTale.MineTale;
+import com.tcm.MineTale.block.workbenches.menu.AbstractWorkbenchContainerMenu;
+import com.tcm.MineTale.block.workbenches.menu.BuildersWorkbenchMenu;
+import com.tcm.MineTale.mixin.client.ClientRecipeBookAccessor;
+import com.tcm.MineTale.network.CraftRequestPayload;
+import com.tcm.MineTale.recipe.MineTaleRecipeBookComponent;
+import com.tcm.MineTale.registry.ModBlocks;
+import com.tcm.MineTale.registry.ModRecipeDisplay;
+import com.tcm.MineTale.registry.ModRecipes;
+
+import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
+import net.minecraft.client.ClientRecipeBook;
+import net.minecraft.client.gui.GuiGraphics;
+import net.minecraft.client.gui.components.Button;
+import net.minecraft.client.gui.navigation.ScreenPosition;
+import net.minecraft.client.gui.screens.inventory.AbstractRecipeBookScreen;
+import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent;
+import net.minecraft.client.renderer.RenderPipelines;
+import net.minecraft.core.Holder;
+import net.minecraft.resources.Identifier;
+import net.minecraft.world.entity.player.Inventory;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.item.Item;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.crafting.Ingredient;
+import net.minecraft.world.item.crafting.display.RecipeDisplayEntry;
+import net.minecraft.world.item.crafting.display.RecipeDisplayId;
+import net.minecraft.world.item.crafting.display.SlotDisplayContext;
+import net.minecraft.network.chat.Component;
+
+public class BuildersWorkbenchScreen extends AbstractRecipeBookScreen {
+ private static final Identifier TEXTURE =
+ Identifier.fromNamespaceAndPath(MineTale.MOD_ID, "textures/gui/container/workbench_workbench.png");
+
+ private final MineTaleRecipeBookComponent mineTaleRecipeBook;
+
+ private RecipeDisplayId lastKnownSelectedId = null;
+
+ private Button craftOneBtn;
+ private Button craftTenBtn;
+ private Button craftAllBtn;
+
+ /**
+ * Initialize a workbench GUI screen using the provided container menu, player inventory, and title.
+ *
+ * @param menu the menu supplying slots and synchronized state for this screen
+ * @param inventory the player's inventory to display and interact with
+ * @param title the title component shown at the top of the screen
+ */
+ public BuildersWorkbenchScreen(BuildersWorkbenchMenu menu, Inventory inventory, Component title) {
+ this(menu, inventory, title, createRecipeBookComponent(menu));
+ }
+
+ /**
+ * Creates a BuildersWorkbenchScreen bound to the given menu, player inventory, title, and recipe book component.
+ *
+ * @param menu the menu backing this screen
+ * @param inventory the player's inventory shown in the screen
+ * @param title the screen title component
+ * @param recipeBook the MineTaleRecipeBookComponent used to display and manage recipes in this screen
+ */
+ private BuildersWorkbenchScreen(BuildersWorkbenchMenu menu, Inventory inventory, Component title, MineTaleRecipeBookComponent recipeBook) {
+ super(menu, recipeBook, inventory, title);
+ this.mineTaleRecipeBook = recipeBook;
+ }
+
+ /**
+ * Create a MineTaleRecipeBookComponent configured for the workbench screen.
+ *
+ * @param menu the workbench menu used to initialize the recipe book component
+ * @return a MineTaleRecipeBookComponent containing the workbench tab and associated recipe category
+ */
+ private static MineTaleRecipeBookComponent createRecipeBookComponent(BuildersWorkbenchMenu menu) {
+ ItemStack tabIcon = new ItemStack(ModBlocks.BUILDERS_WORKBENCH_BLOCK.asItem());
+
+ List tabs = List.of(
+ new RecipeBookComponent.TabInfo(tabIcon.getItem(), ModRecipeDisplay.BUILDERS_SEARCH)
+ );
+
+ return new MineTaleRecipeBookComponent(menu, tabs, ModRecipes.BUILDERS_TYPE);
+ }
+
+ /**
+ * Configure the screen's GUI dimensions and initialize widgets.
+ *
+ * Sets the layout size (imageWidth = 176, imageHeight = 166), delegates remaining
+ * layout initialization to the superclass, and creates the three craft buttons
+ * ("1", "10", "All") wired to their respective handlers.
+ */
+ @Override
+ protected void init() {
+ // Important: Set your GUI size before super.init()
+ this.imageWidth = 176;
+ this.imageHeight = 166;
+
+ super.init();
+
+ int defaultLeft = this.leftPos + 90;
+ int defaultTop = this.topPos + 25;
+
+ this.craftOneBtn = addRenderableWidget(Button.builder(Component.literal("Craft"), (button) -> {
+ handleCraftRequest(1);
+ }).bounds(defaultLeft, defaultTop, 75, 20).build());
+
+ this.craftTenBtn = addRenderableWidget(Button.builder(Component.literal("x10"), (button) -> {
+ handleCraftRequest(10);
+ }).bounds(defaultLeft, defaultTop + 22, 35, 20).build());
+
+ this.craftAllBtn = addRenderableWidget(Button.builder(Component.literal("All"), (button) -> {
+ handleCraftRequest(-1); // -1 represents "All" logic
+ }).bounds(defaultLeft + 40, defaultTop + 22, 35, 20).build());
+ }
+
+ /**
+ * Sends a crafting request for the currently selected recipe in the integrated recipe book.
+ *
+ * Locates the last recipe collection and last selected recipe ID from the recipe book component,
+ * resolves the recipe's result item, and sends a CraftRequestPayload to the server containing that
+ * item and the requested amount.
+ *
+ * @param amount the quantity to craft; use -1 to request crafting of the full available stack ("All")
+ */
+ 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 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;
+ }
+ }
+ }
+ System.out.println("Request failed: No recipe was ever selected!");
+ }
+
+ /**
+ * Draws the workbench GUI background texture at the screen's top-left corner.
+ *
+ * @param guiGraphics the graphics context used to draw GUI elements
+ * @param f partial tick time for interpolation
+ * @param i current mouse x coordinate relative to the window
+ * @param j current mouse y coordinate relative to the window
+ */
+ protected void renderBg(GuiGraphics guiGraphics, float f, int i, int j) {
+ int k = this.leftPos;
+ int l = this.topPos;
+ guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, k, l, 0.0F, 0.0F, this.imageWidth, this.imageHeight, 256, 256);
+ }
+
+ @Override
+ public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
+ renderBackground(graphics, mouseX, mouseY, delta);
+ super.render(graphics, mouseX, mouseY, delta);
+
+ // 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;
+ }
+
+ // 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();
+ selectedEntry = ((ClientRecipeBookAccessor) book).getKnown().get(this.lastKnownSelectedId);
+ }
+
+ // 2. Button Activation Logic
+ if (selectedEntry != null) {
+ // We use the entry directly. It contains the 15 ingredients needed!
+ boolean canCraftOne = canCraft(this.minecraft.player, selectedEntry, 1);
+ boolean canCraftMoreThanOne = canCraft(this.minecraft.player, selectedEntry, 2);
+ boolean canCraftTen = canCraft(this.minecraft.player, selectedEntry, 10);
+
+ this.craftOneBtn.active = canCraftOne;
+ this.craftTenBtn.active = canCraftTen;
+ this.craftAllBtn.active = canCraftMoreThanOne;
+ } else {
+ this.craftOneBtn.active = false;
+ this.craftTenBtn.active = false;
+ this.craftAllBtn.active = false;
+ }
+
+ renderTooltip(graphics, mouseX, mouseY);
+ }
+
+ /**
+ * Determines whether the player has enough ingredients to craft the given recipe the specified number of times.
+ *
+ * @param player the player whose inventory (and networked nearby items) will be checked; may be null
+ * @param entry the recipe display entry providing crafting requirements; may be null
+ * @param craftCount the multiplier for required ingredient quantities (e.g., 1, 10, or -1 is not specially handled here)
+ * @return `true` if the player has at least the required quantity of each ingredient multiplied by `craftCount`, `false` otherwise (also returns `false` if `player` or `entry` is null or the recipe has no requirements)
+ */
+ private boolean canCraft(Player player, RecipeDisplayEntry entry, int craftCount) {
+ if (player == null || entry == null) return false;
+
+ Optional> reqs = entry.craftingRequirements();
+ if (reqs.isEmpty()) return false;
+
+ // 1. Group ingredients by their underlying Item Holders.
+ // Using List> as the key ensures structural equality (content-based hashing).
+ Map>, Integer> aggregatedRequirements = new HashMap<>();
+ Map>, Ingredient> holderToIngredient = new HashMap<>();
+
+ for (Ingredient ing : reqs.get()) {
+ // Collect holders into a List to get a stable hashCode() and equals()
+ @SuppressWarnings("deprecation")
+ List> key = ing.items().toList();
+
+ // Aggregate the counts (how many of this specific ingredient set are required)
+ aggregatedRequirements.put(key, aggregatedRequirements.getOrDefault(key, 0) + 1);
+
+ // Map the list back to the original ingredient for use in hasIngredientAmount
+ holderToIngredient.putIfAbsent(key, ing);
+ }
+
+ // 2. Check the player's inventory against the aggregated totals
+ Inventory inv = player.getInventory();
+ for (Map.Entry>, Integer> entryReq : aggregatedRequirements.entrySet()) {
+ List> key = entryReq.getKey();
+ int totalNeeded = entryReq.getValue() * craftCount;
+
+ // Retrieve the original Ingredient object associated with this list of holders
+ Ingredient originalIng = holderToIngredient.get(key);
+
+ if (!hasIngredientAmount(inv, originalIng, totalNeeded)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean hasIngredientAmount(Inventory inventory, Ingredient ingredient, int totalRequired) {
+ System.out.println("DEBUG: Searching inventory + nearby for " + totalRequired + "...");
+ if (totalRequired <= 0) return true;
+
+ int found = 0;
+
+ // 1. Check Player Inventory
+ for (int i = 0; i < inventory.getContainerSize(); i++) {
+ ItemStack stack = inventory.getItem(i);
+ if (!stack.isEmpty() && ingredient.test(stack)) {
+ found += stack.getCount();
+ }
+ }
+
+ // 2. CHECK THE NETWORKED ITEMS FROM CHESTS
+ // This is the list we sent via the packet!
+ if (this.menu instanceof AbstractWorkbenchContainerMenu workbenchMenu) {
+ for (ItemStack stack : workbenchMenu.getNetworkedNearbyItems()) {
+ if (!stack.isEmpty() && ingredient.test(stack)) {
+ found += stack.getCount();
+ System.out.println("DEBUG: Found " + stack.getCount() + " in nearby networked list. Total: " + found);
+ }
+ }
+ }
+
+ if (found >= totalRequired) {
+ System.out.println("DEBUG: Requirement MET with " + found + "/" + totalRequired);
+ return true;
+ }
+
+ System.out.println("DEBUG: FAILED. Only found: " + found + "/" + totalRequired);
+ return false;
+ }
+
+ /**
+ * Computes the on-screen position for the recipe book toggle button for this GUI.
+ *
+ * @return the screen position placed 5 pixels from the GUI's left edge and 49 pixels above the GUI's vertical center
+ */
+ @Override
+ protected ScreenPosition getRecipeBookButtonPosition() {
+ // 1. Calculate the start (left) of your workbench GUI
+ int guiLeft = (this.width - this.imageWidth) / 2;
+
+ // 2. Calculate the top of your workbench GUI
+ int guiTop = (this.height - this.imageHeight) / 2;
+
+ // 3. Standard Vanilla positioning:
+ // Usually 5 pixels in from the left and 49 pixels up from the center
+ return new ScreenPosition(guiLeft + 5, guiTop + this.imageHeight / 2 - 49);
+ }
+}
\ No newline at end of file
diff --git a/src/client/java/com/tcm/MineTale/block/workbenches/screen/FarmersWorkbenchScreen.java b/src/client/java/com/tcm/MineTale/block/workbenches/screen/FarmersWorkbenchScreen.java
index b34db37..7e4d612 100644
--- a/src/client/java/com/tcm/MineTale/block/workbenches/screen/FarmersWorkbenchScreen.java
+++ b/src/client/java/com/tcm/MineTale/block/workbenches/screen/FarmersWorkbenchScreen.java
@@ -9,7 +9,6 @@
import com.tcm.MineTale.block.workbenches.menu.AbstractWorkbenchContainerMenu;
import com.tcm.MineTale.block.workbenches.menu.FarmersWorkbenchMenu;
import com.tcm.MineTale.mixin.client.ClientRecipeBookAccessor;
-import com.tcm.MineTale.mixin.client.RecipeBookComponentAccessor;
import com.tcm.MineTale.network.CraftRequestPayload;
import com.tcm.MineTale.recipe.MineTaleRecipeBookComponent;
import com.tcm.MineTale.registry.ModBlocks;
@@ -23,7 +22,6 @@
import net.minecraft.client.gui.navigation.ScreenPosition;
import net.minecraft.client.gui.screens.inventory.AbstractRecipeBookScreen;
import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent;
-import net.minecraft.client.gui.screens.recipebook.RecipeCollection;
import net.minecraft.client.renderer.RenderPipelines;
import net.minecraft.core.Holder;
import net.minecraft.resources.Identifier;
diff --git a/src/client/java/com/tcm/MineTale/block/workbenches/screen/WorkbenchWorkbenchScreen.java b/src/client/java/com/tcm/MineTale/block/workbenches/screen/WorkbenchWorkbenchScreen.java
index 4bcf340..5eaa8a3 100644
--- a/src/client/java/com/tcm/MineTale/block/workbenches/screen/WorkbenchWorkbenchScreen.java
+++ b/src/client/java/com/tcm/MineTale/block/workbenches/screen/WorkbenchWorkbenchScreen.java
@@ -9,7 +9,6 @@
import com.tcm.MineTale.block.workbenches.menu.AbstractWorkbenchContainerMenu;
import com.tcm.MineTale.block.workbenches.menu.WorkbenchWorkbenchMenu;
import com.tcm.MineTale.mixin.client.ClientRecipeBookAccessor;
-import com.tcm.MineTale.mixin.client.RecipeBookComponentAccessor;
import com.tcm.MineTale.network.CraftRequestPayload;
import com.tcm.MineTale.recipe.MineTaleRecipeBookComponent;
import com.tcm.MineTale.registry.ModBlocks;
@@ -23,7 +22,6 @@
import net.minecraft.client.gui.navigation.ScreenPosition;
import net.minecraft.client.gui.screens.inventory.AbstractRecipeBookScreen;
import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent;
-import net.minecraft.client.gui.screens.recipebook.RecipeCollection;
import net.minecraft.client.renderer.RenderPipelines;
import net.minecraft.core.Holder;
import net.minecraft.resources.Identifier;
diff --git a/src/client/java/com/tcm/MineTale/datagen/recipes/BuilderRecipes.java b/src/client/java/com/tcm/MineTale/datagen/recipes/BuilderRecipes.java
index 2c2a960..b6815ef 100644
--- a/src/client/java/com/tcm/MineTale/datagen/recipes/BuilderRecipes.java
+++ b/src/client/java/com/tcm/MineTale/datagen/recipes/BuilderRecipes.java
@@ -1,5 +1,11 @@
package com.tcm.MineTale.datagen.recipes;
+import com.tcm.MineTale.datagen.builders.WorkbenchRecipeBuilder;
+import com.tcm.MineTale.registry.ModBlocks;
+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;
@@ -7,22 +13,20 @@
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");
+ new WorkbenchRecipeBuilder(ModRecipes.BUILDERS_TYPE, ModRecipes.BUILDERS_SERIALIZER)
+ .input(ModItems.PLANT_FIBER)
+ .output(ModBlocks.ROPE)
+ .time(3)
+ .unlockedBy("has_builders_workbench", provider.has(ModBlocks.BUILDERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.BUILDERS_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");
+ new WorkbenchRecipeBuilder(ModRecipes.BUILDERS_TYPE, ModRecipes.BUILDERS_SERIALIZER)
+ .input(ModItems.PLANT_FIBER)
+ .output(ModBlocks.ROPE_DIAGONAL)
+ .time(3)
+ .unlockedBy("has_builders_workbench", provider.has(ModBlocks.BUILDERS_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.BUILDERS_SEARCH)
+ .save(exporter, "builders_workbench_rope_diagonal");
}
}
diff --git a/src/client/java/com/tcm/MineTale/datagen/recipes/WorkbenchRecipes.java b/src/client/java/com/tcm/MineTale/datagen/recipes/WorkbenchRecipes.java
index c0ff6b3..b683341 100644
--- a/src/client/java/com/tcm/MineTale/datagen/recipes/WorkbenchRecipes.java
+++ b/src/client/java/com/tcm/MineTale/datagen/recipes/WorkbenchRecipes.java
@@ -33,7 +33,6 @@ public static void buildRecipes(RecipeProvider provider, RecipeOutput exporter,
.bookCategory(ModRecipeDisplay.WORKBENCH_SEARCH)
.save(exporter, "workbench_furnace_workbench_t1");
- // TODO: FarmersWorkbench Not Implemented
new WorkbenchRecipeBuilder(ModRecipes.WORKBENCH_TYPE, ModRecipes.WORKBENCH_SERIALIZER)
.input(ItemTags.LOGS, lookup, 6)
.input(ModItems.PLANT_FIBER, 20)
@@ -43,15 +42,14 @@ public static void buildRecipes(RecipeProvider provider, RecipeOutput exporter,
.bookCategory(ModRecipeDisplay.WORKBENCH_SEARCH)
.save(exporter, "workbench_farmers_workbench");
- // TODO: Builder's Workbench Not Implemented
- // new WorkbenchRecipeBuilder(ModRecipes.WORKBENCH_TYPE, ModRecipes.WORKBENCH_SERIALIZER)
- // .input(ItemTags.LOGS, lookup, 6)
- // .input(ItemTags.STONE_TOOL_MATERIALS, lookup, 3)
- // .output(ModBlocks.BUILDERS_WORKBENCH.asItem())
- // .time(2)
- // .unlockedBy("has_workbench", provider.has(ModBlocks.WORKBENCH_WORKBENCH_BLOCK.asItem()))
- // .bookCategory(ModRecipeDisplay.WORKBENCH_SEARCH)
- // .save(exporter, "workbench_builders_workbench");
+ new WorkbenchRecipeBuilder(ModRecipes.WORKBENCH_TYPE, ModRecipes.WORKBENCH_SERIALIZER)
+ .input(ItemTags.LOGS, lookup, 6)
+ .input(ItemTags.STONE_TOOL_MATERIALS, lookup, 3)
+ .output(ModBlocks.BUILDERS_WORKBENCH_BLOCK)
+ .time(2)
+ .unlockedBy("has_workbench", provider.has(ModBlocks.WORKBENCH_WORKBENCH_BLOCK))
+ .bookCategory(ModRecipeDisplay.WORKBENCH_SEARCH)
+ .save(exporter, "workbench_builders_workbench");
// TODO: HAY_TARGET Not Implemented
// new WorkbenchRecipeBuilder(ModRecipes.WORKBENCH_TYPE, ModRecipes.WORKBENCH_SERIALIZER)
diff --git a/src/main/java/com/tcm/MineTale/MineTale.java b/src/main/java/com/tcm/MineTale/MineTale.java
index fecd8f6..206cd14 100644
--- a/src/main/java/com/tcm/MineTale/MineTale.java
+++ b/src/main/java/com/tcm/MineTale/MineTale.java
@@ -18,8 +18,6 @@
import com.tcm.MineTale.block.workbenches.entity.AbstractWorkbenchEntity;
import com.tcm.MineTale.block.workbenches.menu.AbstractWorkbenchContainerMenu;
-import com.tcm.MineTale.block.workbenches.menu.ArmorersWorkbenchMenu;
-import com.tcm.MineTale.block.workbenches.menu.FarmersWorkbenchMenu;
import com.tcm.MineTale.network.ClientboundNearbyInventorySyncPacket;
import com.tcm.MineTale.network.CraftRequestPayload;
import com.tcm.MineTale.recipe.WorkbenchRecipe;
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/BuildersWorkbench.java b/src/main/java/com/tcm/MineTale/block/workbenches/BuildersWorkbench.java
new file mode 100644
index 0000000..7f9c7eb
--- /dev/null
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/BuildersWorkbench.java
@@ -0,0 +1,96 @@
+package com.tcm.MineTale.block.workbenches;
+
+import java.util.function.Supplier;
+
+import org.jetbrains.annotations.Nullable;
+
+import com.mojang.serialization.MapCodec;
+import com.tcm.MineTale.block.workbenches.entity.AbstractWorkbenchEntity;
+import com.tcm.MineTale.block.workbenches.entity.BuildersWorkbenchEntity;
+import com.tcm.MineTale.registry.ModBlockEntities;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.level.BlockGetter;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.RenderShape;
+import net.minecraft.world.level.block.entity.BlockEntity;
+import net.minecraft.world.level.block.entity.BlockEntityTicker;
+import net.minecraft.world.level.block.entity.BlockEntityType;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.phys.shapes.CollisionContext;
+import net.minecraft.world.phys.shapes.VoxelShape;
+
+public class BuildersWorkbench extends AbstractWorkbench {
+ public static final boolean IS_WIDE = true;
+ public static final boolean IS_TALL = false;
+
+ public static final MapCodec CODEC = simpleCodec(BuildersWorkbench::new);
+
+ /**
+ * Constructs a BuildersWorkbench that uses the mod's BUILDERS_WORKBENCH_BE block entity type.
+ *
+ * @param properties block properties for this workbench
+ */
+ public BuildersWorkbench(Properties properties) {
+ // Hardcode the supplier and sounds here if they never change
+ super(properties, () -> ModBlockEntities.BUILDERS_WORKBENCH_BE, IS_WIDE, IS_TALL, 1);
+ }
+
+ /**
+ * Constructs a BuildersWorkbench using the provided block properties and block-entity type supplier.
+ *
+ * @param properties block properties to apply to this workbench
+ * @param supplier supplier that provides the BlockEntityType for the BuildersWorkbenchEntity
+ */
+ public BuildersWorkbench(Properties properties, Supplier> supplier) {
+ super(properties, supplier, IS_WIDE, IS_TALL, 1);
+ }
+
+ /**
+ * Provides a ticker for workbench block entities when the supplied block entity type matches this block's entity type.
+ *
+ * @param type the block entity type to match against this block's workbench entity type
+ * @return a BlockEntityTicker that updates matching workbench block entities, or {@code null} if the types do not match
+ */
+ @Nullable
+ @Override
+ public BlockEntityTicker getTicker(Level level, BlockState state, BlockEntityType type) {
+ // This connects the Level's ticking system to your static tick method
+ return createTickerHelper(type, ModBlockEntities.BUILDERS_WORKBENCH_BE, AbstractWorkbenchEntity::tick);
+ }
+
+ /**
+ * Provides the MapCodec used to serialize and deserialize this workbench.
+ *
+ * @return the MapCodec for this BuildersWorkbench
+ */
+ @Override
+ protected MapCodec extends BuildersWorkbench> codec() {
+ return CODEC;
+ }
+
+ /**
+ * Specifies that this block is rendered using its block model.
+ *
+ * @return RenderShape.MODEL to render the block using its JSON/model representation.
+ */
+ @Override
+ public RenderShape getRenderShape(BlockState state) {
+ // BaseEntityBlock defaults to INVISIBLE.
+ // We set it to MODEL so the JSON model is rendered.
+ return RenderShape.MODEL;
+ }
+
+ private static final VoxelShape SHAPE = Block.box(0, 0, 0, 16, 16, 16);
+
+ /**
+ * The block's collision and interaction shape as a 1×1 footprint (x 0–16, y 0–16, z 0–16).
+ *
+ * @return the voxel shape used for collision and interaction
+ */
+ @Override
+ public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
+ return SHAPE;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/entity/BuildersWorkbenchEntity.java b/src/main/java/com/tcm/MineTale/block/workbenches/entity/BuildersWorkbenchEntity.java
new file mode 100644
index 0000000..510e9c7
--- /dev/null
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/entity/BuildersWorkbenchEntity.java
@@ -0,0 +1,176 @@
+package com.tcm.MineTale.block.workbenches.entity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jspecify.annotations.Nullable;
+
+import com.mojang.serialization.Codec;
+import com.tcm.MineTale.block.workbenches.menu.BuildersWorkbenchMenu;
+import com.tcm.MineTale.recipe.WorkbenchRecipe;
+import com.tcm.MineTale.registry.ModBlockEntities;
+import com.tcm.MineTale.registry.ModRecipes;
+import com.tcm.MineTale.util.Constants;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.entity.player.Inventory;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ContainerData;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.crafting.RecipeType;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.storage.ValueInput;
+import net.minecraft.world.level.storage.ValueOutput;
+
+public class BuildersWorkbenchEntity extends AbstractWorkbenchEntity {
+ protected final ContainerData data = new ContainerData() {
+ /**
+ * Retrieves an internal data value by index for UI synchronization.
+ *
+ * @param index There is no cook time or anything for this block as it doesnt use it
+ * @return the value associated with {@code index}, or 0 for any other index
+ */
+ @Override
+ public int get(int index) {
+ return switch (index) {
+ default -> 0;
+ };
+ }
+
+ /**
+ * No-op for this workbench; data is server-driven and not set client-side.
+ *
+ * `@param` index the data index to set
+ * `@param` value the value to assign (ignored)
+ */
+ @Override
+ public void set(int index, int value) {
+ // Not required on WorkbenchEntity
+ }
+
+ /**
+ * The number of data values exposed by this ContainerData.
+ *
+ * @return the number of data entries (4)
+ */
+ @Override
+ public int getCount() {
+ return 4;
+ }
+ };
+
+ /**
+ * Creates a BuildersWorkbenchEntity for the specified world position and block state.
+ *
+ * Sets the entity's scanRadius to 0.0 and tier to 1.
+ *
+ * @param blockPos the world position of this block entity
+ * @param blockState the block state for this block entity
+ */
+ public BuildersWorkbenchEntity(BlockPos blockPos, BlockState blockState) {
+ super(ModBlockEntities.BUILDERS_WORKBENCH_BE, blockPos, blockState);
+
+ this.tier = 1;
+ this.canPullFromNearby = true;
+ }
+
+ /**
+ * Persist this workbench's state to the given ValueOutput.
+ *
+ * Stores "WorkbenchTier" (int), "ScanRadius" (double), and the full inventory under "Inventory"
+ * using type-safe Codecs.
+ *
+ * @param valueOutput the writer used to serialize this entity's fields
+ */
+ @Override
+ protected void saveAdditional(ValueOutput valueOutput) {
+ super.saveAdditional(valueOutput);
+ // store() uses Codecs for type safety
+ valueOutput.store("WorkbenchTier", Codec.INT, this.tier);
+ valueOutput.store("ScanRadius", Codec.DOUBLE, this.scanRadius);
+
+ // Convert the SimpleContainer to a List of ItemStacks for the Codec
+ // Or use the built-in NBT helper if your framework supports it
+ List stacks = new ArrayList<>();
+ for (int i = 0; i < inventory.getContainerSize(); i++) {
+ stacks.add(inventory.getItem(i));
+ }
+
+ // CHANGE: Use OPTIONAL_CODEC instead of CODEC
+ valueOutput.store("Inventory", ItemStack.OPTIONAL_CODEC.listOf(), stacks);
+ }
+
+ /**
+ * Restores workbench-specific state from persistent storage, applying defaults when keys are absent.
+ *
+ * Delegates to the superclass load logic, then:
+ * - reads "WorkbenchTier" (int) into {@code tier}, defaulting to {@code 1} if missing;
+ * - reads "ScanRadius" (double) into {@code scanRadius}, defaulting to {@code 0.0} if missing;
+ * - reads "Inventory" as a list of {@code ItemStack} and populates the internal inventory up to its capacity.
+ */
+ @Override
+ protected void loadAdditional(ValueInput valueInput) {
+ super.loadAdditional(valueInput);
+ // read() returns an Optional
+ this.tier = valueInput.read("WorkbenchTier", Codec.INT).orElse(1);
+ this.scanRadius = valueInput.read("ScanRadius", Codec.DOUBLE).orElse(0.0);
+
+ // Read the inventory list back
+ valueInput.read("Inventory", ItemStack.OPTIONAL_CODEC.listOf()).ifPresent(stacks -> {
+ for (int i = 0; i < stacks.size() && i < inventory.getContainerSize(); i++) {
+ inventory.setItem(i, stacks.get(i));
+ }
+ });
+ }
+
+ /**
+ * Creates the server-side container menu for this workbench's UI.
+ *
+ * @param syncId the window id used to synchronize the menu with the client
+ * @param playerInventory the opening player's inventory
+ * @param player the player who opened the menu
+ * @return a BuildersWorkbenchMenu bound to this workbench's inventory and synced data
+ */
+ @Override
+ public @Nullable AbstractContainerMenu createMenu(int syncId, Inventory playerInventory, Player player) {
+ // 1. Trigger the sync to the client before returning the menu
+ if (player instanceof ServerPlayer serverPlayer) {
+ this.syncNearbyToPlayer(serverPlayer);
+ }
+
+ // // 2. Return the menu as usual
+ return new BuildersWorkbenchMenu(syncId, playerInventory, this.data, this);
+ }
+
+ /**
+ * Identifies the recipe type used to find and match recipes for this workbench.
+ *
+ * @return the RecipeType for workbench recipes
+ */
+ @Override
+ public RecipeType getWorkbenchRecipeType() {
+ return ModRecipes.BUILDERS_TYPE;
+ }
+
+ /**
+ * Determines whether the workbench currently has fuel available.
+ *
+ * Checks that the entity is in a loaded level and that the configured fuel slot contains an item.
+ *
+ * @return `true` if the entity is in a loaded level and the fuel slot contains an item, `false` otherwise.
+ */
+ @Override
+ protected boolean hasFuel() {
+ if (this.level == null) return false;
+
+ // Check if block is lit
+ // BlockState state = this.level.getBlockState(this.worldPosition);
+ // boolean isLit = state.hasProperty(BlockStateProperties.LIT) && state.getValue(BlockStateProperties.LIT);
+
+ boolean hasFuelItem = !this.getItem(Constants.FUEL_SLOT).isEmpty();
+
+ return hasFuelItem;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/menu/BuildersWorkbenchMenu.java b/src/main/java/com/tcm/MineTale/block/workbenches/menu/BuildersWorkbenchMenu.java
new file mode 100644
index 0000000..5fb4e8f
--- /dev/null
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/menu/BuildersWorkbenchMenu.java
@@ -0,0 +1,122 @@
+package com.tcm.MineTale.block.workbenches.menu;
+
+import java.util.List;
+
+import org.jspecify.annotations.Nullable;
+
+import com.tcm.MineTale.block.workbenches.entity.AbstractWorkbenchEntity;
+import com.tcm.MineTale.recipe.WorkbenchRecipeInput;
+import com.tcm.MineTale.registry.ModMenuTypes;
+import com.tcm.MineTale.registry.ModRecipes;
+
+import net.minecraft.world.SimpleContainer;
+import net.minecraft.world.entity.player.Inventory;
+import net.minecraft.world.entity.player.StackedItemContents;
+import net.minecraft.world.inventory.ContainerData;
+import net.minecraft.world.inventory.RecipeBookType;
+import net.minecraft.world.inventory.SimpleContainerData;
+import net.minecraft.world.item.ItemStack;
+
+public class BuildersWorkbenchMenu extends AbstractWorkbenchContainerMenu {
+ // No internal inventory needed anymore, but we pass an empty container to the super
+ private static final int EMPTY_SIZE = 0;
+ private static final int DATA_SIZE = 0;
+
+ @Nullable
+ private final AbstractWorkbenchEntity blockEntity;
+ private final Inventory playerInventory;
+
+ /**
+ * Creates a client-side menu instance when the workbench UI is opened.
+ *
+ * @param syncId the synchronization id used to match this menu with the server
+ * @param playerInventory the player's inventory bound to this menu
+ */
+ public BuildersWorkbenchMenu(int syncId, Inventory playerInventory) {
+ this(syncId, playerInventory, new SimpleContainerData(EMPTY_SIZE), null);
+ }
+
+ /**
+ * Creates a workbench menu associated with the given player inventory and optional block entity.
+ *
+ * Uses an empty internal container (size 0) and the class's data size for syncing numeric state.
+ *
+ * @param syncId synchronization id for this menu
+ * @param playerInventory the player's inventory used for slot access and recipe-book integration
+ * @param data container data used to sync numeric state between server and client
+ * @param blockEntity nullable block entity this menu is bound to, or {@code null} if not bound
+ */
+ public BuildersWorkbenchMenu(int syncId, Inventory playerInventory, ContainerData data, @Nullable AbstractWorkbenchEntity blockEntity) {
+ // Note: The order of arguments depends on your AbstractWorkbenchContainerMenu,
+ // but the 'expectedSize' parameter MUST be 0.
+ super(
+ ModMenuTypes.BUILDERS_WORKBENCH_MENU,
+ syncId,
+ new SimpleContainer(EMPTY_SIZE),
+ data,
+ DATA_SIZE,
+ playerInventory,
+ EMPTY_SIZE,
+ EMPTY_SIZE,
+ ModRecipes.BUILDERS_TYPE
+ );
+ this.blockEntity = blockEntity;
+ this.playerInventory = playerInventory;
+ }
+
+ /**
+ * Accesses the block entity bound to this menu, if present.
+ *
+ * @return the bound AbstractWorkbenchEntity, or {@code null} if this menu is not bound to a block entity
+ */
+ @Override
+ public @Nullable AbstractWorkbenchEntity getBlockEntity() {
+ return this.blockEntity;
+ }
+
+ @Override
+ public void fillCraftSlotsStackedContents(StackedItemContents contents) {
+ // 1. Account for items in the player's pockets
+ this.playerInventory.fillStackedContents(contents);
+
+ // 2. Account for items sitting in the Workbench slots (if any)
+ for (int i = 0; i < this.container.getContainerSize(); i++) {
+ contents.accountStack(this.container.getItem(i));
+ }
+
+ // 3. THE FIX: Use the list provided by the Packet (Networked Items)
+ // We stop calling be.getNearbyInventories() here because it returns empty on Client
+ List nearbyItems = this.getNetworkedNearbyItems();
+
+ if (!nearbyItems.isEmpty() && this.playerInventory.player.level().isClientSide()) {
+ System.out.println("DEBUG: Recipe Book is now accounting for " + nearbyItems.size() + " stacks from the packet!");
+ }
+
+ for (ItemStack stack : nearbyItems) {
+ contents.accountStack(stack);
+ }
+ }
+
+ /**
+ * Selects the crafting recipe-book category for this menu.
+ *
+ * @return {@code RecipeBookType.CRAFTING}
+ */
+ @Override
+ public RecipeBookType getRecipeBookType() {
+ // This keeps the Crafting-style recipe book available on the UI
+ return RecipeBookType.CRAFTING;
+ }
+
+ /**
+ * Create the recipe input used by this menu's crafting UI; this implementation provides an empty input.
+ *
+ * @return a WorkbenchRecipeInput with both input stacks set to ItemStack.EMPTY
+ */
+ @Override
+ public WorkbenchRecipeInput createRecipeInput() {
+ // Since there are no slots, we return an empty input.
+ // The actual crafting logic will scan the player inventory directly when a button is clicked.
+ return new WorkbenchRecipeInput(ItemStack.EMPTY, ItemStack.EMPTY);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/menu/WorkbenchWorkbenchMenu.java b/src/main/java/com/tcm/MineTale/block/workbenches/menu/WorkbenchWorkbenchMenu.java
index 981f8dc..f588a0e 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/menu/WorkbenchWorkbenchMenu.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/menu/WorkbenchWorkbenchMenu.java
@@ -5,7 +5,6 @@
import org.jspecify.annotations.Nullable;
import com.tcm.MineTale.block.workbenches.entity.AbstractWorkbenchEntity;
-import com.tcm.MineTale.recipe.WorkbenchRecipe;
import com.tcm.MineTale.recipe.WorkbenchRecipeInput;
import com.tcm.MineTale.registry.ModMenuTypes;
import com.tcm.MineTale.registry.ModRecipes;
@@ -17,7 +16,6 @@
import net.minecraft.world.inventory.RecipeBookType;
import net.minecraft.world.inventory.SimpleContainerData;
import net.minecraft.world.item.ItemStack;
-import net.minecraft.world.item.crafting.RecipeType;
public class WorkbenchWorkbenchMenu extends AbstractWorkbenchContainerMenu {
// No internal inventory needed anymore, but we pass an empty container to the super
diff --git a/src/main/java/com/tcm/MineTale/registry/ModBlockEntities.java b/src/main/java/com/tcm/MineTale/registry/ModBlockEntities.java
index 1f69384..6f05812 100644
--- a/src/main/java/com/tcm/MineTale/registry/ModBlockEntities.java
+++ b/src/main/java/com/tcm/MineTale/registry/ModBlockEntities.java
@@ -2,6 +2,7 @@
import com.tcm.MineTale.MineTale;
import com.tcm.MineTale.block.workbenches.entity.ArmorersWorkbenchEntity;
+import com.tcm.MineTale.block.workbenches.entity.BuildersWorkbenchEntity;
import com.tcm.MineTale.block.workbenches.entity.CampfireWorkbenchEntity;
import com.tcm.MineTale.block.workbenches.entity.FurnaceWorkbenchEntity;
import com.tcm.MineTale.block.workbenches.entity.WorkbenchWorkbenchEntity;
@@ -40,6 +41,12 @@ public class ModBlockEntities {
ModBlocks.FARMERS_WORKBENCH_BLOCK
);
+ public static final BlockEntityType BUILDERS_WORKBENCH_BE = register(
+ "builders_workbench_be",
+ BuildersWorkbenchEntity::new,
+ ModBlocks.BUILDERS_WORKBENCH_BLOCK
+ );
+
/**
* Register a BlockEntityType for the given furnace tier and store it in the tier map.
*
diff --git a/src/main/java/com/tcm/MineTale/registry/ModBlocks.java b/src/main/java/com/tcm/MineTale/registry/ModBlocks.java
index 3516eff..d629d2f 100644
--- a/src/main/java/com/tcm/MineTale/registry/ModBlocks.java
+++ b/src/main/java/com/tcm/MineTale/registry/ModBlocks.java
@@ -6,6 +6,7 @@
import com.tcm.MineTale.MineTale;
import com.tcm.MineTale.block.workbenches.ArmorersWorkbench;
+import com.tcm.MineTale.block.workbenches.BuildersWorkbench;
import com.tcm.MineTale.block.workbenches.CampfireWorkbench;
import com.tcm.MineTale.block.workbenches.FarmersWorkbench;
import com.tcm.MineTale.block.workbenches.FurnaceWorkbench;
@@ -83,6 +84,13 @@ public class ModBlocks {
true
);
+ public static final Block BUILDERS_WORKBENCH_BLOCK = register(
+ "builders_workbench",
+ BuildersWorkbench::new,
+ BlockBehaviour.Properties.of().sound(SoundType.WOOD),
+ true
+ );
+
//Logs
public static final Block AMBER_LOG = register("amber_log", RotatedPillarBlock::new, logProperties(MapColor.COLOR_ORANGE, MapColor.PODZOL, SoundType.WOOD), true);
public static final Block BAMBOO_LOG = register("bamboo_log", RotatedPillarBlock::new, logProperties(MapColor.COLOR_GREEN, MapColor.GRASS, SoundType.WOOD), true);
@@ -174,6 +182,10 @@ public class ModBlocks {
// Functional / Crafted
public static final Block BAMBOO_PLANTER = register("bamboo_planter", Block::new, BlockBehaviour.Properties.of().sound(SoundType.WOOD), true);
+ // Decorational
+ public static final Block ROPE = register("rope", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+ public static final Block ROPE_DIAGONAL = register("rope_diagonal", Block::new, BlockBehaviour.Properties.of().sound(SoundType.GRASS), true);
+
// Ores
/// 1. COPPER
diff --git a/src/main/java/com/tcm/MineTale/registry/ModMenuTypes.java b/src/main/java/com/tcm/MineTale/registry/ModMenuTypes.java
index ced67ab..e15a697 100644
--- a/src/main/java/com/tcm/MineTale/registry/ModMenuTypes.java
+++ b/src/main/java/com/tcm/MineTale/registry/ModMenuTypes.java
@@ -5,6 +5,7 @@
import com.tcm.MineTale.block.workbenches.menu.WorkbenchWorkbenchMenu;
import com.tcm.MineTale.block.workbenches.menu.FarmersWorkbenchMenu;
import com.tcm.MineTale.block.workbenches.menu.ArmorersWorkbenchMenu;
+import com.tcm.MineTale.block.workbenches.menu.BuildersWorkbenchMenu;
import com.tcm.MineTale.block.workbenches.menu.CampfireWorkbenchMenu;
import net.minecraft.core.Registry;
@@ -40,6 +41,11 @@ public class ModMenuTypes {
"farmers_workbench_menu",
FarmersWorkbenchMenu::new
);
+
+ public static final MenuType BUILDERS_WORKBENCH_MENU = register(
+ "builders_workbench_menu",
+ BuildersWorkbenchMenu::new
+ );
/**
* Triggers static registration of the mod's menu types.
diff --git a/src/main/java/com/tcm/MineTale/registry/ModRecipeDisplay.java b/src/main/java/com/tcm/MineTale/registry/ModRecipeDisplay.java
index f9a2986..692e12e 100644
--- a/src/main/java/com/tcm/MineTale/registry/ModRecipeDisplay.java
+++ b/src/main/java/com/tcm/MineTale/registry/ModRecipeDisplay.java
@@ -33,12 +33,16 @@ public class ModRecipeDisplay {
public static final RecipeDisplay.Type FARMERS_TYPE =
new RecipeDisplay.Type<>(WorkbenchRecipeDisplay.CODEC, STREAM_CODEC);
+ public static final RecipeDisplay.Type BUILDERS_TYPE =
+ new RecipeDisplay.Type<>(WorkbenchRecipeDisplay.CODEC, STREAM_CODEC);
+
// 1. Declare the fields but don't assign them yet
public static final RecipeBookCategory CAMPFIRE_SEARCH = registerCategory("campfire_recipe_book_category");
public static final RecipeBookCategory WORKBENCH_SEARCH = registerCategory("workbench_recipe_book_category");
public static final RecipeBookCategory ARMORERS_SEARCH = registerCategory("armorers_recipe_book_category");
public static final RecipeBookCategory FURNACE_T1_SEARCH = registerCategory("furnace_t1_recipe_book_category");
public static final RecipeBookCategory FARMERS_SEARCH = registerCategory("farmers_recipe_book_category");
+ public static final RecipeBookCategory BUILDERS_SEARCH = registerCategory("builders_recipe_book_category");
/**
* Registers the workbench recipe display type into the built-in recipe display registry.
diff --git a/src/main/java/com/tcm/MineTale/registry/ModRecipes.java b/src/main/java/com/tcm/MineTale/registry/ModRecipes.java
index 2f8cdf1..f758ccd 100644
--- a/src/main/java/com/tcm/MineTale/registry/ModRecipes.java
+++ b/src/main/java/com/tcm/MineTale/registry/ModRecipes.java
@@ -18,6 +18,7 @@ public class ModRecipes {
public static final RecipeType ARMORERS_TYPE = createType("armorers_recipe_type");
public static final RecipeType FURNACE_T1_TYPE = createType("furnace_t1_recipe_type");
public static final RecipeType FARMERS_TYPE = createType("farmers_recipe_type");
+ public static final RecipeType BUILDERS_TYPE = createType("builders_recipe_type");
// 2. Define the Serializers (The "How")
// We pass the specific Type into the Serializer's constructor
@@ -36,6 +37,9 @@ public class ModRecipes {
public static final RecipeSerializer FARMERS_SERIALIZER =
new WorkbenchRecipe.Serializer(FARMERS_TYPE);
+ public static final RecipeSerializer BUILDERS_SERIALIZER =
+ new WorkbenchRecipe.Serializer(BUILDERS_TYPE);
+
/**
* Registers the mod's recipe types and their serializers into the game's built-in registries.
*
@@ -54,6 +58,8 @@ public static void initialize() {
register(ARMORERS_TYPE, ARMORERS_SERIALIZER);
register(FARMERS_TYPE, FARMERS_SERIALIZER);
+
+ register(BUILDERS_TYPE, BUILDERS_SERIALIZER);
}
/**
From 41f3a8aa72175a4d72a0cfc7e067da1bb845d32f Mon Sep 17 00:00:00 2001
From: "coderabbitai[bot]"
<136622811+coderabbitai[bot]@users.noreply.github.com>
Date: Thu, 26 Feb 2026 11:37:02 +0000
Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=93=9D=20Add=20docstrings=20to=20`fea?=
=?UTF-8?q?t/add-farmers-workbench`?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Docstrings generation was requested by @The-Code-Monkey.
The following files were modified:
* `src/client/java/com/tcm/MineTale/MineTaleClient.java`
* `src/client/java/com/tcm/MineTale/block/workbenches/screen/ArmorersWorkbenchScreen.java`
* `src/client/java/com/tcm/MineTale/block/workbenches/screen/FarmersWorkbenchScreen.java`
* `src/client/java/com/tcm/MineTale/block/workbenches/screen/WorkbenchWorkbenchScreen.java`
* `src/main/java/com/tcm/MineTale/MineTale.java`
* `src/main/java/com/tcm/MineTale/block/workbenches/FarmersWorkbench.java`
* `src/main/java/com/tcm/MineTale/block/workbenches/entity/FarmersWorkbenchEntity.java`
* `src/main/java/com/tcm/MineTale/block/workbenches/menu/AbstractWorkbenchContainerMenu.java`
* `src/main/java/com/tcm/MineTale/block/workbenches/menu/ArmorersWorkbenchMenu.java`
* `src/main/java/com/tcm/MineTale/block/workbenches/menu/CampfireWorkbenchMenu.java`
* `src/main/java/com/tcm/MineTale/block/workbenches/menu/FarmersWorkbenchMenu.java`
* `src/main/java/com/tcm/MineTale/block/workbenches/menu/FurnaceWorkbenchMenu.java`
* `src/main/java/com/tcm/MineTale/block/workbenches/menu/WorkbenchWorkbenchMenu.java`
These files were kept as they were:
* `src/client/java/com/tcm/MineTale/datagen/recipes/FarmerRecipes.java`
* `src/client/java/com/tcm/MineTale/datagen/recipes/WorkbenchRecipes.java`
* `src/main/java/com/tcm/MineTale/registry/ModRecipes.java`
---
.../java/com/tcm/MineTale/MineTaleClient.java | 14 +++----
.../screen/ArmorersWorkbenchScreen.java | 12 +++---
.../screen/FarmersWorkbenchScreen.java | 30 +++++++-------
.../screen/WorkbenchWorkbenchScreen.java | 29 +++++++-------
src/main/java/com/tcm/MineTale/MineTale.java | 9 +++--
.../block/workbenches/FarmersWorkbench.java | 6 +--
.../entity/FarmersWorkbenchEntity.java | 6 +--
.../menu/AbstractWorkbenchContainerMenu.java | 40 ++++++++++---------
.../menu/ArmorersWorkbenchMenu.java | 8 ++--
.../menu/CampfireWorkbenchMenu.java | 17 ++++----
.../menu/FarmersWorkbenchMenu.java | 12 +++---
.../menu/FurnaceWorkbenchMenu.java | 12 +++---
.../menu/WorkbenchWorkbenchMenu.java | 19 +++++----
13 files changed, 108 insertions(+), 106 deletions(-)
diff --git a/src/client/java/com/tcm/MineTale/MineTaleClient.java b/src/client/java/com/tcm/MineTale/MineTaleClient.java
index ad83d84..148bb6b 100644
--- a/src/client/java/com/tcm/MineTale/MineTaleClient.java
+++ b/src/client/java/com/tcm/MineTale/MineTaleClient.java
@@ -24,15 +24,15 @@
public class MineTaleClient implements ClientModInitializer {
/**
- * Initializes client-side handlers for the MineTale mod.
+ * Initialises client-side handlers for the MineTale mod.
*
- * Registers screen factories for custom workbench menu types, configures block render
- * layers for furnace workbenches, and registers a global network receiver that applies
- * nearby inventory item lists to an open workbench menu when available.
+ * Registers screen factories for custom workbench menu types, configures render
+ * layers for furnace workbench blocks, and registers a global network receiver
+ * that applies nearby inventory items to an open workbench menu.
*
- * The network receiver schedules a client task that retries up to 10 frames if the
- * player's container menu is not yet an instance of the expected workbench menu and
- * logs a failure message if synchronization could not be applied after retries.
+ * The network receiver schedules work on the client thread and retries application
+ * for up to 10 client ticks if the expected workbench menu is not yet open; if
+ * synchronization still fails it logs a failure message.
*/
@Override
public void onInitializeClient() {
diff --git a/src/client/java/com/tcm/MineTale/block/workbenches/screen/ArmorersWorkbenchScreen.java b/src/client/java/com/tcm/MineTale/block/workbenches/screen/ArmorersWorkbenchScreen.java
index cbc8b5d..6f27b05 100644
--- a/src/client/java/com/tcm/MineTale/block/workbenches/screen/ArmorersWorkbenchScreen.java
+++ b/src/client/java/com/tcm/MineTale/block/workbenches/screen/ArmorersWorkbenchScreen.java
@@ -88,12 +88,12 @@ private static MineTaleRecipeBookComponent createRecipeBookComponent(ArmorersWor
}
/**
- * Configure the screen's GUI dimensions and initialize widgets.
- *
- * Sets the layout size (imageWidth = 176, imageHeight = 166), delegates remaining
- * layout initialization to the superclass, and creates the three craft buttons
- * ("1", "10", "All") wired to their respective handlers.
- */
+ * Initialises the screen size and adds the three crafting buttons.
+ *
+ * Sets the GUI image dimensions, delegates further initialisation to the superclass,
+ * and creates/registers three buttons wired to craft one, ten or all items
+ * (they invoke handleCraftRequest with 1, 10 and -1 respectively; -1 signifies "All").
+ */
@Override
protected void init() {
// Important: Set your GUI size before super.init()
diff --git a/src/client/java/com/tcm/MineTale/block/workbenches/screen/FarmersWorkbenchScreen.java b/src/client/java/com/tcm/MineTale/block/workbenches/screen/FarmersWorkbenchScreen.java
index bd56f55..d8b0824 100644
--- a/src/client/java/com/tcm/MineTale/block/workbenches/screen/FarmersWorkbenchScreen.java
+++ b/src/client/java/com/tcm/MineTale/block/workbenches/screen/FarmersWorkbenchScreen.java
@@ -90,11 +90,12 @@ private static MineTaleRecipeBookComponent createRecipeBookComponent(FarmersWork
}
/**
- * Configure the screen layout size and initialize crafting widgets.
+ * Initialises the screen layout and registers the crafting controls.
*
- * Sets the GUI dimensions to 176x166 and creates three craft buttons:
- * "Craft" (requests 1), "x10" (requests 10), and "All" (requests all, represented by -1),
- * each wired to their respective handler.
+ * Sets the GUI dimensions to 176×166 and adds three buttons wired to craft requests:
+ * - "Craft" requests 1,
+ * - "x10" requests 10,
+ * - "All" requests all (represented by -1).
*/
@Override
protected void init() {
@@ -257,16 +258,13 @@ private boolean canCraft(Player player, RecipeDisplayEntry entry, int craftCount
}
/**
- * Checks whether the given inventory combined with the workbench's networked nearby items
- * contains at least the requested number of items matching the provided ingredient.
+ * Determine whether the player inventory together with the workbench's networked nearby items
+ * contains at least the specified quantity of items matching the given ingredient.
*
- * Counts matching item stacks from the supplied player inventory and, when the screen's menu
- * is an AbstractWorkbenchContainerMenu, from its networked nearby items list.
- *
- * @param inventory the player's inventory to search
- * @param ingredient the ingredient matcher to test item stacks against
+ * @param inventory the player's inventory to check
+ * @param ingredient the ingredient matcher used to test item stacks
* @param totalRequired the total number of matching items required
- * @return `true` if the combined sources contain at least `totalRequired` matching items, `false` otherwise
+ * @return true if the combined sources contain at least totalRequired matching items, false otherwise
*/
private boolean hasIngredientAmount(Inventory inventory, Ingredient ingredient, int totalRequired) {
System.out.println("DEBUG: Searching inventory + nearby for " + totalRequired + "...");
@@ -303,10 +301,10 @@ private boolean hasIngredientAmount(Inventory inventory, Ingredient ingredient,
}
/**
- * Computes the on-screen position for the recipe book toggle button for this GUI.
- *
- * @return the screen position placed 5 pixels from the GUI's left edge and 49 pixels above the GUI's vertical center
- */
+ * Get the screen position for the recipe book toggle button.
+ *
+ * @return the ScreenPosition located 5 pixels from the GUI's left edge and 49 pixels above the GUI's vertical centre
+ */
@Override
protected ScreenPosition getRecipeBookButtonPosition() {
// 1. Calculate the start (left) of your workbench GUI
diff --git a/src/client/java/com/tcm/MineTale/block/workbenches/screen/WorkbenchWorkbenchScreen.java b/src/client/java/com/tcm/MineTale/block/workbenches/screen/WorkbenchWorkbenchScreen.java
index 0f39751..37c6636 100644
--- a/src/client/java/com/tcm/MineTale/block/workbenches/screen/WorkbenchWorkbenchScreen.java
+++ b/src/client/java/com/tcm/MineTale/block/workbenches/screen/WorkbenchWorkbenchScreen.java
@@ -90,12 +90,14 @@ private static MineTaleRecipeBookComponent createRecipeBookComponent(WorkbenchWo
}
/**
- * Configure the screen's GUI dimensions and initialize widgets.
- *
- * Sets the layout size (imageWidth = 176, imageHeight = 166), delegates remaining
- * layout initialization to the superclass, and creates the three craft buttons
- * ("1", "10", "All") wired to their respective handlers.
- */
+ * Initialises the workbench screen's GUI size and interactive widgets.
+ *
+ * Sets the screen image dimensions, delegates remaining setup to the superclass,
+ * computes default button positions and creates three craft buttons:
+ * - "Craft" (requests 1),
+ * - "x10" (requests 10),
+ * - "All" (requests -1 to indicate all).
+ */
@Override
protected void init() {
// Important: Set your GUI size before super.init()
@@ -147,12 +149,12 @@ private void handleCraftRequest(int amount) {
}
/**
- * Draws the workbench GUI background texture at the screen's top-left corner.
+ * Draws the workbench background texture at the screen's current GUI origin.
*
* @param guiGraphics the graphics context used to draw GUI elements
* @param f partial tick time for interpolation
- * @param i current mouse x coordinate relative to the window
- * @param j current mouse y coordinate relative to the window
+ * @param i current mouse x coordinate
+ * @param j current mouse y coordinate
*/
protected void renderBg(GuiGraphics guiGraphics, float f, int i, int j) {
int k = this.leftPos;
@@ -161,12 +163,11 @@ protected void renderBg(GuiGraphics guiGraphics, float f, int i, int j) {
}
/**
- * Renders the screen, updates the remembered recipe selection, and enables or disables craft buttons based on the player's available ingredients.
+ * Render the screen, remember the current recipe selection and update craft-button availability.
*
- * This method draws the background and the superclass UI, captures the current recipe selection (persisting it to
- * {@code lastKnownSelectedId} when present), resolves the remembered selection against the client's known recipes
- * (when a level and player are available), updates the activation state of the craft buttons for counts of 1, 2 and 10
- * depending on whether the player has sufficient materials, and finally renders any tooltips.
+ * Remembers the recipe selected in the recipe book, resolves that selection against the client's known recipes when possible,
+ * sets the craft buttons active or inactive according to whether the player has sufficient ingredients for counts of 1, 2 and 10,
+ * renders the background, the superclass UI and any tooltips.
*/
@Override
public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
diff --git a/src/main/java/com/tcm/MineTale/MineTale.java b/src/main/java/com/tcm/MineTale/MineTale.java
index 5f5099a..9ae1cae 100644
--- a/src/main/java/com/tcm/MineTale/MineTale.java
+++ b/src/main/java/com/tcm/MineTale/MineTale.java
@@ -42,11 +42,12 @@ public class MineTale implements ModInitializer {
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
/**
- * Initialize and register the mod's game content, network payloads, and runtime subsystems on startup.
+ * Initialise the mod: register game content, networking codecs and runtime subsystems.
*
- * Registers blocks, items, block entities, entities, menus, recipes, recipe displays, creative tab,
- * entity data serializers, recipe serializer synchronization, and loot-table modifiers. Also registers
- * client↔server payload codecs and a global server receiver that processes craft requests from workbench-like menus.
+ * Performs startup registration in dependency order (blocks, items, block entities, entities,
+ * menus, recipes and recipe displays), registers the creative tab and entity data serializers,
+ * synchronises the furnace recipe serializer, applies loot-table modifiers, registers client↔server
+ * payload codecs and a global server receiver that processes craft requests from workbench-like menus.
*/
@Override
public void onInitialize() {
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/FarmersWorkbench.java b/src/main/java/com/tcm/MineTale/block/workbenches/FarmersWorkbench.java
index 0612c10..f9f7ec0 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/FarmersWorkbench.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/FarmersWorkbench.java
@@ -30,7 +30,7 @@ public class FarmersWorkbench extends AbstractWorkbench
public static final MapCodec CODEC = simpleCodec(FarmersWorkbench::new);
/**
- * Creates a FarmersWorkbench preconfigured to use ModBlockEntities.FARMERS_WORKBENCH_BE as its block entity type.
+ * Initialise a FarmersWorkbench that uses the mod's FARMERS_WORKBENCH_BE block-entity type and default size.
*
* @param properties block properties for this workbench
*/
@@ -40,10 +40,10 @@ public FarmersWorkbench(Properties properties) {
}
/**
- * Create a FarmersWorkbench with the given block properties and a supplier for its block-entity type.
+ * Constructs a FarmersWorkbench with the supplied block properties and block-entity type supplier.
*
* @param properties block properties to apply to this workbench
- * @param supplier supplier that provides the specific BlockEntityType to use for FarmersWorkbenchEntity
+ * @param supplier supplier of the BlockEntityType to use for the workbench's block entity
*/
public FarmersWorkbench(Properties properties, Supplier> supplier) {
super(properties, supplier, IS_WIDE, IS_TALL, 1);
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/entity/FarmersWorkbenchEntity.java b/src/main/java/com/tcm/MineTale/block/workbenches/entity/FarmersWorkbenchEntity.java
index 4993b42..5af0494 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/entity/FarmersWorkbenchEntity.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/entity/FarmersWorkbenchEntity.java
@@ -42,10 +42,10 @@ public int get(int index) {
}
/**
- * No-op setter: client-side attempts to change workbench UI data are ignored because the server is authoritative.
+ * Ignore attempts to modify the workbench's container data from the client; changes are a no-op.
*
- * @param index the data index (ignored)
- * @param value the value to assign (ignored)
+ * @param index the data slot index (ignored)
+ * @param value the value provided (ignored)
*/
@Override
public void set(int index, int value) {
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/menu/AbstractWorkbenchContainerMenu.java b/src/main/java/com/tcm/MineTale/block/workbenches/menu/AbstractWorkbenchContainerMenu.java
index 8e64bee..de486d9 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/menu/AbstractWorkbenchContainerMenu.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/menu/AbstractWorkbenchContainerMenu.java
@@ -44,8 +44,12 @@ public abstract class AbstractWorkbenchContainerMenu extends RecipeBookMenu impl
private List networkedNearbyItems = new ArrayList<>();
/**
- * Updates the list of items available from nearby chests.
- * Called by the networking system when a sync packet arrives.
+ * Replace the cached list of nearby item stacks supplied by the server for recipe simulation.
+ *
+ * Updates the menu's networked nearby-items list with the provided stacks; this list is used
+ * when the recipe book or placement logic simulates available ingredients from nearby inventories.
+ *
+ * @param items the new list of nearby ItemStack instances received from the server
*/
public void setNetworkedNearbyItems(List items) {
this.networkedNearbyItems = items;
@@ -61,31 +65,31 @@ public List getNetworkedNearbyItems() {
}
/**
- * Provide the recipe system used by this workbench menu.
+ * The recipe type used to match recipes for this workbench.
*
- * @return the {@code RecipeType} used to match and retrieve recipes for this workbench
+ * @return the RecipeType for WorkbenchRecipe instances, or {@code null} if no recipe type was provided
*/
public RecipeType getRecipeType() {
return this.recipeType;
}
/**
- * Constructs a workbench container menu, initializes inventory and sync state, and opens the container for the player.
+ * Initialise a workbench container menu, attach the player inventory and hotbar, and open the backing container for the player.
*
- * The constructor conditionally validates container size and attaches container data slots only when the container actually
- * contains slots (i.e., when `outputEnd >= 0` and the container size is > 0). For slotless workbenches it only verifies the
- * container is non-null with size 0. If the container has slots, workbench-specific slots are added; the player's inventory
- * and hotbar are always added. The container is opened for the provided player.
+ * If the backing container provides slots (determined by a non-negative `outputEnd` and a positive container size),
+ * container size and data slot counts are validated and the provided `data` is attached for progress syncing; when the
+ * container is slotless the constructor validates an empty container and skips slot/data attachment.
*
- * @param menuType the menu type or null for an unregistered type
- * @param syncId synchronization id for the menu
- * @param container the underlying container backing this menu
- * @param data container data used to sync progress/state (e.g., burn/cook times)
- * @param containerDataSize expected size of `data` when the container provides slots; used for data count validation
- * @param playerInventory the player's inventory to attach to this menu
- * @param inputEnd index (inclusive) of the last input slot in the container
- * @param outputEnd index (inclusive) of the last output slot in the container; if negative or container is empty,
- * the menu is treated as slotless and slot/data initialization is skipped
+ * @param menuType the menu type, or {@code null} for an unregistered type
+ * @param syncId synchronization id for this menu instance
+ * @param container the backing container for workbench slots (may be empty for slotless workbenches)
+ * @param data container data used to sync progress/state (e.g. burn/cook times)
+ * @param containerDataSize expected size of {@code data} when the container provides slots; used for validation
+ * @param playerInventory the player's inventory to attach to the menu and to open the container for
+ * @param inputEnd inclusive index of the last input slot in {@code container}
+ * @param outputEnd inclusive index of the last output slot in {@code container}; negative or empty container
+ * indicates a slotless workbench
+ * @param recipeType the recipe type used by this workbench, or {@code null} if none
*/
public AbstractWorkbenchContainerMenu(@Nullable MenuType> menuType, int syncId, Container container, ContainerData data, int containerDataSize, Inventory playerInventory, int inputEnd, int outputEnd, RecipeType recipeType) {
super(menuType, syncId);
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/menu/ArmorersWorkbenchMenu.java b/src/main/java/com/tcm/MineTale/block/workbenches/menu/ArmorersWorkbenchMenu.java
index 8f5959f..92e59d4 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/menu/ArmorersWorkbenchMenu.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/menu/ArmorersWorkbenchMenu.java
@@ -37,13 +37,13 @@ public ArmorersWorkbenchMenu(int syncId, Inventory playerInventory) {
}
/**
- * Creates a workbench menu associated with the given player inventory and optional block entity.
+ * Creates a workbench menu bound to the given player inventory and optional block entity.
*
- * Uses an empty internal container (size 0) and the class's data size for syncing numeric state.
+ * Uses an empty internal container (size 0) and the class's data size to synchronise numeric state.
*
- * @param syncId synchronization id for this menu
+ * @param syncId the synchronisation id for this menu
* @param playerInventory the player's inventory used for slot access and recipe-book integration
- * @param data container data used to sync numeric state between server and client
+ * @param data container data used to synchronise numeric state between server and client
* @param blockEntity nullable block entity this menu is bound to, or {@code null} if not bound
*/
public ArmorersWorkbenchMenu(int syncId, Inventory playerInventory, ContainerData data, @Nullable AbstractWorkbenchEntity blockEntity) {
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/menu/CampfireWorkbenchMenu.java b/src/main/java/com/tcm/MineTale/block/workbenches/menu/CampfireWorkbenchMenu.java
index 96b5512..166a2be 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/menu/CampfireWorkbenchMenu.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/menu/CampfireWorkbenchMenu.java
@@ -24,12 +24,11 @@ public class CampfireWorkbenchMenu extends AbstractWorkbenchContainerMenu {
private final AbstractWorkbenchEntity blockEntity;
/**
- * Creates a CampfireWorkbenchMenu using default internal storage and data containers.
+ * Create a CampfireWorkbenchMenu initialised with the default internal inventory and data containers.
*
- * Constructs a menu with a new SimpleContainer of size {@code containerSize} and a new
- * SimpleContainerData of size {@code containerDataSize}, then delegates to the primary constructor.
+ * The menu is constructed with a 7‑slot internal container and a data container sized by {@code containerDataSize}.
*
- * @param syncId synchronization id for the menu
+ * @param syncId the synchronization id for this menu
* @param playerInventory the player's inventory interacting with this menu
*/
public CampfireWorkbenchMenu(int syncId, Inventory playerInventory) {
@@ -37,13 +36,13 @@ public CampfireWorkbenchMenu(int syncId, Inventory playerInventory) {
}
/**
- * Constructs a CampfireWorkbenchMenu connected to the given player inventory, container, and container data.
+ * Create a CampfireWorkbenchMenu bound to the given player inventory, container and container data.
*
- * @param syncId the synchronization id for this menu used for client/server container sync
+ * @param syncId the synchronisation id used for client–server container sync
* @param playerInventory the player's inventory
- * @param container the backing container for the workbench slots
- * @param data the container data used for syncing additional numeric state
- * @param blockEntity the associated workbench block entity, or `null` if not bound to a block
+ * @param container the backing container that provides the workbench slots
+ * @param data the container data used to synchronise numeric state
+ * @param blockEntity the associated workbench block entity, or {@code null} if not bound to a block
*/
public CampfireWorkbenchMenu(int syncId, Inventory playerInventory, Container container, ContainerData data, @Nullable AbstractWorkbenchEntity blockEntity) {
super(ModMenuTypes.CAMPFIRE_WORKBENCH_MENU, syncId, container, data, containerDataSize, playerInventory, Constants.INPUT_START + 1, 6, ModRecipes.CAMPFIRE_TYPE);
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/menu/FarmersWorkbenchMenu.java b/src/main/java/com/tcm/MineTale/block/workbenches/menu/FarmersWorkbenchMenu.java
index 154cbdc..9d5484f 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/menu/FarmersWorkbenchMenu.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/menu/FarmersWorkbenchMenu.java
@@ -27,24 +27,24 @@ public class FarmersWorkbenchMenu extends AbstractWorkbenchContainerMenu {
private final Inventory playerInventory;
/**
- * Creates a client-side menu instance when the workbench UI is opened.
+ * Constructs a client-side FarmersWorkbenchMenu with no bound block entity and empty container data.
*
- * @param syncId the synchronization id used to match this menu with the server
- * @param playerInventory the player's inventory bound to this menu
+ * @param syncId the menu synchronization id used to match this client menu with its server counterpart
+ * @param playerInventory the player's inventory to bind to the menu
*/
public FarmersWorkbenchMenu(int syncId, Inventory playerInventory) {
this(syncId, playerInventory, new SimpleContainerData(EMPTY_SIZE), null);
}
/**
- * Creates a workbench menu associated with the given player inventory and optional block entity.
+ * Initialise a Farmers workbench menu bound to a player's inventory and an optional block entity.
*
- * Uses an empty internal container (size 0) and the class's data size for syncing numeric state.
+ * Uses an empty internal container and the menu's container data for server–client numeric syncing.
*
* @param syncId synchronization id for this menu
* @param playerInventory the player's inventory used for slot access and recipe-book integration
* @param data container data used to sync numeric state between server and client
- * @param blockEntity nullable block entity this menu is bound to, or {@code null} if not bound
+ * @param blockEntity the block entity this menu is bound to, or {@code null} if not bound
*/
public FarmersWorkbenchMenu(int syncId, Inventory playerInventory, ContainerData data, @Nullable AbstractWorkbenchEntity blockEntity) {
// Note: The order of arguments depends on your AbstractWorkbenchContainerMenu,
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/menu/FurnaceWorkbenchMenu.java b/src/main/java/com/tcm/MineTale/block/workbenches/menu/FurnaceWorkbenchMenu.java
index d2905dc..985edf4 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/menu/FurnaceWorkbenchMenu.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/menu/FurnaceWorkbenchMenu.java
@@ -24,7 +24,7 @@ public class FurnaceWorkbenchMenu extends AbstractWorkbenchContainerMenu {
private final AbstractFurnaceWorkbenchEntity blockEntity;
/**
- * Constructs a client-side FurnaceWorkbenchMenu backed by a new empty internal container and default container data.
+ * Create a client-side FurnaceWorkbenchMenu backed by a fresh internal container and default container data.
*
* @param syncId the window synchronization id assigned by the client
* @param playerInventory the player's inventory to attach to this menu
@@ -34,13 +34,13 @@ public FurnaceWorkbenchMenu(int syncId, Inventory playerInventory) {
}
/**
- * Creates a FurnaceWorkbenchMenu bound to the provided player inventory, container, and optional block entity.
+ * Create a menu instance for a furnace workbench bound to the supplied player inventory, container and optional block entity.
*
- * @param syncId window sync id used by the client-server menu sync
+ * @param syncId the window sync id for client-server menu synchronisation
* @param playerInventory the player's inventory
- * @param container the backing container holding slot items for this menu
- * @param data container data used for syncing menu state (e.g., progress fields)
- * @param blockEntity the associated AbstractFurnaceWorkbenchEntity instance, or {@code null} for client-side construction
+ * @param container the backing container holding this menu's slot items
+ * @param data container data used to sync menu state (for example progress fields)
+ * @param blockEntity the associated AbstractFurnaceWorkbenchEntity, or {@code null} when constructed client-side
*/
public FurnaceWorkbenchMenu(int syncId, Inventory playerInventory, Container container, ContainerData data, @Nullable AbstractFurnaceWorkbenchEntity blockEntity) {
super(ModMenuTypes.FURNACE_WORKBENCH_MENU, syncId, container, data, containerDataSize, playerInventory, Constants.INPUT_START + 1, 6, ModRecipes.FURNACE_T1_TYPE);
diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/menu/WorkbenchWorkbenchMenu.java b/src/main/java/com/tcm/MineTale/block/workbenches/menu/WorkbenchWorkbenchMenu.java
index 17ca965..3c8ddfb 100644
--- a/src/main/java/com/tcm/MineTale/block/workbenches/menu/WorkbenchWorkbenchMenu.java
+++ b/src/main/java/com/tcm/MineTale/block/workbenches/menu/WorkbenchWorkbenchMenu.java
@@ -39,14 +39,14 @@ public WorkbenchWorkbenchMenu(int syncId, Inventory playerInventory) {
}
/**
- * Creates a workbench menu associated with the given player inventory and optional block entity.
+ * Initialises a workbench menu bound to a player inventory and an optional block entity.
*
- * Uses an empty internal container (size 0) and the class's data size for syncing numeric state.
+ * The menu uses an empty internal container (size 0) and the class's data size for numeric state syncing.
*
- * @param syncId synchronization id for this menu
- * @param playerInventory the player's inventory used for slot access and recipe-book integration
- * @param data container data used to sync numeric state between server and client
- * @param blockEntity nullable block entity this menu is bound to, or {@code null} if not bound
+ * @param syncId synchronization id for this menu
+ * @param playerInventory the player's inventory used for slot access and recipe-book integration
+ * @param data container data used to sync numeric state between server and client
+ * @param blockEntity nullable block entity this menu is bound to, or {@code null} if not bound
*/
public WorkbenchWorkbenchMenu(int syncId, Inventory playerInventory, ContainerData data, @Nullable AbstractWorkbenchEntity blockEntity) { // Note: The order of arguments depends on your AbstractWorkbenchContainerMenu,
// but the 'expectedSize' parameter MUST be 0.
@@ -76,11 +76,10 @@ public WorkbenchWorkbenchMenu(int syncId, Inventory playerInventory, ContainerDa
}
/**
- * Populate the provided StackedItemContents with all item stacks available to the crafting UI.
+ * Populate the given StackedItemContents with all item stacks available to the crafting UI.
*
- * Accounts for items in the player's pockets, items stored in this menu's internal container,
- * and nearby items delivered by the server (network-synced). When running on the client and
- * networked nearby items are present, a debug message may be printed.
+ * Accounts for items in the player's inventory, items in this menu's internal container,
+ * and nearby item stacks synchronised from the server so the recipe book can consider them.
*
* @param contents the StackedItemContents to populate with available item stacks
*/