Skip to content

Commit bda5b65

Browse files
committed
Add PrepareRecipeEvent + a few extra goodies.
1 parent 6cdb485 commit bda5b65

File tree

4 files changed

+206
-2
lines changed

4 files changed

+206
-2
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2023-2025 Fox2Code
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package com.fox2code.foxloader.event.recipe;
25+
26+
import com.fox2code.foxevents.Event;
27+
import net.minecraft.common.block.container.Container;
28+
import net.minecraft.common.entity.inventory.InventoryCrafting;
29+
import net.minecraft.common.entity.player.EntityPlayer;
30+
import net.minecraft.common.item.ItemStack;
31+
import net.minecraft.common.recipe.IRecipe;
32+
import org.jetbrains.annotations.NotNull;
33+
import org.jetbrains.annotations.Nullable;
34+
35+
import java.util.Collections;
36+
import java.util.Objects;
37+
import java.util.Set;
38+
39+
/**
40+
* Called when a recipe is being prepared.
41+
*/
42+
public final class PrepareRecipeEvent extends Event implements Event.Cancellable {
43+
private final IRecipe recipe;
44+
private final InventoryCrafting inventory;
45+
private ItemStack result;
46+
47+
public PrepareRecipeEvent(@Nullable IRecipe recipe,@NotNull InventoryCrafting inventory,@Nullable ItemStack result) {
48+
this.recipe = recipe;
49+
this.inventory = Objects.requireNonNull(inventory, "inventory");
50+
this.result = result;
51+
}
52+
53+
@Nullable public IRecipe getRecipe() {
54+
return this.recipe;
55+
}
56+
57+
@NotNull public InventoryCrafting getInventory() {
58+
return this.inventory;
59+
}
60+
61+
@Nullable public ItemStack getResult() {
62+
return this.result;
63+
}
64+
65+
public void setResult(@Nullable ItemStack result) {
66+
this.result = result;
67+
}
68+
69+
@Nullable public ItemStack getEffectiveResult() {
70+
return this.isCancelled() ? null : this.result;
71+
}
72+
73+
/**
74+
* @return the current viewers of the crafting process, can be an empty set, especially on automated machines.
75+
*/
76+
@NotNull public Set<EntityPlayer> getRecipeViewers() {
77+
Container container = this.inventory.getCraftingContainer();
78+
Set<EntityPlayer> entityPlayers;
79+
if (container == null || (entityPlayers = container.getActiveViewers()) == null) {
80+
return Collections.emptySet();
81+
}
82+
return entityPlayers;
83+
}
84+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2023-2025 Fox2Code
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package com.fox2code.foxloader.internal;
25+
26+
import com.fox2code.foxevents.EventHolder;
27+
import com.fox2code.foxloader.event.recipe.PrepareRecipeEvent;
28+
import net.minecraft.common.entity.inventory.InventoryCrafting;
29+
import net.minecraft.common.item.ItemStack;
30+
import net.minecraft.common.recipe.IRecipe;
31+
32+
public final class InternalRecipeHooks {
33+
private static final EventHolder<PrepareRecipeEvent> PREPARE_RECIPE_EVENT =
34+
EventHolder.getHolderFromEvent(PrepareRecipeEvent.class);
35+
36+
private InternalRecipeHooks() {}
37+
38+
public static ItemStack onWorkbenchRecipe(ItemStack itemStack, InventoryCrafting inventoryCrafting) {
39+
if (PREPARE_RECIPE_EVENT.isEmpty()) {
40+
return itemStack;
41+
}
42+
PrepareRecipeEvent prepareRecipeEvent =
43+
new PrepareRecipeEvent(null, inventoryCrafting, itemStack);
44+
prepareRecipeEvent.callEvent();
45+
return prepareRecipeEvent.getEffectiveResult();
46+
}
47+
48+
public static ItemStack onWorkbenchRecipe(IRecipe recipe, InventoryCrafting inventoryCrafting) {
49+
if (PREPARE_RECIPE_EVENT.isEmpty()) {
50+
return recipe.getCraftingResult(inventoryCrafting);
51+
}
52+
PrepareRecipeEvent prepareRecipeEvent =
53+
new PrepareRecipeEvent(recipe, inventoryCrafting,
54+
recipe.getCraftingResult(inventoryCrafting));
55+
prepareRecipeEvent.callEvent();
56+
return prepareRecipeEvent.getEffectiveResult();
57+
}
58+
}

patching/src/main/java/com/fox2code/foxloader/patching/game/ContainerPatch.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,16 @@ final class ContainerPatch extends GamePatch {
3333
private static final String ContainerManager = "com/fox2code/foxloader/container/ContainerManager";
3434

3535
ContainerPatch() {
36-
super(new String[]{EntityPlayer, EntityPlayerMP});
36+
super(new String[]{Container, EntityPlayer, EntityPlayerMP});
3737
}
3838

3939
@Override
4040
public ClassNode transform(ClassNode classNode) {
4141
switch (classNode.name) {
42+
case Container: {
43+
patchContainer(classNode);
44+
break;
45+
}
4246
case EntityPlayer: {
4347
patchEntityPlayer(classNode);
4448
break;
@@ -51,6 +55,17 @@ public ClassNode transform(ClassNode classNode) {
5155
return classNode;
5256
}
5357

58+
private void patchContainer(ClassNode classNode) {
59+
FieldNode fieldNode = TransformerUtils.getField(classNode, "playersList");
60+
MethodNode methodNode = new MethodNode(ACC_PUBLIC | ACC_FINAL,
61+
"getActiveViewers", "()" + fieldNode.desc, "()" + fieldNode.signature, null);
62+
methodNode.instructions.add(new VarInsnNode(ALOAD, 0));
63+
methodNode.instructions.add(new FieldInsnNode(GETFIELD,
64+
classNode.name, fieldNode.name, fieldNode.desc));
65+
methodNode.instructions.add(new InsnNode(ARETURN));
66+
classNode.methods.add(methodNode);
67+
}
68+
5469
private static void patchEntityPlayer(ClassNode classNode) {
5570
MethodNode openFoxLoaderContainer = new MethodNode(ACC_PUBLIC,
5671
"openFoxLoaderContainer", "(L" + Container + ";III)Z", null, null);

patching/src/main/java/com/fox2code/foxloader/patching/game/RecipesPatch.java

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,30 @@
3131
import java.util.Collections;
3232

3333
final class RecipesPatch extends GamePatch {
34+
private static final String ItemStack = "net/minecraft/common/item/ItemStack";
3435
private static final String Items = "net/minecraft/common/item/Items";
36+
private static final String Container = "net/minecraft/common/block/container/Container";
37+
private static final String InventoryCrafting = "net/minecraft/common/entity/inventory/InventoryCrafting";
38+
private static final String IRecipe = "net/minecraft/common/recipe/IRecipe";
3539
private static final String ShapedRecipes = "net/minecraft/common/recipe/ShapedRecipes";
3640
private static final String ShapelessRecipes = "net/minecraft/common/recipe/ShapelessRecipes";
41+
private static final String CraftingManager = "net/minecraft/common/recipe/CraftingManager";
3742
private static final String RecipesDyes = "net/minecraft/common/recipe/RecipesDyes";
3843
private static final String TaggedIngredient = "net/minecraft/common/recipe/TaggedIngredient";
3944
private static final String FoxTaggedIngredients = "com/fox2code/foxloader/recipe/FoxTaggedIngredients";
45+
private static final String InternalRecipeHooks = "com/fox2code/foxloader/internal/InternalRecipeHooks";
4046

4147
RecipesPatch() {
42-
super(new String[]{ShapedRecipes, ShapelessRecipes, RecipesDyes});
48+
super(new String[]{InventoryCrafting, ShapedRecipes, ShapelessRecipes, CraftingManager, RecipesDyes});
4349
}
4450

4551
@Override
4652
public ClassNode transform(ClassNode classNode) {
4753
switch (classNode.name) {
54+
case InventoryCrafting: {
55+
patchInventoryCrafting(classNode);
56+
break;
57+
}
4858
case ShapedRecipes: {
4959
TransformerUtils.makeGetterForFields(classNode, "width", "height", "ingredients");
5060
break;
@@ -53,6 +63,10 @@ public ClassNode transform(ClassNode classNode) {
5363
TransformerUtils.makeGetterForFields(classNode, "ingredients");
5464
break;
5565
}
66+
case CraftingManager: {
67+
patchCraftingManager(classNode);
68+
break;
69+
}
5670
case RecipesDyes: {
5771
patchAllDyeRecipes(classNode);
5872
break;
@@ -61,6 +75,39 @@ public ClassNode transform(ClassNode classNode) {
6175
return classNode;
6276
}
6377

78+
private void patchInventoryCrafting(ClassNode classNode) {
79+
FieldNode fieldNode = TransformerUtils.getFieldDesc(classNode, "L" + Container + ";");
80+
MethodNode methodNode = new MethodNode(ACC_PUBLIC,
81+
"getCraftingContainer", "()" + fieldNode.desc, null, null);
82+
methodNode.instructions.add(new VarInsnNode(ALOAD, 0));
83+
methodNode.instructions.add(new FieldInsnNode(GETFIELD,
84+
classNode.name, fieldNode.name, fieldNode.desc));
85+
methodNode.instructions.add(new InsnNode(ARETURN));
86+
classNode.methods.add(methodNode);
87+
}
88+
89+
private void patchCraftingManager(ClassNode classNode) {
90+
MethodNode methodNode = TransformerUtils.getMethod(classNode, "findMatchingRecipe");
91+
for (AbstractInsnNode abstractInsnNode : methodNode.instructions.toArray()) {
92+
if (abstractInsnNode.getOpcode() == ARETURN) {
93+
AbstractInsnNode previous = abstractInsnNode.getPrevious();
94+
if (previous.getOpcode() == INVOKEINTERFACE &&
95+
((MethodInsnNode) previous).owner.equals(IRecipe)) {
96+
methodNode.instructions.remove(previous);
97+
methodNode.instructions.insertBefore(abstractInsnNode, new MethodInsnNode(
98+
INVOKESTATIC, InternalRecipeHooks, "onWorkbenchRecipe",
99+
"(L" + IRecipe + ";L" + InventoryCrafting + ";)L" + ItemStack + ";"));
100+
} else if (previous.getOpcode() != ACONST_NULL) {
101+
methodNode.instructions.insertBefore(
102+
abstractInsnNode, new VarInsnNode(ALOAD, 1));
103+
methodNode.instructions.insertBefore(abstractInsnNode, new MethodInsnNode(
104+
INVOKESTATIC, InternalRecipeHooks, "onWorkbenchRecipe",
105+
"(L" + ItemStack + ";L" + InventoryCrafting + ";)L" + ItemStack + ";"));
106+
}
107+
}
108+
}
109+
}
110+
64111
private static void patchAllDyeRecipes(ClassNode classNode) {
65112
for (MethodNode methodNode : classNode.methods) {
66113
ArrayList<StaticDyeOccurrence> staticDyeOccurrences = new ArrayList<>();

0 commit comments

Comments
 (0)