22
33import java .util .ArrayList ;
44import java .util .List ;
5+ import java .util .Optional ;
56
67import org .jspecify .annotations .Nullable ;
78
9+ import com .tcm .MineTale .recipe .WorkbenchRecipe ;
10+ import com .tcm .MineTale .recipe .WorkbenchRecipeInput ;
11+ import com .tcm .MineTale .util .Constants ;
12+
813import net .minecraft .core .BlockPos ;
914import net .minecraft .network .chat .Component ;
1015import net .minecraft .world .Container ;
1419import net .minecraft .world .entity .player .Player ;
1520import net .minecraft .world .inventory .AbstractContainerMenu ;
1621import net .minecraft .world .item .ItemStack ;
22+ import net .minecraft .world .item .crafting .RecipeHolder ;
23+ import net .minecraft .world .item .crafting .RecipeType ;
24+ import net .minecraft .world .level .Level ;
1725import net .minecraft .world .level .block .entity .BlockEntity ;
1826import net .minecraft .world .level .block .entity .BlockEntityType ;
1927import net .minecraft .world .level .block .state .BlockState ;
@@ -22,6 +30,11 @@ public abstract class AbstractWorkbenchEntity extends BlockEntity implements Men
2230 protected int tier = 1 ;
2331 protected double scanRadius = 5.0 ;
2432
33+ // Slot Mapping: 0-1 Inputs, 2 Fuel, 3-6 Outputs
34+ protected final SimpleContainer inventory = new SimpleContainer (7 );
35+ protected int progress = 0 ;
36+ protected int maxProgress = 200 ;
37+
2538 /**
2639 * Creates a new workbench block entity instance.
2740 *
@@ -34,17 +47,178 @@ public AbstractWorkbenchEntity(BlockEntityType<?> type, BlockPos pos, BlockState
3447 }
3548
3649 /**
37- * Gets the workstation's current tier.
38- *
39- * @return the current tier value
40- */
41- public int getTier () { return tier ; }
50+ * Subclasses (Campfire/Furnace) must define which recipe type they look for.
51+ */
52+ public abstract RecipeType <WorkbenchRecipe > getWorkbenchRecipeType ();
53+
54+ public static void tick (Level level , BlockPos pos , BlockState state , AbstractWorkbenchEntity entity ) {
55+ // 1. Create the input wrapper using the internal SimpleContainer
56+ // Slot 1 = Input A, Slot 2 = Input B
57+ WorkbenchRecipeInput input = new WorkbenchRecipeInput (
58+ entity .inventory .getItem (Constants .INPUT_1 ),
59+ entity .inventory .getItem (Constants .INPUT_2 )
60+ );
61+
62+ // DEBUG 1: Is the machine even seeing the pork?
63+ if (!entity .inventory .getItem (Constants .INPUT_1 ).isEmpty ()) {
64+ System .out .println ("Slot 1 (Input) contains: " + entity .inventory .getItem (Constants .INPUT_1 ).getItem ().toString ());
65+ }
66+
67+ if (!entity .inventory .getItem (Constants .FUEL_SLOT ).isEmpty ()) {
68+ System .out .println ("Slot 0 (Fuel) contains: " + entity .inventory .getItem (Constants .FUEL_SLOT ).getItem ().toString ());
69+ }
70+
71+ // 2. Fetch the RecipeManager from the server
72+ if (level .getServer () == null ) return ;
73+ var recipeManager = level .getServer ().getRecipeManager ();
74+
75+ // 3. Lookup the recipe using our explicit generic types
76+ Optional <RecipeHolder <WorkbenchRecipe >> recipeHolder = recipeManager
77+ .getRecipeFor (entity .getWorkbenchRecipeType (), input , level );
78+
79+ if (recipeHolder .isPresent ()) {
80+ WorkbenchRecipe recipe = recipeHolder .get ().value ();
81+ entity .maxProgress = recipe .cookTime ();
82+
83+ if (!entity .hasFuel ()) {
84+ System .out .println ("DEBUG: Failed because hasFuel() is false." );
85+ }
86+ if (!entity .canFitOutputs (recipeHolder .get ().value ().results ())) {
87+ System .out .println ("DEBUG: Failed because outputs are full." );
88+ }
89+
90+ boolean hasFuel = entity .hasFuel ();
91+ boolean canFit = entity .canFitOutputs (recipe .results ());
92+
93+ if (hasFuel && canFit ) {
94+ entity .progress ++;
95+ // Only print every 20 ticks (1 second) to avoid console spam
96+ if (entity .progress % 20 == 0 ) {
97+ System .out .println ("DEBUG: Cooking... Progress is now " + entity .progress );
98+ }
99+
100+ setChanged (level , pos , state );
101+
102+ if (entity .progress >= entity .maxProgress ) {
103+ System .out .println ("DEBUG: Progress complete! Triggering craft()." );
104+ entity .craft (recipe );
105+ entity .progress = 0 ;
106+ }
107+ } else {
108+ // This tells us exactly WHY it stopped
109+ if (!hasFuel ) {
110+ System .out .println ("DEBUG: Cooking stalled - NO FUEL (Check LIT state or Fuel Slot)" );
111+ }
112+ if (!canFit ) {
113+ System .out .println ("DEBUG: Cooking stalled - NO SPACE in output slots (3-6)" );
114+ }
115+ }
116+ } else {
117+ if (!input .getItem (0 ).isEmpty ()) {
118+ System .out .println ("DEBUG: No recipe found for this input type: " + entity .getWorkbenchRecipeType ().toString ());
119+ }
120+ // Reset progress if ingredients are removed
121+ if (entity .progress > 0 ) {
122+ entity .progress = 0 ;
123+ setChanged (level , pos , state );
124+ }
125+ }
126+ }
127+
128+ public ItemStack getItem (int slot ) { return this .inventory .getItem (slot ); }
129+
130+ public boolean canFitOutputs (List <ItemStack > results ) {
131+ for (ItemStack result : results ) {
132+ // If we can't find a home for even one of the results, return false
133+ if (findOutputSlot (result , Constants .OUTPUT_START , Constants .OUTPUT_END ) == -1 ) {
134+ return false ;
135+ }
136+ }
137+ return true ;
138+ }
139+
140+ public int findOutputSlot (ItemStack result , int start , int end ) {
141+ for (int i = start ; i <= end ; i ++) {
142+ ItemStack stack = getItem (i );
143+ if (stack .isEmpty ()) return i ;
144+
145+ // 1.21.1 Check: Same item + same components + space for more
146+ if (ItemStack .isSameItemSameComponents (stack , result ) &&
147+ stack .getCount () + result .getCount () <= stack .getMaxStackSize ()) {
148+ return i ;
149+ }
150+ }
151+ return -1 ;
152+ }
153+
154+ public int getContainerSize () {
155+ return this .inventory .getContainerSize ();
156+ }
157+
158+ public boolean isEmpty () {
159+ return this .inventory .isEmpty ();
160+ }
161+
162+ protected void craft (WorkbenchRecipe recipe ) {
163+ // 1. Consume 1 from each ingredient slot (Slots 1 and 2)
164+ this .removeItem (Constants .INPUT_1 , 1 );
165+ this .removeItem (Constants .INPUT_2 , 1 );
166+
167+ // 2. Distribute results from the recipe
168+ for (ItemStack result : recipe .results ()) {
169+ if (result .isEmpty ()) continue ;
170+
171+ // Use the constants for the output range (3 to 6)
172+ int slot = findOutputSlot (result , Constants .OUTPUT_START , Constants .OUTPUT_END );
173+
174+ if (slot != -1 ) {
175+ ItemStack existing = getItem (slot );
176+ if (existing .isEmpty ()) {
177+ setItem (slot , result .copy ());
178+ } else {
179+ existing .grow (result .getCount ());
180+ // Crucial: SimpleContainer needs to know the stack changed
181+ this .setChanged ();
182+ }
183+ }
184+ }
185+ }
186+
187+ public ItemStack removeItem (int slot , int amount ) {
188+ // SimpleContainer has its own removeItem logic built-in
189+ ItemStack result = this .inventory .removeItem (slot , amount );
190+ if (!result .isEmpty ()) {
191+ this .setChanged ();
192+ }
193+ return result ;
194+ }
195+
196+ public void setItem (int slot , ItemStack stack ) {
197+ // Use setItem(), not set()
198+ this .inventory .setItem (slot , stack );
199+
200+ // Check max stack size
201+ if (!stack .isEmpty () && stack .getCount () > stack .getMaxStackSize ()) {
202+ stack .setCount (stack .getMaxStackSize ());
203+ }
204+ this .setChanged ();
205+ }
206+
207+ // Subclasses handle fuel logic (Campfires might return true always, Furnaces check slot 2)
208+ protected abstract boolean hasFuel ();
209+
42210 /**
43- * Sets the workstation's tier and marks the block entity as changed.
44- *
45- * @param tier the new tier value for this workstation
46- */
47- public void setTier (int tier ) { this .tier = tier ; setChanged (); }
211+ * Gets the workstation's current tier.
212+ *
213+ * @return the current tier value
214+ */
215+ public int getTier () { return tier ; }
216+ /**
217+ * Sets the workstation's tier and marks the block entity as changed.
218+ *
219+ * @param tier the new tier value for this workstation
220+ */
221+ public void setTier (int tier ) { this .tier = tier ; setChanged (); }
48222
49223 /**
50224 * Collects nearby inventory-containing block entities within the configured scan radius and vertical range.
0 commit comments