Skip to content

Commit 5614392

Browse files
committed
feat(events): Add CookingEvent.Recipe for recipe books
Since recipe books also have cooking events, these transactions are typically logged as warnings being uncaught and no events being thrown.
1 parent be38452 commit 5614392

10 files changed

Lines changed: 261 additions & 25 deletions

File tree

forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/network/ServerGamePacketListenerImplMixin_Forge.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,21 @@
2424
*/
2525
package org.spongepowered.forge.mixin.core.server.network;
2626

27+
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
28+
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
2729
import net.minecraft.network.protocol.game.ServerGamePacketListener;
2830
import net.minecraft.server.network.ServerGamePacketListenerImpl;
2931
import net.minecraft.world.inventory.RecipeBookMenu;
3032
import net.minecraft.world.item.crafting.RecipeHolder;
33+
import net.minecraft.world.level.block.entity.BlockEntity;
3134
import org.checkerframework.checker.nullness.qual.NonNull;
3235
import org.spongepowered.api.item.inventory.Inventory;
3336
import org.spongepowered.api.item.inventory.crafting.CraftingInventory;
3437
import org.spongepowered.api.item.inventory.query.QueryTypes;
3538
import org.spongepowered.asm.mixin.Mixin;
3639
import org.spongepowered.asm.mixin.injection.At;
37-
import org.spongepowered.asm.mixin.injection.Redirect;
3840
import org.spongepowered.common.SpongeCommon;
41+
import org.spongepowered.common.accessor.world.inventory.AbstractFurnaceMenuAccessor;
3942
import org.spongepowered.common.event.tracking.PhaseContext;
4043
import org.spongepowered.common.event.tracking.PhaseTracker;
4144
import org.spongepowered.common.event.tracking.context.transaction.EffectTransactor;
@@ -45,21 +48,32 @@
4548
public abstract class ServerGamePacketListenerImplMixin_Forge implements ServerGamePacketListener {
4649

4750
@SuppressWarnings({"unchecked", "rawtypes"})
48-
@Redirect(method = "lambda$handlePlaceRecipe$10",
51+
@WrapOperation(method = "lambda$handlePlaceRecipe$10",
4952
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/inventory/RecipeBookMenu;handlePlacement(ZLnet/minecraft/world/item/crafting/RecipeHolder;Lnet/minecraft/server/level/ServerPlayer;)V"))
50-
private void forge$onPlaceRecipe(final RecipeBookMenu recipeBookMenu, final boolean shift, final RecipeHolder recipe, final net.minecraft.server.level.ServerPlayer player) {
53+
private void forge$onPlaceRecipe(
54+
final RecipeBookMenu recipeBookMenu, final boolean shift, final RecipeHolder recipe, final net.minecraft.server.level.ServerPlayer player,
55+
final Operation<Void> original
56+
) {
5157
final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext();
5258
final TransactionalCaptureSupplier transactor = context.getTransactor();
5359

60+
final var menu = (Inventory) player.containerMenu;
61+
if (menu instanceof AbstractFurnaceMenuAccessor afm && afm.accessor$container() instanceof BlockEntity be) {
62+
try (final EffectTransactor ignored = transactor.logPlaceCookingRecipe(be, shift, recipe, player)) {
63+
original.call(recipeBookMenu, shift, recipe, player);
64+
player.containerMenu.broadcastChanges();
65+
return;
66+
}
67+
}
5468
final Inventory craftInv = ((Inventory) player.containerMenu).query(QueryTypes.INVENTORY_TYPE.get().of(CraftingInventory.class));
5569
if (!(craftInv instanceof CraftingInventory)) {
56-
recipeBookMenu.handlePlacement(shift, recipe, player);
70+
original.call(recipeBookMenu, shift, recipe, player);
5771
SpongeCommon.logger().warn("Detected crafting without a InventoryCrafting!? Crafting Event will not fire.");
5872
return;
5973
}
6074

6175
try (final EffectTransactor ignored = transactor.logPlaceRecipe(shift, recipe, player, (CraftingInventory) craftInv)) {
62-
recipeBookMenu.handlePlacement(shift, recipe, player);
76+
original.call(recipeBookMenu, shift, recipe, player);
6377
player.containerMenu.broadcastChanges();
6478
}
6579
}

neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/server/network/ServerGamePacketListenerImplMixin_Neo.java

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,21 @@
2424
*/
2525
package org.spongepowered.neoforge.mixin.core.server.network;
2626

27+
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
28+
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
2729
import net.minecraft.network.protocol.game.ServerGamePacketListener;
2830
import net.minecraft.server.network.ServerGamePacketListenerImpl;
2931
import net.minecraft.world.inventory.RecipeBookMenu;
3032
import net.minecraft.world.item.crafting.RecipeHolder;
33+
import net.minecraft.world.level.block.entity.BlockEntity;
3134
import org.checkerframework.checker.nullness.qual.NonNull;
3235
import org.spongepowered.api.item.inventory.Inventory;
3336
import org.spongepowered.api.item.inventory.crafting.CraftingInventory;
3437
import org.spongepowered.api.item.inventory.query.QueryTypes;
3538
import org.spongepowered.asm.mixin.Mixin;
3639
import org.spongepowered.asm.mixin.injection.At;
37-
import org.spongepowered.asm.mixin.injection.Redirect;
3840
import org.spongepowered.common.SpongeCommon;
41+
import org.spongepowered.common.accessor.world.inventory.AbstractFurnaceMenuAccessor;
3942
import org.spongepowered.common.event.tracking.PhaseContext;
4043
import org.spongepowered.common.event.tracking.PhaseTracker;
4144
import org.spongepowered.common.event.tracking.context.transaction.EffectTransactor;
@@ -45,21 +48,35 @@
4548
public abstract class ServerGamePacketListenerImplMixin_Neo implements ServerGamePacketListener {
4649

4750
@SuppressWarnings({"unchecked", "rawtypes"})
48-
@Redirect(method = "lambda$handlePlaceRecipe$10",
51+
@WrapOperation(method = "lambda$handlePlaceRecipe$10",
4952
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/inventory/RecipeBookMenu;handlePlacement(ZLnet/minecraft/world/item/crafting/RecipeHolder;Lnet/minecraft/server/level/ServerPlayer;)V"))
50-
private void neo$onPlaceRecipe(final RecipeBookMenu recipeBookMenu, final boolean shift, final RecipeHolder recipe, final net.minecraft.server.level.ServerPlayer player) {
53+
private void neo$onPlaceRecipe(
54+
final RecipeBookMenu recipeBookMenu,
55+
final boolean shift,
56+
final RecipeHolder recipe,
57+
final net.minecraft.server.level.ServerPlayer player,
58+
final Operation<Void> original
59+
) {
5160
final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext();
5261
final TransactionalCaptureSupplier transactor = context.getTransactor();
5362

63+
final var menu = (Inventory) player.containerMenu;
64+
if (menu instanceof AbstractFurnaceMenuAccessor afm && afm.accessor$container() instanceof BlockEntity be) {
65+
try (final EffectTransactor ignored = transactor.logPlaceCookingRecipe(be, shift, recipe, player)) {
66+
original.call(recipeBookMenu, shift, recipe, player);
67+
player.containerMenu.broadcastChanges();
68+
return;
69+
}
70+
}
5471
final Inventory craftInv = ((Inventory) player.containerMenu).query(QueryTypes.INVENTORY_TYPE.get().of(CraftingInventory.class));
5572
if (!(craftInv instanceof CraftingInventory)) {
56-
recipeBookMenu.handlePlacement(shift, recipe, player);
73+
original.call(recipeBookMenu, shift, recipe, player);
5774
SpongeCommon.logger().warn("Detected crafting without a InventoryCrafting!? Crafting Event will not fire.");
5875
return;
5976
}
6077

6178
try (final EffectTransactor ignored = transactor.logPlaceRecipe(shift, recipe, player, (CraftingInventory) craftInv)) {
62-
recipeBookMenu.handlePlacement(shift, recipe, player);
79+
original.call(recipeBookMenu, shift, recipe, player);
6380
player.containerMenu.broadcastChanges();
6481
}
6582
}

src/main/java/org/spongepowered/common/event/tracking/context/transaction/TransactionSink.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@
8484
import org.spongepowered.common.event.tracking.context.transaction.inventory.ExplicitInventoryOmittedTransaction;
8585
import org.spongepowered.common.event.tracking.context.transaction.inventory.InventoryTransaction;
8686
import org.spongepowered.common.event.tracking.context.transaction.inventory.OpenMenuTransaction;
87-
import org.spongepowered.common.event.tracking.context.transaction.inventory.PlaceRecipeTransaction;
87+
import org.spongepowered.common.event.tracking.context.transaction.inventory.PlaceCookingRecipeTransaction;
88+
import org.spongepowered.common.event.tracking.context.transaction.inventory.PlaceCraftingRecipeTransaction;
8889
import org.spongepowered.common.event.tracking.context.transaction.inventory.PlayerInventoryTransaction;
8990
import org.spongepowered.common.event.tracking.context.transaction.inventory.SelectTradeTransaction;
9091
import org.spongepowered.common.event.tracking.context.transaction.inventory.SetCarriedItemTransaction;
@@ -377,7 +378,13 @@ default EffectTransactor logCloseInventory(final PhaseContext<@NonNull ?> curren
377378
}
378379

379380
default EffectTransactor logPlaceRecipe(final boolean shift, final RecipeHolder<Recipe<?>> recipe, final ServerPlayer player, final CraftingInventory craftInv) {
380-
final PlaceRecipeTransaction transaction = new PlaceRecipeTransaction(player, shift, recipe, craftInv);
381+
final PlaceCraftingRecipeTransaction transaction = new PlaceCraftingRecipeTransaction(player, shift, recipe, craftInv);
382+
this.logTransaction(transaction);
383+
return this.pushEffect(new ResultingTransactionBySideEffect(InventoryEffect.getInstance()));
384+
}
385+
386+
default EffectTransactor logPlaceCookingRecipe(final BlockEntity blockEntity, boolean shift, final RecipeHolder<?> recipe, final ServerPlayer player) {
387+
final PlaceCookingRecipeTransaction transaction = new PlaceCookingRecipeTransaction(blockEntity, player, shift, recipe);
381388
this.logTransaction(transaction);
382389
return this.pushEffect(new ResultingTransactionBySideEffect(InventoryEffect.getInstance()));
383390
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* This file is part of Sponge, licensed under the MIT License (MIT).
3+
*
4+
* Copyright (c) SpongePowered <https://www.spongepowered.org>
5+
* Copyright (c) contributors
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
package org.spongepowered.common.event.tracking.context.transaction.inventory;
26+
27+
import net.minecraft.network.protocol.game.ServerboundPlaceRecipePacket;
28+
import net.minecraft.server.level.ServerPlayer;
29+
import net.minecraft.world.item.crafting.RecipeHolder;
30+
import net.minecraft.world.level.block.entity.BlockEntity;
31+
import org.checkerframework.checker.nullness.qual.NonNull;
32+
import org.checkerframework.checker.nullness.qual.Nullable;
33+
import org.spongepowered.api.ResourceKey;
34+
import org.spongepowered.api.data.Transaction;
35+
import org.spongepowered.api.entity.Entity;
36+
import org.spongepowered.api.event.Cause;
37+
import org.spongepowered.api.event.SpongeEventFactory;
38+
import org.spongepowered.api.event.block.entity.CookingEvent;
39+
import org.spongepowered.api.event.item.inventory.container.ClickContainerEvent;
40+
import org.spongepowered.api.item.inventory.Container;
41+
import org.spongepowered.api.item.inventory.ItemStackSnapshot;
42+
import org.spongepowered.api.item.inventory.transaction.SlotTransaction;
43+
import org.spongepowered.api.item.recipe.cooking.CookingRecipe;
44+
import org.spongepowered.common.event.tracking.PhaseContext;
45+
import org.spongepowered.common.event.tracking.phase.packet.inventory.InventoryPacketContext;
46+
import org.spongepowered.common.item.util.ItemStackUtil;
47+
48+
import java.util.List;
49+
import java.util.Optional;
50+
51+
public class PlaceCookingRecipeTransaction extends ContainerBasedTransaction {
52+
53+
private final ServerPlayer player;
54+
private final ItemStackSnapshot originalCursor;
55+
private final BlockEntity blockEntity;
56+
private final boolean shift;
57+
private RecipeHolder<?> recipe;
58+
59+
public PlaceCookingRecipeTransaction(
60+
final BlockEntity blockEntity,
61+
final ServerPlayer player,
62+
boolean shift, final RecipeHolder<?> recipe
63+
) {
64+
super(player.containerMenu);
65+
this.blockEntity = blockEntity;
66+
this.player = player;
67+
this.originalCursor = ItemStackUtil.snapshotOf(player.containerMenu.getCarried());
68+
this.recipe = recipe;
69+
this.shift = shift;
70+
}
71+
72+
@Override
73+
Optional<ClickContainerEvent> createInventoryEvent(
74+
final List<SlotTransaction> slotTransactions, final List<Entity> entities,
75+
final PhaseContext<@NonNull ?> context,
76+
final Cause cause
77+
) {
78+
final Transaction<ItemStackSnapshot> cursorTransaction = new Transaction<>(this.originalCursor, ItemStackUtil.snapshotOf(this.player.containerMenu.getCarried()));
79+
if (this.shift) {
80+
return Optional.of(
81+
SpongeEventFactory.createCookingEventRecipeAll(cause,
82+
(org.spongepowered.api.block.entity.BlockEntity) this.blockEntity,
83+
(Container) this.menu, cursorTransaction, Optional.empty(),
84+
Optional.of(this.recipe).map(RecipeHolder::value).map(CookingRecipe.class::cast),
85+
Optional.of(this.recipe).map(RecipeHolder::id).map(ResourceKey.class::cast),
86+
Optional.empty(), slotTransactions)
87+
);
88+
}
89+
final CookingEvent.Recipe event = SpongeEventFactory.createCookingEventRecipeSingle(cause,
90+
(org.spongepowered.api.block.entity.BlockEntity) this.blockEntity,
91+
(Container) this.menu, cursorTransaction, Optional.empty(),
92+
Optional.of(this.recipe).map(RecipeHolder::value).map(CookingRecipe.class::cast),
93+
Optional.of(this.recipe).map(RecipeHolder::id).map(ResourceKey.class::cast),
94+
Optional.empty(), slotTransactions);
95+
return Optional.of(event);
96+
}
97+
98+
@Override
99+
public void restore(final PhaseContext<@NonNull ?> context, final ClickContainerEvent event) {
100+
this.handleEventResults(this.player, event);
101+
}
102+
103+
@Override
104+
public void postProcessEvent(PhaseContext<@NonNull ?> context, ClickContainerEvent event) {
105+
this.handleEventResults(this.player, event);
106+
}
107+
108+
@Override
109+
boolean isContainerEventAllowed(final PhaseContext<@Nullable ?> context) {
110+
if (!(context instanceof InventoryPacketContext)) {
111+
return false;
112+
}
113+
final int containerId = ((InventoryPacketContext) context).<ServerboundPlaceRecipePacket>getPacket().getContainerId();
114+
return containerId != this.player.containerMenu.containerId;
115+
}
116+
}

src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/PlaceRecipeTransaction.java renamed to src/main/java/org/spongepowered/common/event/tracking/context/transaction/inventory/PlaceCraftingRecipeTransaction.java

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

2727
import net.minecraft.network.protocol.game.ServerboundPlaceRecipePacket;
2828
import net.minecraft.server.level.ServerPlayer;
29-
import net.minecraft.world.item.crafting.Recipe;
3029
import net.minecraft.world.item.crafting.RecipeHolder;
3130
import org.checkerframework.checker.nullness.qual.NonNull;
3231
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -48,15 +47,15 @@
4847
import java.util.List;
4948
import java.util.Optional;
5049

51-
public class PlaceRecipeTransaction extends ContainerBasedTransaction {
50+
public class PlaceCraftingRecipeTransaction extends ContainerBasedTransaction {
5251

5352
private final ServerPlayer player;
5453
private final ItemStackSnapshot originalCursor;
5554
private boolean shift;
56-
private RecipeHolder<Recipe<?>> recipe;
55+
private RecipeHolder<?> recipe;
5756
private CraftingInventory craftingInventory;
5857

59-
public PlaceRecipeTransaction(final ServerPlayer player, final boolean shift, final RecipeHolder<Recipe<?>> recipe, CraftingInventory craftingInventory) {
58+
public PlaceCraftingRecipeTransaction(final ServerPlayer player, final boolean shift, final RecipeHolder<?> recipe, CraftingInventory craftingInventory) {
6059
super(player.containerMenu);
6160
this.player = player;
6261
this.originalCursor = ItemStackUtil.snapshotOf(player.containerMenu.getCarried());

src/main/java/org/spongepowered/common/event/tracking/phase/player/PlayerInteractContext.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
public final class PlayerInteractContext extends PhaseContext<PlayerInteractContext> {
3636

3737
private @Nullable ServerLocation containerLocation;
38+
private @Nullable IPhaseState<?> parentState;
39+
private @Nullable PhaseContext<? extends PhaseContext<?>> parentContext;
3840

3941
PlayerInteractContext(final IPhaseState<PlayerInteractContext> state, final PhaseTracker tracker) {
4042
super(state, tracker);
@@ -54,5 +56,22 @@ public Optional<ServerLocation> containerLocation() {
5456
protected void reset() {
5557
super.reset();
5658
this.containerLocation = null;
59+
this.parentState = null;
60+
this.parentContext = null;
61+
}
62+
63+
64+
public PlayerInteractContext parentContext(IPhaseState<?> currentState, PhaseContext<? extends PhaseContext<?>> currentContext) {
65+
this.parentState = currentState;
66+
this.parentContext = currentContext;
67+
return this;
68+
}
69+
70+
public @Nullable IPhaseState<?> forward() {
71+
return this.parentState;
72+
}
73+
74+
public @Nullable PhaseContext<?> forwardContext() {
75+
return this.parentContext;
5776
}
5877
}

0 commit comments

Comments
 (0)