diff --git a/forge/src/launch/java/org/spongepowered/forge/launch/bridge/event/ForgeEventBridge_Forge.java b/forge/src/launch/java/org/spongepowered/forge/launch/bridge/event/ForgeEventBridge_Forge.java index 446b84fe464..41b3171e8a5 100644 --- a/forge/src/launch/java/org/spongepowered/forge/launch/bridge/event/ForgeEventBridge_Forge.java +++ b/forge/src/launch/java/org/spongepowered/forge/launch/bridge/event/ForgeEventBridge_Forge.java @@ -25,6 +25,7 @@ package org.spongepowered.forge.launch.bridge.event; import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.Event; public interface ForgeEventBridge_Forge { @@ -55,7 +56,9 @@ public interface ForgeEventBridge_Forge { /** * Creates a Sponge event from this Forge event + * + * @param frame The stack frame in which the event will be fired */ - @Nullable Event bridge$createSpongeEvent(); + @Nullable Event bridge$createSpongeEvent(final CauseStackManager.StackFrame frame); } diff --git a/forge/src/launch/java/org/spongepowered/forge/launch/event/ForgeEventManager.java b/forge/src/launch/java/org/spongepowered/forge/launch/event/ForgeEventManager.java index 1a4fd2dd074..6a939ea0f9e 100644 --- a/forge/src/launch/java/org/spongepowered/forge/launch/event/ForgeEventManager.java +++ b/forge/src/launch/java/org/spongepowered/forge/launch/event/ForgeEventManager.java @@ -32,8 +32,10 @@ import net.minecraftforge.eventbus.api.IEventBusInvokeDispatcher; import net.minecraftforge.eventbus.api.IEventListener; import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.common.event.manager.RegisteredListener; import org.spongepowered.common.event.manager.SpongeEventManager; +import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.forge.launch.bridge.event.ForgeEventBridge_Forge; import org.spongepowered.forge.launch.bridge.event.SpongeEventBridge_Forge; @@ -116,9 +118,11 @@ public boolean post(final Event event, final IEventBusInvokeDispatcher wrapper) if (event instanceof ForgeEventBridge_Forge) { // intercept! final ForgeEventBridge_Forge forgeEvent = (ForgeEventBridge_Forge) event; - final org.spongepowered.api.event.@Nullable Event spongeEvent = forgeEvent.bridge$createSpongeEvent(); - if (spongeEvent != null) { - return this.postDualBus(spongeEvent, Collections.singleton(event), wrapper); + try (final CauseStackManager.StackFrame frame = PhaseTracker.getCauseStackManager().pushCauseFrame()) { + final org.spongepowered.api.event.@Nullable Event spongeEvent = forgeEvent.bridge$createSpongeEvent(frame); + if (spongeEvent != null) { + return this.postDual(spongeEvent, event, wrapper); + } } } // Do as Forge does - SpongeVanilla has no role to play here. @@ -145,15 +149,21 @@ public boolean post(final org.spongepowered.api.event.Event event) { // Do as SpongeVanilla does - Forge has no role to play here. return super.post(event); } - return this.postDualBus(event, forgeEvents, eventBridge.bridge$eventDispatcher()); + return this.postDual(event, forgeEvents, eventBridge.bridge$eventDispatcher()); } // Implementation - private boolean postDualBus(final org.spongepowered.api.event.Event spongeEvent, final Collection forgeEvents, + private boolean postDual(final org.spongepowered.api.event.Event spongeEvent, final Event forgeEvent, + final IEventBusInvokeDispatcher dispatcher) { + return postDual(spongeEvent, Collections.singleton(forgeEvent), dispatcher); + } + + private boolean postDual(final org.spongepowered.api.event.Event spongeEvent, final Collection forgeEvents, final IEventBusInvokeDispatcher dispatcher) { try (final NoExceptionClosable ignored = this.preparePost(spongeEvent)) { final RegisteredListener.Cache listeners = this.getHandlerCache(spongeEvent); + final List> beforeModifications = listeners.beforeModifications(); if (!beforeModifications.isEmpty()) { // First, we fire the Sponge beforeModifications on the Sponge event @@ -164,6 +174,7 @@ private boolean postDualBus(final org.spongepowered.api.event.Event spongeEvent, ((ForgeEventBridge_Forge) forgeEvent).bridge$syncFrom(spongeEvent); } } + // Then, we fire all our Forge events for (final Event forgeEvent : forgeEvents) { this.wrappedEventBus.post(forgeEvent, dispatcher); @@ -172,8 +183,15 @@ private boolean postDualBus(final org.spongepowered.api.event.Event spongeEvent, ((ForgeEventBridge_Forge) forgeEvent).bridge$syncTo(spongeEvent); } - // and now we do our standard event listener stuff. - return this.post(spongeEvent, listeners.afterModifications()); + // Now we do our standard event listener stuff. + final boolean result = this.post(spongeEvent, listeners.afterModifications()); + + // Finally, we sync to the Forge events again + for (final Event forgeEvent : forgeEvents) { + ((ForgeEventBridge_Forge) forgeEvent).bridge$syncFrom(spongeEvent); + } + + return result; } } } diff --git a/forge/src/main/java/org/spongepowered/forge/util/TristateUtil.java b/forge/src/main/java/org/spongepowered/forge/util/TristateUtil.java new file mode 100644 index 00000000000..46a8768dd9d --- /dev/null +++ b/forge/src/main/java/org/spongepowered/forge/util/TristateUtil.java @@ -0,0 +1,60 @@ +/* + * 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.util; + +import net.minecraftforge.eventbus.api.Event; +import org.spongepowered.api.util.Tristate; + +public final class TristateUtil { + + public static Tristate fromEventResult(final Event.Result result) { + switch (result) { + case DENY: + return Tristate.FALSE; + case DEFAULT: + return Tristate.UNDEFINED; + case ALLOW: + return Tristate.TRUE; + default: + throw new IllegalArgumentException("Event.Result " + result); + } + } + + public static Event.Result toEventResult(final Tristate tristate) { + switch (tristate) { + case TRUE: + return Event.Result.ALLOW; + case FALSE: + return Event.Result.DENY; + case UNDEFINED: + return Event.Result.DEFAULT; + default: + throw new IllegalArgumentException("Tristate " + tristate); + } + } + + private TristateUtil() { + } +} diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/api/event/block/InteractBlockEvent_SecondaryMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/api/event/block/InteractBlockEvent_SecondaryMixin_Forge.java new file mode 100644 index 00000000000..ae3e5c79927 --- /dev/null +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/api/event/block/InteractBlockEvent_SecondaryMixin_Forge.java @@ -0,0 +1,76 @@ +/* + * 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.core.api.event.block; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.event.entity.player.PlayerInteractEvent; +import net.minecraftforge.eventbus.api.Event; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.event.EventContextKeys; +import org.spongepowered.api.event.block.InteractBlockEvent; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.util.DirectionUtil; +import org.spongepowered.common.util.VecHelper; +import org.spongepowered.forge.launch.bridge.event.SpongeEventBridge_Forge; + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; + +@Mixin(InteractBlockEvent.Secondary.class) +public interface InteractBlockEvent_SecondaryMixin_Forge extends SpongeEventBridge_Forge { + + @Override + default @Nullable Collection bridge$createForgeEvents() { + final InteractBlockEvent.Secondary spongeEvent = (InteractBlockEvent.Secondary) this; + + final Optional playerOpt = spongeEvent.cause().first(Player.class); + if (!playerOpt.isPresent()) { + return null; + } + + final InteractionHand hand = (InteractionHand) (Object) spongeEvent.context().require(EventContextKeys.USED_HAND); + final BlockPos blockPos = VecHelper.toBlockPos(spongeEvent.block().position()); + final Direction direction = DirectionUtil.getFor(spongeEvent.targetSide()); + final Vec3 interactionPoint = VecHelper.toVanillaVector3d(spongeEvent.interactionPoint()); + final BlockHitResult hitResult = new BlockHitResult(interactionPoint, direction, blockPos, false); + + final PlayerInteractEvent.RightClickBlock forgeEvent = new PlayerInteractEvent.RightClickBlock( + playerOpt.get(), + hand, + blockPos, + hitResult + ); + forgeEvent.setCancellationResult(InteractionResult.FAIL); + + return Collections.singleton(forgeEvent); + } +} diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/ServerChatEventMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/ServerChatEventMixin_Forge.java index ce16a7ac959..742c7122b56 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/ServerChatEventMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/ServerChatEventMixin_Forge.java @@ -30,13 +30,13 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.entity.living.player.PlayerChatFormatter; import org.spongepowered.api.entity.living.player.server.ServerPlayer; +import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.Event; import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.event.message.PlayerChatEvent; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.adventure.SpongeAdventure; -import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.forge.launch.bridge.event.ForgeEventBridge_Forge; import java.util.Optional; @@ -65,12 +65,12 @@ public abstract class ServerChatEventMixin_Forge implements ForgeEventBridge_For } @Override - public @Nullable Event bridge$createSpongeEvent() { + public @Nullable Event bridge$createSpongeEvent(final CauseStackManager.StackFrame frame) { final Audience audience = (Audience) this.shadow$getPlayer().server; final PlayerChatFormatter chatFormatter = ((ServerPlayer) this.shadow$getPlayer()).chatFormatter(); final net.kyori.adventure.text.Component originalMessage = SpongeAdventure.asAdventure(this.shadow$getComponent()); return SpongeEventFactory.createPlayerChatEvent( - PhaseTracker.getCauseStackManager().currentCause(), + frame.currentCause(), audience, Optional.of(audience), chatFormatter, diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/EntityTravelToDimensionEventMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/EntityTravelToDimensionEventMixin_Forge.java index bb964ccbbfe..6efe91c0055 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/EntityTravelToDimensionEventMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/EntityTravelToDimensionEventMixin_Forge.java @@ -31,6 +31,7 @@ import net.minecraftforge.event.entity.EntityEvent; import net.minecraftforge.event.entity.EntityTravelToDimensionEvent; import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.Event; import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.event.entity.ChangeEntityWorldEvent; @@ -39,7 +40,6 @@ import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.forge.launch.bridge.event.ForgeEventBridge_Forge; @Mixin(value = EntityTravelToDimensionEvent.class, remap = false) @@ -65,10 +65,10 @@ public abstract class EntityTravelToDimensionEventMixin_Forge implements ForgeEv } @Override - public @Nullable Event bridge$createSpongeEvent() { + public @Nullable Event bridge$createSpongeEvent(final CauseStackManager.StackFrame frame) { final Entity entity = ((EntityEvent) (Object) this).getEntity(); final ServerLevel toWorld = SpongeCommon.server().getLevel(this.dimension); - return SpongeEventFactory.createChangeEntityWorldEventPre(PhaseTracker.getCauseStackManager().currentCause(), + return SpongeEventFactory.createChangeEntityWorldEventPre(frame.currentCause(), (org.spongepowered.api.entity.Entity) entity, (org.spongepowered.api.world.server.ServerWorld) entity.getCommandSenderWorld(), (org.spongepowered.api.world.server.ServerWorld) toWorld, (org.spongepowered.api.world.server.ServerWorld) toWorld); } diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/player/PlayerEvent_PlayerChangedDimensionEventMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/player/PlayerEvent_PlayerChangedDimensionEventMixin_Forge.java index f76a5d3d5ef..0fc24ced19b 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/player/PlayerEvent_PlayerChangedDimensionEventMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/player/PlayerEvent_PlayerChangedDimensionEventMixin_Forge.java @@ -28,6 +28,7 @@ import net.minecraft.world.level.Level; import net.minecraftforge.event.entity.player.PlayerEvent; import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.Event; import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.world.server.ServerWorld; @@ -35,7 +36,6 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.common.SpongeCommon; -import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.forge.launch.bridge.event.ForgeEventBridge_Forge; @Mixin(value = PlayerEvent.PlayerChangedDimensionEvent.class, remap = false) @@ -58,10 +58,10 @@ public final class PlayerEvent_PlayerChangedDimensionEventMixin_Forge implements @SuppressWarnings("ConstantConditions") @Override - public Event bridge$createSpongeEvent() { + public Event bridge$createSpongeEvent(final CauseStackManager.StackFrame frame) { final PlayerEvent.PlayerChangedDimensionEvent thisEvent = (PlayerEvent.PlayerChangedDimensionEvent) (Object) this; return SpongeEventFactory.createChangeEntityWorldEventPost( - PhaseTracker.getCauseStackManager().currentCause(), + frame.currentCause(), (Entity) thisEvent.getPlayer(), (ServerWorld) SpongeCommon.server().getLevel(this.fromDim), (ServerWorld) SpongeCommon.server().getLevel(this.toDim), diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/player/PlayerInteractEvent_RightClickBlockMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/player/PlayerInteractEvent_RightClickBlockMixin_Forge.java new file mode 100644 index 00000000000..92b46ad87ce --- /dev/null +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/player/PlayerInteractEvent_RightClickBlockMixin_Forge.java @@ -0,0 +1,73 @@ +/* + * 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.core.minecraftforge.event.entity.player; + +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; +import net.minecraftforge.event.entity.player.PlayerInteractEvent; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.event.CauseStackManager; +import org.spongepowered.api.event.Event; +import org.spongepowered.api.event.block.InteractBlockEvent; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.event.SpongeCommonEventFactory; +import org.spongepowered.forge.launch.bridge.event.ForgeEventBridge_Forge; +import org.spongepowered.forge.util.TristateUtil; + +@Mixin(value = PlayerInteractEvent.RightClickBlock.class, remap = false) +public abstract class PlayerInteractEvent_RightClickBlockMixin_Forge implements ForgeEventBridge_Forge { + + @Override + public void bridge$syncFrom(final Event event) { + final InteractBlockEvent.Secondary spongeEvent = (InteractBlockEvent.Secondary) event; + final PlayerInteractEvent.RightClickBlock forgeEvent = (PlayerInteractEvent.RightClickBlock) (Object) this; + + forgeEvent.setCanceled(spongeEvent.isCancelled()); + forgeEvent.setUseBlock(TristateUtil.toEventResult(spongeEvent.useBlockResult())); + forgeEvent.setUseItem(TristateUtil.toEventResult(spongeEvent.useItemResult())); + } + + @Override + public void bridge$syncTo(final Event event) { + final InteractBlockEvent.Secondary spongeEvent = (InteractBlockEvent.Secondary) event; + final PlayerInteractEvent.RightClickBlock forgeEvent = (PlayerInteractEvent.RightClickBlock) (Object) this; + + spongeEvent.setCancelled(forgeEvent.isCanceled()); + spongeEvent.setUseBlockResult(TristateUtil.fromEventResult(forgeEvent.getUseBlock())); + spongeEvent.setUseItemResult(TristateUtil.fromEventResult(forgeEvent.getUseItem())); + } + + @Override + public @Nullable Event bridge$createSpongeEvent(final CauseStackManager.StackFrame frame) { + final PlayerInteractEvent.RightClickBlock forgeEvent = (PlayerInteractEvent.RightClickBlock) (Object) this; + final Level world = forgeEvent.getWorld(); + if (world.isClientSide) { + return null; + } + + return SpongeCommonEventFactory.createInteractBlockEventSecondary(forgeEvent.getPlayer(), (ServerLevel) world, forgeEvent.getItemStack(), + forgeEvent.getHand(), forgeEvent.getHitVec(), frame); + } +} diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/world/BlockEvent_BreakEventMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/world/BlockEvent_BreakEventMixin_Forge.java index 04e1410b3fa..82c2ad4a20f 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/world/BlockEvent_BreakEventMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/world/BlockEvent_BreakEventMixin_Forge.java @@ -31,13 +31,13 @@ import org.spongepowered.api.block.BlockTypes; import org.spongepowered.api.block.transaction.BlockTransaction; import org.spongepowered.api.block.transaction.Operations; +import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.Event; import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.event.block.ChangeBlockEvent; import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.common.block.SpongeBlockSnapshot; -import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.util.VecHelper; import org.spongepowered.forge.launch.bridge.event.ForgeEventBridge_Forge; import org.spongepowered.math.vector.Vector3i; @@ -72,7 +72,7 @@ public abstract class BlockEvent_BreakEventMixin_Forge extends BlockEventMixin_F } @Override - public @Nullable Event bridge$createSpongeEvent() { + public @Nullable Event bridge$createSpongeEvent(final CauseStackManager.StackFrame frame) { final LevelAccessor accessor = this.shadow$getWorld(); if (accessor instanceof ServerWorld) { final ServerWorld serverWorld = (ServerWorld) accessor; @@ -91,7 +91,7 @@ public abstract class BlockEvent_BreakEventMixin_Forge extends BlockEventMixin_F .build(), Operations.BREAK.get() ); - return SpongeEventFactory.createChangeBlockEventAll(PhaseTracker.getCauseStackManager().currentCause(), + return SpongeEventFactory.createChangeBlockEventAll(frame.currentCause(), Collections.singletonList(transaction), serverWorld); } return null; diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/level/ServerPlayerMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/level/ServerPlayerMixin_Forge.java index 4ab735eb723..2f80d5112e7 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/level/ServerPlayerMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/server/level/ServerPlayerMixin_Forge.java @@ -27,11 +27,13 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; +import net.minecraftforge.common.ForgeMod; import net.minecraftforge.common.util.ITeleporter; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.common.bridge.server.level.ServerPlayerBridge; import org.spongepowered.common.bridge.world.entity.EntityBridge; import org.spongepowered.common.event.tracking.PhaseContext; import org.spongepowered.common.event.tracking.PhaseTracker; @@ -41,8 +43,13 @@ import org.spongepowered.common.world.portal.PortalLogic; import org.spongepowered.forge.mixin.core.world.entity.LivingEntityMixin_Forge; -@Mixin(ServerPlayer.class) -public abstract class ServerPlayerMixin_Forge extends LivingEntityMixin_Forge { +@Mixin(value = ServerPlayer.class, priority = 1300) +public abstract class ServerPlayerMixin_Forge extends LivingEntityMixin_Forge implements ServerPlayerBridge { + + @Override + public double bridge$reachDistance() { + return this.shadow$getAttribute(ForgeMod.REACH_DISTANCE.get()).getValue(); + } /** * @author dualspiral - 18th December 2020 - 1.16.4 diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge.java index 819262550c7..56911acc56f 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge.java @@ -26,8 +26,12 @@ import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.attributes.Attribute; +import net.minecraft.world.entity.ai.attributes.AttributeInstance; import net.minecraftforge.common.ForgeHooks; +import org.checkerframework.checker.nullness.qual.Nullable; 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.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -36,6 +40,8 @@ @Mixin(value = LivingEntity.class) public abstract class LivingEntityMixin_Forge implements PlatformLivingEntityBridge { + @Shadow @Nullable public abstract AttributeInstance shadow$getAttribute(Attribute param0); + @Inject( method = "updateFallFlying", at = @At( diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/player/PlayerMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/player/PlayerMixin_Forge.java index e49f36b530a..7d1d446e0ac 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/player/PlayerMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/player/PlayerMixin_Forge.java @@ -31,7 +31,7 @@ import org.spongepowered.forge.mixin.core.world.entity.LivingEntityMixin_Forge; @Mixin(Player.class) -public class PlayerMixin_Forge extends LivingEntityMixin_Forge { +public abstract class PlayerMixin_Forge extends LivingEntityMixin_Forge { @Override public boolean bridge$onLivingAttack( final LivingEntity entity, final DamageSource source, final float amount diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/item/ItemStackMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/item/ItemStackMixin_Forge.java new file mode 100644 index 00000000000..8187f0b7b3b --- /dev/null +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/item/ItemStackMixin_Forge.java @@ -0,0 +1,49 @@ +/* + * 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.core.world.item; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.LevelReader; +import net.minecraftforge.common.extensions.IForgeItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.common.bridge.world.item.ItemStackBridge; + +@Mixin(value = ItemStack.class, priority = 1300) +public abstract class ItemStackMixin_Forge implements ItemStackBridge { + + @Override + public InteractionResult bridge$onItemUseFirst(UseOnContext context) { + return ((IForgeItemStack) this).onItemUseFirst(context); + } + + @Override + public boolean bridge$doesSneakBypassUse(LevelReader world, BlockPos pos, Player player) { + return ((IForgeItemStack) this).doesSneakBypassUse(world, pos, player); + } +} diff --git a/forge/src/mixins/resources/mixins.spongeforge.core.json b/forge/src/mixins/resources/mixins.spongeforge.core.json index befa83edc4b..c4fb5531208 100644 --- a/forge/src/mixins/resources/mixins.spongeforge.core.json +++ b/forge/src/mixins/resources/mixins.spongeforge.core.json @@ -9,6 +9,7 @@ "mixins": [ "api.event.EventMixin_Forge", "api.event.block.ChangeBlockEvent_AllMixin_Forge", + "api.event.block.InteractBlockEvent_SecondaryMixin_Forge", "api.event.entity.ChangeEntityWorldEvent_PreMixin_Forge", "api.event.entity.ChangeEventWorldEvent_PostMixin_Forge", "api.event.message.PlayerChatEventMixin_Forge", @@ -19,6 +20,7 @@ "minecraftforge.event.ServerChatEventMixin_Forge", "minecraftforge.event.entity.EntityTravelToDimensionEventMixin_Forge", "minecraftforge.event.entity.player.PlayerEvent_PlayerChangedDimensionEventMixin_Forge", + "minecraftforge.event.entity.player.PlayerInteractEvent_RightClickBlockMixin_Forge", "minecraftforge.event.world.BlockEvent_BreakEventMixin_Forge", "minecraftforge.event.world.BlockEventMixin_Forge", "minecraftforge.fml.BrandingControlMixin_Forge", @@ -37,6 +39,7 @@ "world.entity.player.PlayerMixin_Forge", "world.entity.projectile.ThrownEnderpealMixin_Forge", "world.entity.vehicle.BoatMixin_Forge", + "world.item.ItemStackMixin_Forge", "world.level.LevelMixin_Forge", "world.level.block.FireBlockMixin_Forge", "world.level.block.state.BlockStateMixin_Forge", diff --git a/src/main/java/org/spongepowered/common/bridge/server/level/ServerPlayerBridge.java b/src/main/java/org/spongepowered/common/bridge/server/level/ServerPlayerBridge.java index c12ebadbb94..27a5b12df6d 100644 --- a/src/main/java/org/spongepowered/common/bridge/server/level/ServerPlayerBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/server/level/ServerPlayerBridge.java @@ -105,4 +105,6 @@ public interface ServerPlayerBridge extends ServerPlayerEntityHealthScaleBridge void bridge$setSleepingIgnored(final boolean sleepingIgnored); + double bridge$reachDistance(); + } diff --git a/src/main/java/org/spongepowered/common/util/TristateUtil.java b/src/main/java/org/spongepowered/common/bridge/world/item/ItemStackBridge.java similarity index 60% rename from src/main/java/org/spongepowered/common/util/TristateUtil.java rename to src/main/java/org/spongepowered/common/bridge/world/item/ItemStackBridge.java index eb28df33435..e1eee7a39c2 100644 --- a/src/main/java/org/spongepowered/common/util/TristateUtil.java +++ b/src/main/java/org/spongepowered/common/bridge/world/item/ItemStackBridge.java @@ -22,30 +22,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.common.util; +package org.spongepowered.common.bridge.world.item; -import com.google.common.collect.EnumBiMap; +import net.minecraft.core.BlockPos; import net.minecraft.world.InteractionResult; -import org.spongepowered.api.util.Tristate; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.LevelReader; -public final class TristateUtil { +public interface ItemStackBridge { - private static final EnumBiMap map = EnumBiMap.create(InteractionResult.class, Tristate.class); + InteractionResult bridge$onItemUseFirst(UseOnContext context); - static { - TristateUtil.map.put(InteractionResult.FAIL, Tristate.FALSE); - TristateUtil.map.put(InteractionResult.PASS, Tristate.UNDEFINED); - TristateUtil.map.put(InteractionResult.SUCCESS, Tristate.TRUE); - } - - public static Tristate fromActionResult(final InteractionResult result) { - return TristateUtil.map.get(result); - } - - public static InteractionResult toActionResult(final Tristate tristate) { - return TristateUtil.map.inverse().get(tristate); - } - - private TristateUtil() { - } + boolean bridge$doesSneakBypassUse(LevelReader world, BlockPos pos, Player player); } diff --git a/src/main/java/org/spongepowered/common/event/SpongeCommonEventFactory.java b/src/main/java/org/spongepowered/common/event/SpongeCommonEventFactory.java index 50b4c6de193..dbf51faa1f0 100644 --- a/src/main/java/org/spongepowered/common/event/SpongeCommonEventFactory.java +++ b/src/main/java/org/spongepowered/common/event/SpongeCommonEventFactory.java @@ -121,6 +121,7 @@ import org.spongepowered.common.map.SpongeMapStorage; import org.spongepowered.common.registry.provider.DirectionFacingProvider; import org.spongepowered.common.util.Constants; +import org.spongepowered.common.util.DirectionUtil; import org.spongepowered.common.util.VecHelper; import org.spongepowered.common.world.server.SpongeLocatableBlockBuilder; import org.spongepowered.math.vector.Vector3d; @@ -344,15 +345,16 @@ public static InteractBlockEvent.Primary callInteractBlockEventPrimary(final Ser } } - public static InteractBlockEvent.Secondary callInteractBlockEventSecondary(final net.minecraft.world.entity.player.Player player, final ItemStack heldItem, final Vector3d hitVec, final BlockSnapshot targetBlock, final Direction targetSide, final InteractionHand hand) { - try (final CauseStackManager.StackFrame frame = PhaseTracker.getCauseStackManager().pushCauseFrame()) { - SpongeCommonEventFactory.applyCommonInteractContext(player, heldItem, hand, targetBlock, null, frame); - final InteractBlockEvent.Secondary event = SpongeEventFactory.createInteractBlockEventSecondary(frame.currentCause(), - Tristate.UNDEFINED, Tristate.UNDEFINED, Tristate.UNDEFINED, Tristate.UNDEFINED, targetBlock, hitVec, - targetSide); - SpongeCommon.post(event); - return event; - } + public static InteractBlockEvent.Secondary createInteractBlockEventSecondary( + final net.minecraft.world.entity.player.Player player, final ServerLevel level, final ItemStack heldItem, final InteractionHand hand, + final BlockHitResult blockHitResult, final CauseStackManager.StackFrame frame) { + final Vector3d interactionPoint = VecHelper.toVector3d(blockHitResult.getLocation()); + final Direction side = DirectionUtil.getFor(blockHitResult.getDirection()); + final BlockSnapshot block = ((ServerWorld) level).createSnapshot(VecHelper.toVector3i(blockHitResult.getBlockPos())); + + SpongeCommonEventFactory.applyCommonInteractContext(player, heldItem, hand, block, null, frame); + return SpongeEventFactory.createInteractBlockEventSecondary(frame.currentCause(), + Tristate.UNDEFINED, Tristate.UNDEFINED, Tristate.UNDEFINED, Tristate.UNDEFINED, block, interactionPoint, side); } public static void applyCommonInteractContext(final net.minecraft.world.entity.player.Player player, final ItemStack stack, final InteractionHand hand, final @Nullable BlockSnapshot targetBlock, diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerGameModeMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerGameModeMixin.java index e803659f024..3eb108f888b 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerGameModeMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerGameModeMixin.java @@ -24,22 +24,14 @@ */ package org.spongepowered.common.mixin.core.server.level; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; import net.minecraft.server.level.ServerPlayerGameMode; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.common.bridge.server.level.ServerPlayerGameModeBridge; @Mixin(ServerPlayerGameMode.class) public abstract class ServerPlayerGameModeMixin implements ServerPlayerGameModeBridge { - private boolean impl$interactBlockLeftClickEventCancelled = false; private boolean impl$interactBlockRightClickEventCancelled = false; - private boolean impl$lastInteractItemOnBlockCancelled = false; @Override public boolean bridge$isInteractBlockRightClickCancelled() { @@ -50,29 +42,4 @@ public abstract class ServerPlayerGameModeMixin implements ServerPlayerGameModeB public void bridge$setInteractBlockRightClickCancelled(final boolean cancelled) { this.impl$interactBlockRightClickEventCancelled = cancelled; } - -// @Override -// public boolean bridge$isLastInteractItemOnBlockCancelled() { -// return this.impl$lastInteractItemOnBlockCancelled; -// } -// -// @Override -// public void bridge$setLastInteractItemOnBlockCancelled(final boolean lastInteractItemOnBlockCancelled) { -// this.impl$lastInteractItemOnBlockCancelled = lastInteractItemOnBlockCancelled; -// } - - /** - * We have to check for cancelled left click events because they occur from different packets - * or processing branches such that there's no clear "context" of where we can store these variables. - * So, we store it to the interaction manager's fields, to avoid contaminating other interaction - * manager's processes. - */ - @Inject(method = "handleBlockBreakAction", at = @At("HEAD"), cancellable = true) - private void impl$cancelIfInteractBlockPrimaryCancelled(final BlockPos pos, final ServerboundPlayerActionPacket.Action action, - final Direction direction, final int maxY, final CallbackInfo ci) { - if (this.impl$interactBlockLeftClickEventCancelled) { - this.impl$interactBlockLeftClickEventCancelled = false; - ci.cancel(); - } - } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerMixin.java index 0b3310202b0..4b793e7a56d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerMixin.java @@ -372,6 +372,11 @@ public abstract class ServerPlayerMixin extends PlayerMixin implements SubjectBr this.impl$sleepingIgnored = sleepingIgnored; } + @Override + public double bridge$reachDistance() { + return 5; + } + /* @Inject(method = "markPlayerActive()V", at = @At("HEAD")) private void impl$onPlayerActive(final CallbackInfo ci) { diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerGamePacketListenerImplMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerGamePacketListenerImplMixin.java index b405a5c0ae9..2d2063a88bc 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerGamePacketListenerImplMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/network/ServerGamePacketListenerImplMixin.java @@ -387,11 +387,11 @@ public abstract class ServerGamePacketListenerImplMixin implements ServerGamePac target = "Lnet/minecraft/world/entity/Entity;interactAt(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/InteractionHand;)Lnet/minecraft/world/InteractionResult;" ) ) - public void impl$onRightClickAtEntity(final ServerboundInteractPacket p_147340_1, final CallbackInfo ci) { - final Entity entity = p_147340_1.getTarget(this.player.getLevel()); - final ItemStack itemInHand = p_147340_1.getHand() == null ? ItemStack.EMPTY : this.player.getItemInHand(p_147340_1.getHand()); + public void impl$onRightClickAtEntity(final ServerboundInteractPacket packet, final CallbackInfo ci) { + final Entity entity = packet.getTarget(this.player.getLevel()); + final ItemStack itemInHand = packet.getHand() == null ? ItemStack.EMPTY : this.player.getItemInHand(packet.getHand()); final InteractEntityEvent.Secondary event = SpongeCommonEventFactory - .callInteractEntityEventSecondary(this.player, itemInHand, entity, p_147340_1.getHand(), VecHelper.toVector3d(p_147340_1.getLocation())); + .callInteractEntityEventSecondary(this.player, itemInHand, entity, packet.getHand(), VecHelper.toVector3d(packet.getLocation())); if (event.isCancelled()) { ci.cancel(); } else { @@ -404,8 +404,8 @@ public abstract class ServerGamePacketListenerImplMixin implements ServerGamePac cancellable = true, at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;attack(Lnet/minecraft/world/entity/Entity;)V") ) - public void impl$onLeftClickEntity(final ServerboundInteractPacket p_147340_1_, final CallbackInfo ci) { - final Entity entity = p_147340_1_.getTarget(this.player.getLevel()); + public void impl$onLeftClickEntity(final ServerboundInteractPacket packet, final CallbackInfo ci) { + final Entity entity = packet.getTarget(this.player.getLevel()); final InteractEntityEvent.Primary event = SpongeCommonEventFactory.callInteractEntityEventPrimary(this.player, this.player.getItemInHand(this.player.getUsedItemHand()), entity, this.player.getUsedItemHand()); @@ -432,8 +432,11 @@ public abstract class ServerGamePacketListenerImplMixin implements ServerGamePac this.impl$ignorePackets--; } else { if (ShouldFire.INTERACT_ITEM_EVENT_PRIMARY) { + final double reachDistance = ((ServerPlayerBridge) this.player).bridge$reachDistance(); + final double pickRange = this.player.gameMode.isCreative() ? reachDistance : (reachDistance - 0.5); + final Vec3 startPos = this.player.getEyePosition(1); - final Vec3 endPos = startPos.add(this.player.getLookAngle().scale(5d)); // TODO hook for blockReachDistance? + final Vec3 endPos = startPos.add(this.player.getLookAngle().scale(pickRange)); final HitResult result = this.player.getLevel().clip(new ClipContext(startPos, endPos, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, this.player)); if (result.getType() == HitResult.Type.MISS) { final ItemStack heldItem = this.player.getItemInHand(hand); @@ -465,22 +468,22 @@ public abstract class ServerGamePacketListenerImplMixin implements ServerGamePac } @Redirect(method = "handlePlayerAction", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayerGameMode;handleBlockBreakAction(Lnet/minecraft/core/BlockPos;Lnet/minecraft/network/protocol/game/ServerboundPlayerActionPacket$Action;Lnet/minecraft/core/Direction;I)V")) - public void impl$callInteractBlockPrimaryEvent(final ServerPlayerGameMode playerInteractionManager, final BlockPos p_225416_1_, - final ServerboundPlayerActionPacket.Action p_225416_2_, final Direction p_225416_3_, final int p_225416_4_) { - final BlockSnapshot snapshot = ((org.spongepowered.api.world.server.ServerWorld) (playerInteractionManager.level)).createSnapshot(VecHelper.toVector3i(p_225416_1_)); - final InteractBlockEvent.Primary event = SpongeCommonEventFactory.callInteractBlockEventPrimary(p_225416_2_, this.player, this.player.getItemInHand( - InteractionHand.MAIN_HAND), snapshot, InteractionHand.MAIN_HAND, p_225416_3_); + public void impl$callInteractBlockPrimaryEvent(final ServerPlayerGameMode playerInteractionManager, final BlockPos blockPos, + final ServerboundPlayerActionPacket.Action action, final Direction direction, final int maxBuildHeight) { + final BlockSnapshot snapshot = ((org.spongepowered.api.world.server.ServerWorld) (playerInteractionManager.level)).createSnapshot(VecHelper.toVector3i(blockPos)); + final InteractBlockEvent.Primary event = SpongeCommonEventFactory.callInteractBlockEventPrimary(action, this.player, this.player.getItemInHand( + InteractionHand.MAIN_HAND), snapshot, InteractionHand.MAIN_HAND, direction); if (event instanceof Cancellable && ((Cancellable) event).isCancelled()) { - this.player.connection.send(new ClientboundBlockBreakAckPacket(p_225416_1_, playerInteractionManager.level.getBlockState(p_225416_1_), p_225416_2_, false, "block action restricted")); + this.player.connection.send(new ClientboundBlockBreakAckPacket(blockPos, playerInteractionManager.level.getBlockState(blockPos), action, false, "block action restricted")); this.impl$ignorePackets++; } else { - if (p_225416_2_ == ServerboundPlayerActionPacket.Action.ABORT_DESTROY_BLOCK) { - if (!Objects.equals(((ServerPlayerGameModeAccessor) playerInteractionManager).accessor$destroyPos(), p_225416_1_)) { + if (action == ServerboundPlayerActionPacket.Action.ABORT_DESTROY_BLOCK) { + if (!Objects.equals(((ServerPlayerGameModeAccessor) playerInteractionManager).accessor$destroyPos(), blockPos)) { return; // prevents Mismatch in destroy block pos warning } } - playerInteractionManager.handleBlockBreakAction(p_225416_1_, p_225416_2_, p_225416_3_, p_225416_4_); - if (p_225416_2_ == ServerboundPlayerActionPacket.Action.START_DESTROY_BLOCK) { + playerInteractionManager.handleBlockBreakAction(blockPos, action, direction, maxBuildHeight); + if (action == ServerboundPlayerActionPacket.Action.START_DESTROY_BLOCK) { this.impl$ignorePackets++; } } diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/ItemStackMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/ItemStackMixin.java index 5024d588944..7d83c670565 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/ItemStackMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/ItemStackMixin.java @@ -24,8 +24,13 @@ */ package org.spongepowered.common.mixin.core.world.item; +import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.LevelReader; import org.checkerframework.checker.nullness.qual.NonNull; import org.spongepowered.api.data.DataTransactionResult; import org.spongepowered.api.data.Key; @@ -39,6 +44,7 @@ import org.spongepowered.common.bridge.data.DataCompoundHolder; import org.spongepowered.common.bridge.data.DataHolderProcessor; import org.spongepowered.common.bridge.data.SpongeDataHolderBridge; +import org.spongepowered.common.bridge.world.item.ItemStackBridge; import org.spongepowered.common.data.DataUtil; import org.spongepowered.common.data.provider.nbt.NBTDataType; import org.spongepowered.common.data.provider.nbt.NBTDataTypes; @@ -48,7 +54,7 @@ import javax.annotation.Nullable; @Mixin(net.minecraft.world.item.ItemStack.class) -public abstract class ItemStackMixin implements SpongeDataHolderBridge, DataCompoundHolder { +public abstract class ItemStackMixin implements ItemStackBridge, SpongeDataHolderBridge, DataCompoundHolder { // @formatter:off @Shadow private boolean emptyCacheFlag; @@ -58,6 +64,16 @@ public abstract class ItemStackMixin implements SpongeDataHolderBridge, DataComp @Shadow @Nullable public abstract CompoundTag shadow$getTag(); // @formatter:on + @Override + public InteractionResult bridge$onItemUseFirst(UseOnContext context) { + return InteractionResult.PASS; + } + + @Override + public boolean bridge$doesSneakBypassUse(LevelReader world, BlockPos pos, Player player) { + return false; + } + @Override public Optional bridge$get(final Key<@NonNull ? extends Value> key) { if (this.emptyCacheFlag) { 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 b94a1d30874..99fa72dc3db 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 @@ -26,6 +26,7 @@ import net.minecraft.advancements.CriteriaTriggers; import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayerGameMode; import net.minecraft.world.InteractionHand; @@ -39,7 +40,6 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; import org.checkerframework.checker.nullness.qual.NonNull; -import org.spongepowered.api.block.BlockSnapshot; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.EventContextKeys; import org.spongepowered.api.event.block.InteractBlockEvent; @@ -53,25 +53,21 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.bridge.server.level.ServerPlayerGameModeBridge; import org.spongepowered.common.bridge.world.inventory.container.ContainerBridge; +import org.spongepowered.common.bridge.world.item.ItemStackBridge; 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.TransactionalCaptureSupplier; import org.spongepowered.common.event.tracking.context.transaction.inventory.PlayerInventoryTransaction; -import org.spongepowered.common.registry.provider.DirectionFacingProvider; import org.spongepowered.common.util.VecHelper; -import org.spongepowered.math.vector.Vector3d; import org.spongepowered.math.vector.Vector3i; @Mixin(ServerPlayerGameMode.class) public abstract class ServerPlayerGameModeMixin_Tracker { - - @Shadow public ServerPlayer player; - @Shadow public net.minecraft.server.level.ServerLevel level; - @Shadow private GameType gameModeForPlayer; @Shadow public abstract boolean isCreative(); @@ -90,29 +86,37 @@ public abstract class ServerPlayerGameModeMixin_Tracker { /** * @author Morph - * @reason Fire interact block event. + * @author Yeregorix - Updated Apr 13th, 2023 - Add Forge support + * @reason Fire InteractBlockEvent.Secondary and InteractContainerEvent.Open. */ @Overwrite - public InteractionResult useItemOn(final ServerPlayer playerIn, final Level worldIn, final ItemStack stackIn, final InteractionHand handIn, final BlockHitResult blockRaytraceResultIn) { - final BlockPos blockpos = blockRaytraceResultIn.getBlockPos(); - final BlockState blockstate = worldIn.getBlockState(blockpos); + public InteractionResult useItemOn(final ServerPlayer playerIn, final Level worldIn, final ItemStack stackIn, final InteractionHand handIn, final BlockHitResult blockHitResultIn) { + final BlockPos blockPos = blockHitResultIn.getBlockPos(); + final BlockState blockState = worldIn.getBlockState(blockPos); + // Sponge start - final BlockSnapshot snapshot = ((ServerWorld) (worldIn)).createSnapshot(VecHelper.toVector3i(blockpos)); - final Vector3d hitVec = Vector3d.from(blockRaytraceResultIn.getBlockPos().getX(), blockRaytraceResultIn.getBlockPos().getY(), blockRaytraceResultIn.getBlockPos().getZ()); - final org.spongepowered.api.util.Direction direction = DirectionFacingProvider.INSTANCE.getKey(blockRaytraceResultIn.getDirection()).get(); - final InteractBlockEvent.Secondary event = SpongeCommonEventFactory.callInteractBlockEventSecondary(playerIn, stackIn, hitVec, snapshot, direction, handIn); + final InteractBlockEvent.Secondary event; + try (final CauseStackManager.StackFrame frame = PhaseTracker.getCauseStackManager().pushCauseFrame()) { + event = SpongeCommonEventFactory.createInteractBlockEventSecondary(playerIn, (ServerLevel) worldIn, stackIn, handIn, + blockHitResultIn, frame); + SpongeCommon.post(event); + } + final Tristate useItem = event.useItemResult(); final Tristate useBlock = event.useBlockResult(); ((ServerPlayerGameModeBridge) this).bridge$setInteractBlockRightClickCancelled(event.isCancelled()); if (event.isCancelled()) { - return InteractionResult.FAIL; + return InteractionResult.FAIL; // On the server, the interaction result only dictates whether to swing the arm or not } // Sponge end + if (this.gameModeForPlayer == GameType.SPECTATOR) { - final MenuProvider inamedcontainerprovider = blockstate.getMenuProvider(worldIn, blockpos); - if (inamedcontainerprovider != null) { - playerIn.openMenu(inamedcontainerprovider); - final Vector3i pos = VecHelper.toVector3i(blockRaytraceResultIn.getBlockPos()); + final MenuProvider menuProvider = blockState.getMenuProvider(worldIn, blockPos); + if (menuProvider != null) { + playerIn.openMenu(menuProvider); + + // Sponge start + final Vector3i pos = VecHelper.toVector3i(blockHitResultIn.getBlockPos()); final ServerLocation location = ServerLocation.of((ServerWorld) worldIn, pos); try (final CauseStackManager.StackFrame frame = PhaseTracker.getCauseStackManager().pushCauseFrame()) { frame.pushCause(playerIn); @@ -122,19 +126,39 @@ public InteractionResult useItemOn(final ServerPlayer playerIn, final Level worl return InteractionResult.SUCCESS; } } + // Sponge end + return InteractionResult.SUCCESS; } else { return InteractionResult.PASS; } } else { - final boolean flag = !playerIn.getMainHandItem().isEmpty() || !playerIn.getOffhandItem().isEmpty(); - final boolean flag1 = playerIn.isSecondaryUseActive() && flag; + // Forge start + final UseOnContext useOnContext = new UseOnContext(playerIn, handIn, blockHitResultIn); + if (useItem != Tristate.FALSE) { + InteractionResult interactionResult = ((ItemStackBridge) (Object) stackIn).bridge$onItemUseFirst(useOnContext); + if (interactionResult != InteractionResult.PASS) { + return interactionResult; + } + } + // Forge end + + final boolean hasItemInAnyHand = !playerIn.getMainHandItem().isEmpty() || !playerIn.getOffhandItem().isEmpty(); + final boolean sneakUse = playerIn.isSecondaryUseActive() && hasItemInAnyHand + // Forge start + && (!((ItemStackBridge) (Object) playerIn.getMainHandItem()).bridge$doesSneakBypassUse(worldIn, blockPos, playerIn) + || !((ItemStackBridge) (Object) playerIn.getOffhandItem()).bridge$doesSneakBypassUse(worldIn, blockPos, playerIn)); + // Forge end + final ItemStack copiedStack = stackIn.copy(); - if (useBlock != Tristate.FALSE && !flag1) { // Sponge check useBlock - final AbstractContainerMenu lastOpenContainer = playerIn.containerMenu; - final InteractionResult result = blockstate.use(worldIn, playerIn, handIn, blockRaytraceResultIn); + if (useBlock != Tristate.FALSE && !sneakUse) { // Sponge check useBlock + final AbstractContainerMenu lastOpenContainer = playerIn.containerMenu; // Sponge + + final InteractionResult result = blockState.use(worldIn, playerIn, handIn, blockHitResultIn); + + // Sponge start if (result.consumesAction() && lastOpenContainer != playerIn.containerMenu) { - final Vector3i pos = VecHelper.toVector3i(blockRaytraceResultIn.getBlockPos()); + final Vector3i pos = VecHelper.toVector3i(blockHitResultIn.getBlockPos()); final ServerLocation location = ServerLocation.of((ServerWorld) worldIn, pos); try (final CauseStackManager.StackFrame frame = PhaseTracker.getCauseStackManager().pushCauseFrame()) { frame.pushCause(playerIn); @@ -145,8 +169,10 @@ public InteractionResult useItemOn(final ServerPlayer playerIn, final Level worl } } } + // Sponge end + if (result.consumesAction()) { - CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(playerIn, blockpos, copiedStack); + CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(playerIn, blockPos, copiedStack); return result; } } @@ -158,30 +184,31 @@ public InteractionResult useItemOn(final ServerPlayer playerIn, final Level worl return InteractionResult.PASS; } // Sponge end - final UseOnContext itemusecontext = new UseOnContext(playerIn, handIn, blockRaytraceResultIn); + final InteractionResult result; if (this.isCreative()) { final int i = stackIn.getCount(); - result = stackIn.useOn(itemusecontext); + result = stackIn.useOn(useOnContext); stackIn.setCount(i); } else { - result = stackIn.useOn(itemusecontext); + result = stackIn.useOn(useOnContext); + // Sponge start - log change in hand final PhaseContext<@NonNull ?> context = PhaseTracker.SERVER.getPhaseContext(); final TransactionalCaptureSupplier transactor = context.getTransactor(); - transactor.logPlayerInventoryChange(this.player, PlayerInventoryTransaction.EventCreator.STANDARD); - this.player.inventoryMenu.broadcastChanges(); + transactor.logPlayerInventoryChange(playerIn, PlayerInventoryTransaction.EventCreator.STANDARD); + playerIn.inventoryMenu.broadcastChanges(); // Sponge end } if (result.consumesAction()) { - CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(playerIn, blockpos, copiedStack); + CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(playerIn, blockPos, copiedStack); } return result; } else { // Sponge start - if(useBlock == Tristate.FALSE && !flag1) { + if (useBlock == Tristate.FALSE && !sneakUse) { ((ServerPlayerGameModeBridge) this).bridge$setInteractBlockRightClickCancelled(true); } // Sponge end