From 55570ff1cfeaf2bb4b115fda58c4ae9d45a89818 Mon Sep 17 00:00:00 2001 From: Gabriel Harris-Rouquette Date: Fri, 10 Sep 2021 23:23:15 -0700 Subject: [PATCH] Forge moves itemstack.mine before breaking the block. Fixes #3516 Signed-off-by: Gabriel Harris-Rouquette --- forge/gradle.properties | 2 +- ...rverPlayerGameModeMixin_Forge_Tracker.java | 79 +++++++++++++++++++ .../resources/mixins.spongeforge.tracker.json | 11 +++ .../TransactionalCaptureSupplier.java | 2 +- .../ServerPlayerGameModeMixin_Tracker.java | 18 ----- vanilla/gradle.properties | 2 +- ...erPlayerGameModeMixin_Vanilla_Tracker.java | 64 +++++++++++++++ .../mixins.spongevanilla.tracker.json | 11 +++ 8 files changed, 168 insertions(+), 21 deletions(-) create mode 100644 forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/server/level/ServerPlayerGameModeMixin_Forge_Tracker.java create mode 100644 forge/src/mixins/resources/mixins.spongeforge.tracker.json create mode 100644 vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/tracker/server/level/ServerPlayerGameModeMixin_Vanilla_Tracker.java create mode 100644 vanilla/src/mixins/resources/mixins.spongevanilla.tracker.json diff --git a/forge/gradle.properties b/forge/gradle.properties index 73e73ff33..7eb16b590 100644 --- a/forge/gradle.properties +++ b/forge/gradle.properties @@ -4,4 +4,4 @@ description=The SpongeAPI implementation for MinecraftForge forgeVersion=36.2.1 loom.platform=forge -mixinConfigs=mixins.spongeforge.accessors.json,mixins.spongeforge.api.json,mixins.spongeforge.core.json \ No newline at end of file +mixinConfigs=mixins.spongeforge.accessors.json,mixins.spongeforge.api.json,mixins.spongeforge.core.json,mixins.spongeforge.tracker.json \ No newline at end of file diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/server/level/ServerPlayerGameModeMixin_Forge_Tracker.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/server/level/ServerPlayerGameModeMixin_Forge_Tracker.java new file mode 100644 index 000000000..318ec0745 --- /dev/null +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/tracker/server/level/ServerPlayerGameModeMixin_Forge_Tracker.java @@ -0,0 +1,79 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.forge.mixin.tracker.server.level; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ServerPlayerGameMode; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.common.event.tracking.PhaseContext; +import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.common.event.tracking.context.transaction.EffectTransactor; +import org.spongepowered.common.event.tracking.context.transaction.ResultingTransactionBySideEffect; +import org.spongepowered.common.event.tracking.context.transaction.TransactionalCaptureSupplier; +import org.spongepowered.common.event.tracking.context.transaction.effect.InventoryEffect; +import org.spongepowered.common.event.tracking.context.transaction.inventory.PlayerInventoryTransaction; + +@Mixin(ServerPlayerGameMode.class) +public class ServerPlayerGameModeMixin_Forge_Tracker { + + @Shadow public ServerPlayer player; + + @Redirect( + method = "destroyBlock", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/item/ItemStack;mineBlock(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/player/Player;)V" + ) + ) + private void forgeTracker$pushTransactionAndEffect( + final ItemStack itemStack, final Level level, final BlockState state, final BlockPos pos, + final Player player + ) { + final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final TransactionalCaptureSupplier transactor = context.getTransactor(); + // Log the prepare drops here because Forge rewrites + // this method to call mine block before calling destroy block + // and mods will possibly already have performed modifications on + // the block before we can capture it in level.destroy. + transactor.logBlockDrops(level, pos, state, level.getBlockEntity(pos)); + + itemStack.mineBlock(level, state, pos, player); + // Needs to get logged as a sideeffect under the BlockChange + try (EffectTransactor ignored = context.getTransactor().pushEffect(new ResultingTransactionBySideEffect( + InventoryEffect.getInstance()))) { + transactor.logPlayerInventoryChange(this.player, PlayerInventoryTransaction.EventCreator.STANDARD); + this.player.inventoryMenu.broadcastChanges(); + } + } +} diff --git a/forge/src/mixins/resources/mixins.spongeforge.tracker.json b/forge/src/mixins/resources/mixins.spongeforge.tracker.json new file mode 100644 index 000000000..13bfaea99 --- /dev/null +++ b/forge/src/mixins/resources/mixins.spongeforge.tracker.json @@ -0,0 +1,11 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "org.spongepowered.forge.mixin.tracker", + "target": "@env(DEFAULT)", + "compatibilityLevel": "JAVA_8", + "priority": 1302, + "mixins": [ + "server.level.ServerPlayerGameModeMixin_Forge_Tracker" + ] +} diff --git a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/TransactionalCaptureSupplier.java b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/TransactionalCaptureSupplier.java index c385e4673..29288ef33 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/context/transaction/TransactionalCaptureSupplier.java +++ b/src/main/java/org/spongepowered/common/event/tracking/context/transaction/TransactionalCaptureSupplier.java @@ -97,7 +97,7 @@ public final class TransactionalCaptureSupplier implements ICaptureSupplier, Tra public EffectTransactor pushEffect(final ResultingTransactionBySideEffect effect) { final GameTransaction<@NonNull ?> parentTransaction = Optional.ofNullable(this.effect) .map(child -> (GameTransaction) child.tail) - .orElse(Objects.requireNonNull(this.tail)); + .orElse(Objects.requireNonNull(this.tail, "Somehow pushing a new effect without an owning Transaction")); final EffectTransactor effectTransactor = new EffectTransactor(effect, parentTransaction, this.effect, this); this.effect = effect; parentTransaction.addLast(effect); diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerPlayerGameModeMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerPlayerGameModeMixin_Tracker.java index 98b57d571..6138abc4e 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerPlayerGameModeMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerPlayerGameModeMixin_Tracker.java @@ -31,7 +31,6 @@ import net.minecraft.server.level.ServerPlayerGameMode; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.MenuProvider; -import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.UseOnContext; @@ -53,17 +52,13 @@ import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.common.bridge.world.inventory.container.ContainerBridge; import org.spongepowered.common.event.SpongeCommonEventFactory; import org.spongepowered.common.event.inventory.InventoryEventFactory; import org.spongepowered.common.event.tracking.PhaseContext; import org.spongepowered.common.event.tracking.PhaseTracker; -import org.spongepowered.common.event.tracking.context.transaction.EffectTransactor; -import org.spongepowered.common.event.tracking.context.transaction.ResultingTransactionBySideEffect; import org.spongepowered.common.event.tracking.context.transaction.TransactionalCaptureSupplier; -import org.spongepowered.common.event.tracking.context.transaction.effect.InventoryEffect; import org.spongepowered.common.event.tracking.context.transaction.inventory.PlayerInventoryTransaction; import org.spongepowered.common.registry.provider.DirectionFacingProvider; import org.spongepowered.common.util.VecHelper; @@ -187,17 +182,4 @@ public abstract class ServerPlayerGameModeMixin_Tracker { } } - @Redirect(method = "destroyBlock", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;mineBlock(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/player/Player;)V")) - public void impl$onMineBlock(final ItemStack itemStack, final Level param0, final BlockState param1, final BlockPos param2, final Player param3) { - final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); - final TransactionalCaptureSupplier transactor = context.getTransactor(); - itemStack.mineBlock(param0, param1, param2, param3); - // Needs to get logged as a sideeffect under the BlockChange - try (EffectTransactor ignored = context.getTransactor().pushEffect(new ResultingTransactionBySideEffect(InventoryEffect.getInstance()))) { - transactor.logPlayerInventoryChange(this.player, PlayerInventoryTransaction.EventCreator.STANDARD); - this.player.inventoryMenu.broadcastChanges(); - } - } - - } diff --git a/vanilla/gradle.properties b/vanilla/gradle.properties index 0492cb27e..de3979d1d 100644 --- a/vanilla/gradle.properties +++ b/vanilla/gradle.properties @@ -2,4 +2,4 @@ name=SpongeVanilla implementation=Vanilla description=The SpongeAPI implementation for Vanilla Minecraft -mixinConfigs=mixins.spongevanilla.api.json,mixins.spongevanilla.core.json \ No newline at end of file +mixinConfigs=mixins.spongevanilla.api.json,mixins.spongevanilla.core.json,mixins.spongevanilla.tracker.json \ No newline at end of file diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/tracker/server/level/ServerPlayerGameModeMixin_Vanilla_Tracker.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/tracker/server/level/ServerPlayerGameModeMixin_Vanilla_Tracker.java new file mode 100644 index 000000000..b6c0ef955 --- /dev/null +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/tracker/server/level/ServerPlayerGameModeMixin_Vanilla_Tracker.java @@ -0,0 +1,64 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.vanilla.mixin.tracker.server.level; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ServerPlayerGameMode; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.common.event.tracking.PhaseContext; +import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.common.event.tracking.context.transaction.EffectTransactor; +import org.spongepowered.common.event.tracking.context.transaction.ResultingTransactionBySideEffect; +import org.spongepowered.common.event.tracking.context.transaction.TransactionalCaptureSupplier; +import org.spongepowered.common.event.tracking.context.transaction.effect.InventoryEffect; +import org.spongepowered.common.event.tracking.context.transaction.inventory.PlayerInventoryTransaction; + +@Mixin(ServerPlayerGameMode.class) +public class ServerPlayerGameModeMixin_Vanilla_Tracker { + + @Shadow public ServerPlayer player; + + @Redirect(method = "destroyBlock", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;mineBlock(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/player/Player;)V")) + public void impl$onMineBlock(final ItemStack itemStack, final Level param0, final BlockState param1, final BlockPos param2, final Player param3) { + final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); + final TransactionalCaptureSupplier transactor = context.getTransactor(); + itemStack.mineBlock(param0, param1, param2, param3); + // Needs to get logged as a sideeffect under the BlockChange + try (EffectTransactor ignored = context.getTransactor().pushEffect(new ResultingTransactionBySideEffect( + InventoryEffect.getInstance()))) { + transactor.logPlayerInventoryChange(this.player, PlayerInventoryTransaction.EventCreator.STANDARD); + this.player.inventoryMenu.broadcastChanges(); + } + } +} diff --git a/vanilla/src/mixins/resources/mixins.spongevanilla.tracker.json b/vanilla/src/mixins/resources/mixins.spongevanilla.tracker.json new file mode 100644 index 000000000..22a7d0498 --- /dev/null +++ b/vanilla/src/mixins/resources/mixins.spongevanilla.tracker.json @@ -0,0 +1,11 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "org.spongepowered.vanilla.mixin.tracker", + "target": "@env(DEFAULT)", + "compatibilityLevel": "JAVA_8", + "priority": 1302, + "mixins": [ + "server.level.ServerPlayerGameModeMixin_Vanilla_Tracker" + ] +}