From 7ed6f4f8de54df2cd466669f6a65dafd25a62579 Mon Sep 17 00:00:00 2001 From: Gabriel Harris-Rouquette Date: Sat, 18 Jan 2025 21:15:17 -0800 Subject: [PATCH 1/5] chore: replace ServerPlayer.die overwrite Fixes #4159 Makes use of MixinExtras wrappers and sugar to share local variables across the several injection points for our death event. --- .../core/server/level/ServerPlayerMixin.java | 352 ++++++++++-------- .../mixin/core/world/entity/EntityMixin.java | 2 - .../core/world/entity/LivingEntityMixin.java | 17 +- .../core/world/entity/player/PlayerMixin.java | 6 +- 4 files changed, 194 insertions(+), 183 deletions(-) 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 5a02e346ed3..3c183db8676 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 @@ -25,24 +25,24 @@ package org.spongepowered.common.mixin.core.server.level; import com.google.common.collect.ImmutableSet; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Share; +import com.llamalad7.mixinextras.sugar.ref.LocalRef; import com.mojang.datafixers.util.Either; import io.netty.channel.Channel; -import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; -import net.minecraft.ChatFormatting; import net.minecraft.advancements.CriteriaTriggers; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.PacketSendListener; import net.minecraft.network.chat.ChatType; -import net.minecraft.network.chat.HoverEvent; import net.minecraft.network.chat.OutgoingChatMessage; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.network.protocol.game.ClientboundChangeDifficultyPacket; import net.minecraft.network.protocol.game.ClientboundGameEventPacket; import net.minecraft.network.protocol.game.ClientboundPlayerAbilitiesPacket; -import net.minecraft.network.protocol.game.ClientboundPlayerCombatKillPacket; import net.minecraft.network.protocol.game.ClientboundRespawnPacket; import net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket; import net.minecraft.resources.ResourceKey; @@ -53,7 +53,6 @@ import net.minecraft.server.level.TicketType; import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.server.players.PlayerList; -import net.minecraft.stats.Stat; import net.minecraft.stats.Stats; import net.minecraft.util.Unit; import net.minecraft.world.InteractionHand; @@ -75,8 +74,6 @@ import net.minecraft.world.level.storage.LevelData; import net.minecraft.world.phys.Vec3; import net.minecraft.world.scores.PlayerTeam; -import net.minecraft.world.scores.Team; -import net.minecraft.world.scores.criteria.ObjectiveCriteria; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.Sponge; import org.spongepowered.api.adventure.Audiences; @@ -114,10 +111,12 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.ModifyVariable; import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.Slice; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.common.SpongeCommon; @@ -172,8 +171,6 @@ public abstract class ServerPlayerMixin extends PlayerMixin implements SubjectBr @Shadow private int lastSentFood; @Shadow public abstract ServerLevel shadow$serverLevel(); - @Shadow public abstract void shadow$resetStat(final Stat statistic); - @Shadow protected abstract void shadow$tellNeutralMobsThatIDied(); @Shadow protected abstract void shadow$triggerDimensionChangeTriggers(ServerLevel serverworld); @Shadow public abstract void shadow$doCloseContainer(); @Shadow public abstract boolean shadow$setGameMode(GameType param0); @@ -182,7 +179,11 @@ public abstract class ServerPlayerMixin extends PlayerMixin implements SubjectBr private net.minecraft.network.chat.@Nullable Component impl$connectionMessage; private Locale impl$language = Locales.DEFAULT; private Scoreboard impl$scoreboard = Sponge.game().server().serverScoreboard().get(); - @Nullable private Boolean impl$keepInventory = null; + // Note that this field cannot be reset until the player has been respawned because + // after death, the server player will remain in the death state, and only when the player + // is actually respawned, will their inventory be transferred to the new player instance. + @Nullable + private Boolean impl$keepInventory = null; // Used to restore original item received in a packet after canceling an event private int impl$viewDistance; private int impl$skinPartMask; @@ -190,7 +191,8 @@ public abstract class ServerPlayerMixin extends PlayerMixin implements SubjectBr private final PlayerOwnBorderListener impl$borderListener = new PlayerOwnBorderListener((net.minecraft.server.level.ServerPlayer) (Object) this); private boolean impl$sleepingIgnored; private boolean impl$noGameModeEvent; - @Nullable private WorldBorder impl$worldBorder; + @Nullable + private WorldBorder impl$worldBorder; private ServerLevel impl$respawnLevel; @Override @@ -236,7 +238,7 @@ public abstract class ServerPlayerMixin extends PlayerMixin implements SubjectBr this.connection.resetPosition(); } else { this.bridge$changeDimension(new DimensionTransition(level, VecHelper.toVanillaVector3d(pos), thisPlayer.getKnownMovement(), - this.shadow$getYRot(), this.shadow$getXRot(), DimensionTransition.DO_NOTHING)); + this.shadow$getYRot(), this.shadow$getXRot(), DimensionTransition.DO_NOTHING)); } return true; } @@ -254,7 +256,7 @@ public abstract class ServerPlayerMixin extends PlayerMixin implements SubjectBr message, message, (ServerPlayer) this - ); + ); if (Sponge.eventManager().post(kickEvent)) { return false; } @@ -310,17 +312,23 @@ public abstract class ServerPlayerMixin extends PlayerMixin implements SubjectBr public void bridge$replaceScoreboard(@org.checkerframework.checker.nullness.qual.Nullable Scoreboard scoreboard) { if (scoreboard == null) { scoreboard = Sponge.game().server().serverScoreboard() - .orElseThrow(() -> new IllegalStateException("Server does not have a valid scoreboard")); + .orElseThrow(() -> new IllegalStateException("Server does not have a valid scoreboard")); } this.impl$scoreboard = scoreboard; } @Override public boolean bridge$keepInventory() { - if (this.impl$keepInventory == null) { - return this.shadow$level().getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY); + return Objects.requireNonNullElseGet(this.impl$keepInventory, () -> this.shadow$level().getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY)); + } + + @Override + protected void impl$dropInventoryWrapForPlayerOverride( + final LivingEntity instance, final Operation original + ) { + if (this.impl$keepInventory == null || !this.impl$keepInventory) { + original.call(instance); } - return this.impl$keepInventory; } @Override @@ -341,9 +349,9 @@ public abstract class ServerPlayerMixin extends PlayerMixin implements SubjectBr final int mask = this.shadow$getEntityData().get(DATA_PLAYER_MODE_CUSTOMISATION); if (this.impl$skinPartMask != mask) { this.impl$skinParts = Sponge.game().registry(RegistryTypes.SKIN_PART).stream() - .map(part -> (SpongeSkinPart) part) - .filter(part -> part.test(mask)) - .collect(ImmutableSet.toImmutableSet()); + .map(part -> (SpongeSkinPart) part) + .filter(part -> part.test(mask)) + .collect(ImmutableSet.toImmutableSet()); this.impl$skinPartMask = mask; } @@ -412,7 +420,7 @@ public void teleportTo(final ServerLevel world, final double x, final double y, final var thisPlayer = (net.minecraft.server.level.ServerPlayer) (Object) this; this.bridge$changeDimension(new DimensionTransition(world, new Vec3(x, y, z), Vec3.ZERO, yaw, pitch, - e -> world.getChunkSource().addRegionTicket(TicketType.POST_TELEPORT, e.chunkPosition(), 1, thisPlayer.getId()))); + e -> world.getChunkSource().addRegionTicket(TicketType.POST_TELEPORT, e.chunkPosition(), 1, thisPlayer.getId()))); } } @@ -449,8 +457,8 @@ public void teleportTo(final ServerLevel world, final double x, final double y, // Sponge End if (newLevel.dimension() == oldLevel.dimension()) { // actually no dimension change - this.connection.teleport(transition.pos().x, transition.pos().y, transition.pos().z, transition.yRot(), transition.xRot()); - this.connection.resetPosition(); + this.connection.teleport(transition.pos().x, transition.pos().y, transition.pos().z, transition.yRot(), transition.xRot()); + this.connection.resetPosition(); transition.postDimensionTransition().onTransition(thisPlayer); // TODO setYHeadRot after would rotate event result return thisPlayer; @@ -490,30 +498,33 @@ public void teleportTo(final ServerLevel world, final double x, final double y, this.lastSentFood = -1; // Sponge Start TODO cause/context like in impl$fireDimensionTransitionEvents Sponge.eventManager().post( - SpongeEventFactory.createChangeEntityWorldEventPost( - PhaseTracker.getCauseStackManager().currentCause(), - (org.spongepowered.api.entity.Entity) this, - (ServerWorld) oldLevel, - (ServerWorld) newLevel, - (ServerWorld) originalNewLevel - ) + SpongeEventFactory.createChangeEntityWorldEventPost( + PhaseTracker.getCauseStackManager().currentCause(), + (org.spongepowered.api.entity.Entity) this, + (ServerWorld) oldLevel, + (ServerWorld) newLevel, + (ServerWorld) originalNewLevel + ) ); // Sponge End return thisPlayer; } + @Unique @Nullable - private DimensionTransition impl$fireDimensionTransitionEvents(final DimensionTransition originalTransition, - final net.minecraft.server.level.ServerPlayer thisPlayer) { + private DimensionTransition impl$fireDimensionTransitionEvents( + final DimensionTransition originalTransition, + final net.minecraft.server.level.ServerPlayer thisPlayer + ) { var transition = originalTransition; var isDimensionChange = transition.newLevel() != thisPlayer.serverLevel(); if (!this.impl$moveEventsFired) { final var contextToSwitchTo = EntityPhase.State.PORTAL_DIMENSION_CHANGE.createPhaseContext(PhaseTracker.getInstance()).worldChange() - .player(); + .player(); final boolean hasMovementContext = PhaseTracker.SERVER.currentContext().containsKey(EventContextKeys.MOVEMENT_TYPE); try (final TeleportContext context = contextToSwitchTo.buildAndSwitch(); - final CauseStackManager.StackFrame frame = PhaseTracker.getCauseStackManager().pushCauseFrame()) { + final CauseStackManager.StackFrame frame = PhaseTracker.getCauseStackManager().pushCauseFrame()) { frame.pushCause(thisPlayer); if (!hasMovementContext) { // TODO we should be able to detect normal plugin code though @@ -532,11 +543,11 @@ public void teleportTo(final ServerLevel world, final double x, final double y, } if (preEvent.destinationWorld() != preEvent.originalDestinationWorld()) { transition = new DimensionTransition((ServerLevel) preEvent.destinationWorld(), - transition.pos(), - transition.speed(), - transition.yRot(), transition.xRot(), - transition.missingRespawnBlock(), - transition.postDimensionTransition()); + transition.pos(), + transition.speed(), + transition.yRot(), transition.xRot(), + transition.missingRespawnBlock(), + transition.postDimensionTransition()); } } @@ -558,11 +569,11 @@ public void teleportTo(final ServerLevel world, final double x, final double y, if (newDest != originalDest) { // if changed override the DimensionTransition transition = new DimensionTransition(transition.newLevel(), - VecHelper.toVanillaVector3d(newDest), - transition.speed(), - transition.yRot(), transition.xRot(), - transition.missingRespawnBlock(), - transition.postDimensionTransition()); + VecHelper.toVanillaVector3d(newDest), + transition.speed(), + transition.yRot(), transition.xRot(), + transition.missingRespawnBlock(), + transition.postDimensionTransition()); } final Vector3d toRot = new Vector3d(transition.xRot(), transition.yRot(), 0); @@ -574,11 +585,11 @@ public void teleportTo(final ServerLevel world, final double x, final double y, } if (toRot != newToRot) { transition = new DimensionTransition(transition.newLevel(), - transition.pos(), - transition.speed(), - (float) newToRot.y(), (float) newToRot.x(), - transition.missingRespawnBlock(), - transition.postDimensionTransition()); + transition.pos(), + transition.speed(), + (float) newToRot.y(), (float) newToRot.x(), + transition.missingRespawnBlock(), + transition.postDimensionTransition()); } } @@ -595,107 +606,121 @@ public void teleportTo(final ServerLevel world, final double x, final double y, } @Redirect( - method = {"openMenu", "openHorseInventory"}, - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/server/level/ServerPlayer;closeContainer()V" - ) + method = {"openMenu", "openHorseInventory"}, + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/level/ServerPlayer;closeContainer()V" + ) ) private void impl$closePreviousContainer(final net.minecraft.server.level.ServerPlayer self) { this.shadow$doCloseContainer(); } - /** - * @author blood - May 12th, 2016 - * @author gabizou - June 3rd, 2016 - * @author gabizou - February 22nd, 2020 - Minecraft 1.14.3 - * @reason SpongeForge requires an overwrite so we do it here instead. This handles player death events. - */ - @Overwrite - public void die(final DamageSource cause) { - // Sponge start - Call Destruct Death Event - if (PlatformHooks.INSTANCE.getEventHooks().callPlayerDestruction((net.minecraft.server.level.ServerPlayer) (Object) this, cause)) { - return; + @Inject( + method = "die", + at = @At("HEAD"), + cancellable = true + ) + private void impl$fireDestructEntityEvent( + final DamageSource cause, final CallbackInfo ci, + @Share("sponge-event") LocalRef event + ) { + event.set(SpongeCommonEventFactory.callDestructEntityEventDeath( + (net.minecraft.server.level.ServerPlayer) (Object) this, + cause, + Audiences.server() + )); + if (event.get().isCancelled()) { + ci.cancel(); } - final DestructEntityEvent.Death event = SpongeCommonEventFactory.callDestructEntityEventDeath((net.minecraft.server.level.ServerPlayer) (Object) this, cause, - Audiences.server()); - if (event.isCancelled()) { - return; + } + + @ModifyExpressionValue( + method = "die", + at = @At( + value = "INVOKE", target = "Lnet/minecraft/world/level/GameRules;getBoolean(Lnet/minecraft/world/level/GameRules$Key;)Z" + ), + slice = @Slice( + from = @At("HEAD"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/damagesource/CombatTracker;getDeathMessage()Lnet/minecraft/network/chat/Component;") + ) + ) + private boolean impl$onlySendMessageIfEventCallsForIt( + boolean gameRules, + @Share("sponge-event") LocalRef event + ) { + final var spongeEvent = event.get(); + return gameRules && (spongeEvent == null || !spongeEvent.isMessageCancelled()); + } + + @ModifyExpressionValue( + method = "die", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/damagesource/CombatTracker;getDeathMessage()Lnet/minecraft/network/chat/Component;") + ) + private net.minecraft.network.chat.Component impl$useEventMessageIfUnset( + final net.minecraft.network.chat.Component original, + @Share("sponge-event") LocalRef event + ) { + final var spongeEvent = event.get(); + if (spongeEvent == null) { + return original; } - // Sponge end - - final var level = this.shadow$level(); - final boolean flag = level.getGameRules().getBoolean(GameRules.RULE_SHOWDEATHMESSAGES) && !event.isMessageCancelled(); - if (flag) { - final net.minecraft.network.chat.Component component = this.shadow$getCombatTracker().getDeathMessage(); - final ClientboundPlayerCombatKillPacket packet = new ClientboundPlayerCombatKillPacket(this.shadow$getId(), component); - this.connection.send(packet, PacketSendListener.exceptionallySend(() -> { - final String s = component.getString(256); - final net.minecraft.network.chat.Component itextcomponent1 = net.minecraft.network.chat.Component.translatable("death.attack.message_too_long", net.minecraft.network.chat.Component.literal(s).withStyle(ChatFormatting.YELLOW)); - final net.minecraft.network.chat.Component itextcomponent2 = net.minecraft.network.chat.Component.translatable("death.attack.even_more_magic", this.shadow$getDisplayName()) - .withStyle((p_212357_1_) -> p_212357_1_.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, itextcomponent1))); - return new ClientboundPlayerCombatKillPacket(this.shadow$getId(), itextcomponent2); - })); - final Team team = this.shadow$getTeam(); - if (team != null && team.getDeathMessageVisibility() != Team.Visibility.ALWAYS) { - if (team.getDeathMessageVisibility() == Team.Visibility.HIDE_FOR_OTHER_TEAMS) { - this.server.getPlayerList().broadcastSystemToTeam( - (net.minecraft.server.level.ServerPlayer) (Object) this, component); - } else if (team.getDeathMessageVisibility() == Team.Visibility.HIDE_FOR_OWN_TEAM) { - this.server.getPlayerList().broadcastSystemToAllExceptTeam( - (net.minecraft.server.level.ServerPlayer) (Object) this, component); - } - } else { - final Component message = event.message(); - // Sponge start - use the event audience - if (message != Component.empty()) { - event.audience().ifPresent(eventChannel -> eventChannel.sendMessage(Identity.nil(), message)); - } - // Sponge end - // this.server.getPlayerList().sendMessage(itextcomponent); - } - } else { - this.connection.send( - new ClientboundPlayerCombatKillPacket(this.shadow$getId(), net.minecraft.network.chat.Component.empty())); + if (Component.IS_NOT_EMPTY.test(spongeEvent.message())) { + return SpongeAdventure.asVanilla(spongeEvent.message()); } - this.shadow$removeEntitiesOnShoulder(); - if (level.getGameRules().getBoolean(GameRules.RULE_FORGIVE_DEAD_PLAYERS)) { - this.shadow$tellNeutralMobsThatIDied(); - } + return original; + } - // Sponge Start - update the keep inventory flag for dropping inventory - // during the death update ticks - this.impl$keepInventory = event.keepInventory(); + @WrapOperation( + method = "die", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/players/PlayerList;broadcastSystemMessage(Lnet/minecraft/network/chat/Component;Z)V" + ) + ) + private void impl$useEventAudienceToSendServerMessage( + final PlayerList serverList, final net.minecraft.network.chat.Component message, + final boolean isSystem, final Operation original, + final @Share("sponge-event") LocalRef event + ) { + final var spongeEvent = event.get(); + if (spongeEvent == null) { + // If we don't have a sponge event, just call the original + original.call(serverList, message, isSystem); + return; + } + // Otherwise, we will use our event's prescribed audience to send the message that has + // been modified by the event listeners. + final Component eventMessage = spongeEvent.message(); + if (eventMessage != Component.empty()) { + spongeEvent.audience().ifPresent(eventChannel -> eventChannel.sendMessage(eventMessage)); + } + } - if (!this.shadow$isSpectator() && level instanceof ServerLevel sLevel) { - this.shadow$dropAllDeathLoot(sLevel, cause); + @Inject( + method = "die", + at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayer;isSpectator()Z") + ) + private void impl$setKeepInventoryFromEvent( + final DamageSource source, final CallbackInfo ci, + final @Share("sponge-event") LocalRef event + ) { + if (event.get() == null) { + return; } - // Sponge End + this.impl$keepInventory = event.get().keepInventory(); + } - this.shadow$getScoreboard().forAllObjectives( - ObjectiveCriteria.DEATH_COUNT, (net.minecraft.server.level.ServerPlayer) (Object) this, sa -> sa.set(sa.get() + 1)); - final LivingEntity livingentity = this.shadow$getKillCredit(); - if (livingentity != null) { - this.shadow$awardStat(Stats.ENTITY_KILLED_BY.get(livingentity.getType())); - livingentity.awardKillScore((net.minecraft.server.level.ServerPlayer) (Object) this, this.deathScore, cause); - this.shadow$createWitherRose(livingentity); - } - - level.broadcastEntityEvent((net.minecraft.server.level.ServerPlayer) (Object) this, (byte) 3); - this.shadow$awardStat(Stats.DEATHS); - this.shadow$resetStat(Stats.CUSTOM.get(Stats.TIME_SINCE_DEATH)); - this.shadow$resetStat(Stats.CUSTOM.get(Stats.TIME_SINCE_REST)); - this.shadow$clearFire(); - this.shadow$setSharedFlag(0, false); - this.shadow$getCombatTracker().recheckStatus(); - } - - @Redirect(method = "restoreFrom(Lnet/minecraft/server/level/ServerPlayer;Z)V", - at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/level/GameRules;getBoolean(Lnet/minecraft/world/level/GameRules$Key;)Z")) - private boolean tracker$useKeepFromBridge(final GameRules gameRules, final GameRules.Key key, - final net.minecraft.server.level.ServerPlayer corpse, final boolean keepEverything) { + @ModifyExpressionValue( + method = "restoreFrom", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/GameRules;getBoolean(Lnet/minecraft/world/level/GameRules$Key;)Z") + ) + private boolean tracker$useKeepFromBridge( + boolean original, + net.minecraft.server.level.ServerPlayer corpse, + boolean wonGame + ) { final boolean keep = ((PlayerBridge) corpse).bridge$keepInventory(); // Override Keep Inventory GameRule? if (!keep) { // Copy corpse inventory to respawned player @@ -709,11 +734,10 @@ public void die(final DamageSource cause) { @Inject(method = "restoreFrom(Lnet/minecraft/server/level/ServerPlayer;Z)V", at = @At("HEAD")) private void impl$copyDataOnRespawn(final net.minecraft.server.level.ServerPlayer oldPlayer, final boolean respawnFromEnd, final CallbackInfo ci) { // Copy Sponge data - if (oldPlayer instanceof DataCompoundHolder) { - final DataCompoundHolder oldEntity = (DataCompoundHolder) oldPlayer; + if (oldPlayer instanceof DataCompoundHolder oldEntity) { DataUtil.syncDataToTag(oldEntity); final CompoundTag compound = oldEntity.data$getCompound(); - ((DataCompoundHolder) this).data$setCompound(compound); + this.data$setCompound(compound); DataUtil.syncTagToData(this); } @@ -737,22 +761,22 @@ public void die(final DamageSource cause) { final Locale newLocale = LocaleCache.getLocale(info.language()); final ImmutableSet skinParts = Sponge.game().registry(RegistryTypes.SKIN_PART).stream() - .map(part -> (SpongeSkinPart) part) - .filter(part -> part.test(info.modelCustomisation())) - .collect(ImmutableSet.toImmutableSet()); + .map(part -> (SpongeSkinPart) part) + .filter(part -> part.test(info.modelCustomisation())) + .collect(ImmutableSet.toImmutableSet()); final int viewDistance = info.viewDistance(); // Post before the player values are updated try (final CauseStackManager.StackFrame frame = PhaseTracker.getCauseStackManager().pushCauseFrame()) { final ChatVisibility visibility = (ChatVisibility) (Object) info.chatVisibility(); final PlayerChangeClientSettingsEvent event = SpongeEventFactory.createPlayerChangeClientSettingsEvent( - frame.currentCause(), - visibility, - skinParts, - newLocale, - (ServerPlayer) this, - info.chatColors(), - viewDistance); + frame.currentCause(), + visibility, + skinParts, + newLocale, + (ServerPlayer) this, + info.chatColors(), + viewDistance); SpongeCommon.post(event); } } @@ -773,7 +797,7 @@ public void die(final DamageSource cause) { } @Inject(method = "sendSystemMessage(Lnet/minecraft/network/chat/Component;Z)V", - cancellable = true, at = @At("HEAD")) + cancellable = true, at = @At("HEAD")) public void sendMessage(final net.minecraft.network.chat.Component $$0, final boolean $$1, final CallbackInfo ci) { if (this.impl$isFake) { // Don't bother sending messages to fake players @@ -800,7 +824,7 @@ public void sendMessage(final OutgoingChatMessage $$0, final boolean $$1, final ) { final ItemStack itemInHand = this.shadow$getItemInHand(hand); final InteractEntityEvent.Secondary event = SpongeCommonEventFactory.callInteractEntityEventSecondary((net.minecraft.server.level.ServerPlayer) (Object) this, - itemInHand, entityToInteractOn, hand, null); + itemInHand, entityToInteractOn, hand, null); if (event.isCancelled()) { this.containerMenu.sendAllDataToRemote(); if (itemInHand.getItem() == Items.LEAD && entityToInteractOn instanceof Mob) { @@ -860,14 +884,14 @@ public void sendMessage(final OutgoingChatMessage $$0, final boolean $$1, final } final DataTransactionResult transaction = DataTransactionResult.builder() - .replace(Value.immutableOf(Keys.GAME_MODE, (GameMode) (Object) this.gameMode.getGameModeForPlayer())) - .success(Value.immutableOf(Keys.GAME_MODE, (GameMode) (Object) value)) - .result(DataTransactionResult.Type.SUCCESS) - .build(); + .replace(Value.immutableOf(Keys.GAME_MODE, (GameMode) (Object) this.gameMode.getGameModeForPlayer())) + .success(Value.immutableOf(Keys.GAME_MODE, (GameMode) (Object) value)) + .result(DataTransactionResult.Type.SUCCESS) + .build(); final ChangeDataHolderEvent.ValueChange - event = - SpongeEventFactory.createChangeDataHolderEventValueChange(PhaseTracker.getCauseStackManager().currentCause(), transaction, (DataHolder.Mutable) this); + event = + SpongeEventFactory.createChangeDataHolderEventValueChange(PhaseTracker.getCauseStackManager().currentCause(), transaction, (DataHolder.Mutable) this); Sponge.eventManager().post(event); @@ -876,8 +900,8 @@ public void sendMessage(final OutgoingChatMessage $$0, final boolean $$1, final } return (GameType) (Object) event.endResult().successfulValue(Keys.GAME_MODE) - .map(Value::get) - .orElse((GameMode) (Object) value); + .map(Value::get) + .orElse((GameMode) (Object) value); } @Override @@ -906,9 +930,9 @@ public void sendMessage(final OutgoingChatMessage $$0, final boolean $$1, final } /** + * @return True if PVP allowed * @author Zidane * @reason Have PVP check if the world allows it or not - * @return True if PVP allowed */ @Overwrite private boolean isPvpAllowed() { @@ -925,12 +949,12 @@ public Entity changeDimension(final DimensionTransition transition) { var playerRespawnDestination = this.server.getLevel(player.getRespawnDimension()); if (playerRespawnDestination == null) { SpongeCommon.logger().warn("The player '{}' respawn location was located in a world that isn't loaded or doesn't exist. This is not safe so " - + "the player will be moved to the spawn of the default world.", player.getGameProfile().getName()); + + "the player will be moved to the spawn of the default world.", player.getGameProfile().getName()); playerRespawnDestination = player.server.overworld(); } final RespawnPlayerEvent.SelectWorld event = SpongeEventFactory.createRespawnPlayerEventSelectWorld(PhaseTracker.getCauseStackManager().currentCause(), - (ServerWorld) playerRespawnDestination, (ServerWorld) player.serverLevel(), (ServerWorld) playerRespawnDestination, (ServerPlayer) player); + (ServerWorld) playerRespawnDestination, (ServerWorld) player.serverLevel(), (ServerWorld) playerRespawnDestination, (ServerPlayer) player); SpongeCommon.post(event); this.impl$respawnLevel = (ServerLevel) event.destinationWorld(); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java index 6fac65f2ceb..f641139545d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/EntityMixin.java @@ -165,8 +165,6 @@ public abstract class EntityMixin implements EntityBridge, PlatformEntityBridge, @Shadow @Nullable public abstract Entity shadow$getVehicle(); @Shadow public abstract AABB shadow$getBoundingBox(); @Shadow @Nullable public abstract PlayerTeam shadow$getTeam(); - @Shadow public abstract void shadow$clearFire(); - @Shadow protected abstract void shadow$setSharedFlag(int flag, boolean set); @Shadow public abstract SynchedEntityData shadow$getEntityData(); @Shadow public abstract net.minecraft.world.phys.Vec3 shadow$getDeltaMovement(); @Shadow public abstract void shadow$setDeltaMovement(net.minecraft.world.phys.Vec3 motion); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin.java index c36f90a02f7..0ded198ee23 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin.java @@ -24,6 +24,8 @@ */ package org.spongepowered.common.mixin.core.world.entity; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.particles.ParticleOptions; @@ -72,7 +74,6 @@ import org.spongepowered.common.SpongeCommon; import org.spongepowered.common.bridge.data.VanishableBridge; import org.spongepowered.common.bridge.world.entity.LivingEntityBridge; -import org.spongepowered.common.bridge.world.entity.player.PlayerBridge; import org.spongepowered.common.bridge.world.level.LevelBridge; import org.spongepowered.common.entity.living.human.HumanEntity; import org.spongepowered.common.event.ShouldFire; @@ -94,7 +95,6 @@ public abstract class LivingEntityMixin extends EntityMixin implements LivingEnt // @formatter:off @Shadow protected int useItemRemaining; @Shadow protected boolean dead; - @Shadow protected int deathScore; @Shadow protected ItemStack useItem; @Shadow @Nullable private DamageSource lastDamageSource; @Shadow private long lastDamageStamp; @@ -110,10 +110,6 @@ public abstract class LivingEntityMixin extends EntityMixin implements LivingEnt @Shadow public abstract Optional shadow$getSleepingPos(); @Shadow protected abstract void shadow$spawnItemParticles(ItemStack stack, int count); @Shadow public abstract ItemStack shadow$getItemInHand(InteractionHand hand); - @Shadow protected abstract void shadow$dropEquipment(); - @Shadow protected abstract void shadow$dropAllDeathLoot(ServerLevel level, DamageSource damageSourceIn); - @Shadow @Nullable public abstract LivingEntity shadow$getKillCredit(); - @Shadow protected abstract void shadow$createWitherRose(@Nullable LivingEntity p_226298_1_); @Shadow public abstract float shadow$getMaxHealth(); @Shadow public abstract AttributeMap shadow$getAttributes(); @Shadow public abstract void shadow$clearSleepingPos(); @@ -171,16 +167,13 @@ public abstract class LivingEntityMixin extends EntityMixin implements LivingEnt } } - @Redirect(method = "dropAllDeathLoot", + @WrapOperation(method = "dropAllDeathLoot", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;dropEquipment()V" ) ) - private void tracker$dropInventory(final LivingEntity thisEntity) { - if (thisEntity instanceof PlayerBridge && ((PlayerBridge) thisEntity).bridge$keepInventory()) { - return; - } - this.shadow$dropEquipment(); + protected void impl$dropInventoryWrapForPlayerOverride(final LivingEntity instance, final Operation original) { + original.call(instance); } @Inject(method = "pushEntities", at = @At("HEAD"), cancellable = true) diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin.java index 59860310fb6..ef859f8e032 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin.java @@ -35,7 +35,6 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundSource; -import net.minecraft.stats.Stat; import net.minecraft.util.Unit; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; @@ -108,9 +107,6 @@ public abstract class PlayerMixin extends LivingEntityMixin implements PlayerBri @Shadow public abstract Scoreboard shadow$getScoreboard(); @Shadow public abstract boolean shadow$isCreative(); @Shadow public abstract String shadow$getScoreboardName(); - @Shadow public abstract void shadow$awardStat(Stat stat); - @Shadow public abstract Component shadow$getDisplayName(); - @Shadow protected abstract void shadow$removeEntitiesOnShoulder(); @Shadow public abstract void shadow$awardStat(ResourceLocation stat); @Shadow public abstract Inventory shadow$getInventory(); @Shadow public Either shadow$startSleepInBed(final BlockPos param0) { @@ -227,7 +223,7 @@ public abstract class PlayerMixin extends LivingEntityMixin implements PlayerBri method = { "playSound(Lnet/minecraft/sounds/SoundEvent;FF)V", "giveExperienceLevels(I)V", - "eat(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/item/ItemStack;)Lnet/minecraft/world/item/ItemStack;" + "eat(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/food/FoodProperties;)Lnet/minecraft/world/item/ItemStack;" }, at = @At( value = "INVOKE", From fbaf18442abc2ffb813bc5508d4d9c74a8197d1b Mon Sep 17 00:00:00 2001 From: Gabriel Harris-Rouquette Date: Sat, 18 Jan 2025 21:52:00 -0800 Subject: [PATCH 2/5] chore: generify event bridges Since we can assert the event is the same one being generated, we can implicitly cast via the compiler. Just like DataProcessors do, we have some liberty to do this with events. --- .../bridge/event/ForgeEventBridge_Forge.java | 8 ++-- .../forge/launch/event/ForgeEventManager.java | 9 ++-- ...tityTravelToDimensionEventMixin_Forge.java | 25 ++++++----- ...layerChangedDimensionEventMixin_Forge.java | 10 ++--- .../event/world/BlockEventMixin_Forge.java | 3 +- .../BlockEvent_BreakEventMixin_Forge.java | 33 +++++++-------- .../bridge/event/NeoEventBridge_Neo.java | 8 ++-- .../launch/event/NeoEventManager.java | 6 +-- ...EntityTravelToDimensionEventMixin_Neo.java | 17 ++++---- ..._PlayerChangedDimensionEventMixin_Neo.java | 2 +- .../world/BlockEvent_BreakEventMixin_Neo.java | 36 ++++++++-------- .../level/ServerPlayerMixin_Tracker.java | 41 +++---------------- 12 files changed, 80 insertions(+), 118 deletions(-) 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..6e46f534331 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 @@ -27,7 +27,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.event.Event; -public interface ForgeEventBridge_Forge { +public interface ForgeEventBridge_Forge { /** * Syncs the Sponge event to this Forge event @@ -39,7 +39,7 @@ public interface ForgeEventBridge_Forge { * * @param event The Sponge event */ - void bridge$syncFrom(Event event); + void bridge$syncFrom(E event); /** * Syncs the Forge event to this Sponge event @@ -51,11 +51,11 @@ public interface ForgeEventBridge_Forge { * * @param event The Sponge event */ - void bridge$syncTo(Event event); + void bridge$syncTo(E event); /** * Creates a Sponge event from this Forge event */ - @Nullable Event bridge$createSpongeEvent(); + @Nullable E bridge$createSpongeEvent(); } 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..72179293a4f 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 @@ -113,9 +113,8 @@ public boolean post(final Event event) { @Override public boolean post(final Event event, final IEventBusInvokeDispatcher wrapper) { - if (event instanceof ForgeEventBridge_Forge) { + if (event instanceof ForgeEventBridge_Forge forgeEvent) { // 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); @@ -150,8 +149,10 @@ public boolean post(final org.spongepowered.api.event.Event event) { // Implementation - private boolean postDualBus(final org.spongepowered.api.event.Event spongeEvent, final Collection forgeEvents, - final IEventBusInvokeDispatcher dispatcher) { + @SuppressWarnings({"unchecked", "rawtypes"}) + private boolean postDualBus( + 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(); 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..3a59d34a00d 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,7 +31,6 @@ import net.minecraftforge.event.entity.EntityEvent; import net.minecraftforge.event.entity.EntityTravelToDimensionEvent; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.event.Event; import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.event.entity.ChangeEntityWorldEvent; import org.spongepowered.asm.mixin.Final; @@ -43,30 +42,30 @@ import org.spongepowered.forge.launch.bridge.event.ForgeEventBridge_Forge; @Mixin(value = EntityTravelToDimensionEvent.class, remap = false) -public abstract class EntityTravelToDimensionEventMixin_Forge implements ForgeEventBridge_Forge { +public abstract class EntityTravelToDimensionEventMixin_Forge extends EntityEvent implements ForgeEventBridge_Forge { // @formatter:off @Shadow @Final @Mutable private ResourceKey dimension; // @formatter:on + private EntityTravelToDimensionEventMixin_Forge(Entity entity) { + super(entity); + } + @Override - public void bridge$syncFrom(final Event event) { - if (event instanceof ChangeEntityWorldEvent.Pre) { - ((net.minecraftforge.eventbus.api.Event) (Object) this).setCanceled(((ChangeEntityWorldEvent.Pre) event).isCancelled()); - this.dimension = ((ServerLevel) ((ChangeEntityWorldEvent.Pre) event).destinationWorld()).dimension(); - } + public void bridge$syncFrom(final ChangeEntityWorldEvent.Pre event) { + this.setCanceled(event.isCancelled()); + this.dimension = ((ServerLevel) event.destinationWorld()).dimension(); } @Override - public void bridge$syncTo(final Event event) { - if (event instanceof ChangeEntityWorldEvent.Pre) { - ((ChangeEntityWorldEvent.Pre) event).setCancelled(((net.minecraftforge.eventbus.api.Event) (Object) this).isCanceled()); - } + public void bridge$syncTo(final ChangeEntityWorldEvent.Pre event) { + event.setCancelled(this.isCanceled()); } @Override - public @Nullable Event bridge$createSpongeEvent() { - final Entity entity = ((EntityEvent) (Object) this).getEntity(); + public ChangeEntityWorldEvent.@Nullable Pre bridge$createSpongeEvent() { + final Entity entity = this.getEntity(); final ServerLevel toWorld = SpongeCommon.server().getLevel(this.dimension); return SpongeEventFactory.createChangeEntityWorldEventPre(PhaseTracker.getCauseStackManager().currentCause(), (org.spongepowered.api.entity.Entity) entity, (org.spongepowered.api.world.server.ServerWorld) entity.getCommandSenderWorld(), 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 f7490394fde..2e477e40ac0 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,8 +28,8 @@ import net.minecraft.world.level.Level; import net.minecraftforge.event.entity.player.PlayerEvent; import org.spongepowered.api.entity.Entity; -import org.spongepowered.api.event.Event; import org.spongepowered.api.event.SpongeEventFactory; +import org.spongepowered.api.event.entity.ChangeEntityWorldEvent; import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -39,7 +39,7 @@ import org.spongepowered.forge.launch.bridge.event.ForgeEventBridge_Forge; @Mixin(value = PlayerEvent.PlayerChangedDimensionEvent.class, remap = false) -public final class PlayerEvent_PlayerChangedDimensionEventMixin_Forge implements ForgeEventBridge_Forge { +public final class PlayerEvent_PlayerChangedDimensionEventMixin_Forge implements ForgeEventBridge_Forge{ // @formatter:off @Shadow @Final private ResourceKey fromDim; @@ -47,18 +47,18 @@ public final class PlayerEvent_PlayerChangedDimensionEventMixin_Forge implements // @formatter:on @Override - public void bridge$syncFrom(final Event event) { + public void bridge$syncFrom(final ChangeEntityWorldEvent.Post event) { // nothing to do -- informational only } @Override - public void bridge$syncTo(final Event event) { + public void bridge$syncTo(final ChangeEntityWorldEvent.Post event) { // nothing to do -- informational only } @SuppressWarnings("ConstantConditions") @Override - public Event bridge$createSpongeEvent() { + public ChangeEntityWorldEvent.Post bridge$createSpongeEvent() { final PlayerEvent.PlayerChangedDimensionEvent thisEvent = (PlayerEvent.PlayerChangedDimensionEvent) (Object) this; return SpongeEventFactory.createChangeEntityWorldEventPost( PhaseTracker.getCauseStackManager().currentCause(), diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/world/BlockEventMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/world/BlockEventMixin_Forge.java index 166b9201a39..a833bfbcbb1 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/world/BlockEventMixin_Forge.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/world/BlockEventMixin_Forge.java @@ -28,11 +28,12 @@ import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.block.state.BlockState; import net.minecraftforge.event.level.BlockEvent; +import net.minecraftforge.eventbus.api.Event; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @Mixin(value = BlockEvent.class, remap = false) -public abstract class BlockEventMixin_Forge { +public abstract class BlockEventMixin_Forge extends Event { // @formatter:off @Shadow public abstract LevelAccessor shadow$getLevel(); 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 55845d3e685..6a79af33dae 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,7 +31,6 @@ 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.Event; import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.event.block.ChangeBlockEvent; import org.spongepowered.api.world.server.ServerWorld; @@ -45,37 +44,33 @@ import java.util.Collections; @Mixin(value = BlockEvent.BreakEvent.class, remap = false) -public abstract class BlockEvent_BreakEventMixin_Forge extends BlockEventMixin_Forge implements ForgeEventBridge_Forge { +public abstract class BlockEvent_BreakEventMixin_Forge extends BlockEventMixin_Forge implements ForgeEventBridge_Forge { @Override - public void bridge$syncFrom(final Event event) { - if (event instanceof ChangeBlockEvent.All) { - final ChangeBlockEvent.All changeBlockEventAll = (ChangeBlockEvent.All) event; - final Vector3i pos = VecHelper.toVector3i(this.shadow$getPos()); - if (changeBlockEventAll.isCancelled() || - changeBlockEventAll.transactions() - .stream() - .filter(x -> x.original().position().equals(pos)) - .anyMatch(x -> !x.isValid() || x.operation() != Operations.BREAK.get() || x.custom().isPresent())) { - ((net.minecraftforge.eventbus.api.Event) (Object) this).setCanceled(true); - } + public void bridge$syncFrom(final ChangeBlockEvent.All event) { + final Vector3i pos = VecHelper.toVector3i(this.shadow$getPos()); + if (event.isCancelled() || + event.transactions() + .stream() + .filter(x -> x.original().position().equals(pos)) + .anyMatch(x -> !x.isValid() || x.operation() != Operations.BREAK.get() || x.custom().isPresent())) { + this.setCanceled(true); } } @Override - public void bridge$syncTo(final Event event) { - if (event instanceof ChangeBlockEvent.All && ((net.minecraftforge.eventbus.api.Event) (Object) this).isCanceled()) { + public void bridge$syncTo(final ChangeBlockEvent.All event) { + if (this.isCanceled()) { final Vector3i pos = VecHelper.toVector3i(this.shadow$getPos()); - ((ChangeBlockEvent.All) event).transactions(Operations.BREAK.get()).filter(x -> x.original().position().equals(pos)) + event.transactions(Operations.BREAK.get()).filter(x -> x.original().position().equals(pos)) .forEach(x -> x.setValid(false)); } } @Override - public @Nullable Event bridge$createSpongeEvent() { + public ChangeBlockEvent.@Nullable All bridge$createSpongeEvent() { final LevelAccessor accessor = this.shadow$getLevel(); - if (accessor instanceof ServerWorld) { - final ServerWorld serverWorld = (ServerWorld) accessor; + if (accessor instanceof ServerWorld serverWorld) { final BlockTransaction transaction = new BlockTransaction( SpongeBlockSnapshot.BuilderImpl .pooled() diff --git a/neoforge/src/launch/java/org/spongepowered/neoforge/launch/bridge/event/NeoEventBridge_Neo.java b/neoforge/src/launch/java/org/spongepowered/neoforge/launch/bridge/event/NeoEventBridge_Neo.java index e986d48e072..00e7a93e118 100644 --- a/neoforge/src/launch/java/org/spongepowered/neoforge/launch/bridge/event/NeoEventBridge_Neo.java +++ b/neoforge/src/launch/java/org/spongepowered/neoforge/launch/bridge/event/NeoEventBridge_Neo.java @@ -27,7 +27,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.event.Event; -public interface NeoEventBridge_Neo { +public interface NeoEventBridge_Neo { /** * Syncs the Sponge event to this Forge event @@ -39,7 +39,7 @@ public interface NeoEventBridge_Neo { * * @param event The Sponge event */ - void bridge$syncFrom(Event event); + void bridge$syncFrom(E event); /** * Syncs the Forge event to this Sponge event @@ -51,11 +51,11 @@ public interface NeoEventBridge_Neo { * * @param event The Sponge event */ - void bridge$syncTo(Event event); + void bridge$syncTo(E event); /** * Creates a Sponge event from this Forge event */ - @Nullable Event bridge$createSpongeEvent(); + @Nullable E bridge$createSpongeEvent(); } diff --git a/neoforge/src/launch/java/org/spongepowered/neoforge/launch/event/NeoEventManager.java b/neoforge/src/launch/java/org/spongepowered/neoforge/launch/event/NeoEventManager.java index f93cf818fac..9bc7563bdaf 100644 --- a/neoforge/src/launch/java/org/spongepowered/neoforge/launch/event/NeoEventManager.java +++ b/neoforge/src/launch/java/org/spongepowered/neoforge/launch/event/NeoEventManager.java @@ -102,10 +102,9 @@ public void unregister(final Object object) { @Override public T post(T event) { - if (event instanceof NeoEventBridge_Neo) { + if (event instanceof NeoEventBridge_Neo bridgeEvent) { // intercept! - final NeoEventBridge_Neo forgeEvent = (NeoEventBridge_Neo) event; - final org.spongepowered.api.event.@Nullable Event spongeEvent = forgeEvent.bridge$createSpongeEvent(); + final org.spongepowered.api.event.@Nullable Event spongeEvent = bridgeEvent.bridge$createSpongeEvent(); if (spongeEvent != null) { this.postDualBus(spongeEvent, Collections.singleton(event)); return event; @@ -141,6 +140,7 @@ public boolean post(final org.spongepowered.api.event.Event event) { // Implementation + @SuppressWarnings({"unchecked", "rawtypes"}) private boolean postDualBus(final org.spongepowered.api.event.Event spongeEvent, final Collection forgeEvents) { try (final NoExceptionClosable ignored = this.preparePost(spongeEvent)) { final RegisteredListener.Cache listeners = this.getHandlerCache(spongeEvent); diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/entity/EntityTravelToDimensionEventMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/entity/EntityTravelToDimensionEventMixin_Neo.java index c0d8dd1109e..afa1d6f98ea 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/entity/EntityTravelToDimensionEventMixin_Neo.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/entity/EntityTravelToDimensionEventMixin_Neo.java @@ -32,7 +32,6 @@ import net.neoforged.neoforge.event.entity.EntityEvent; import net.neoforged.neoforge.event.entity.EntityTravelToDimensionEvent; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.event.Event; import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.event.entity.ChangeEntityWorldEvent; import org.spongepowered.asm.mixin.Final; @@ -43,30 +42,30 @@ import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.neoforge.launch.bridge.event.NeoEventBridge_Neo; -@Mixin(value = EntityTravelToDimensionEvent.class, remap = false) -public abstract class EntityTravelToDimensionEventMixin_Neo implements NeoEventBridge_Neo { +@Mixin(value = EntityTravelToDimensionEvent.class) +public abstract class EntityTravelToDimensionEventMixin_Neo implements NeoEventBridge_Neo, ICancellableEvent { // @formatter:off @Shadow @Final @Mutable private ResourceKey dimension; // @formatter:on @Override - public void bridge$syncFrom(final Event event) { + public void bridge$syncFrom(final ChangeEntityWorldEvent.Pre event) { + this.setCanceled(event.isCancelled()); + this.dimension = ((ServerLevel) event.destinationWorld()).dimension(); if (event instanceof ChangeEntityWorldEvent.Pre) { - ((ICancellableEvent) this).setCanceled(((ChangeEntityWorldEvent.Pre) event).isCancelled()); - this.dimension = ((ServerLevel) ((ChangeEntityWorldEvent.Pre) event).destinationWorld()).dimension(); } } @Override - public void bridge$syncTo(final Event event) { + public void bridge$syncTo(final ChangeEntityWorldEvent.Pre event) { if (event instanceof ChangeEntityWorldEvent.Pre) { - ((ChangeEntityWorldEvent.Pre) event).setCancelled(((ICancellableEvent) this).isCanceled()); + event.setCancelled(this.isCanceled()); } } @Override - public @Nullable Event bridge$createSpongeEvent() { + public ChangeEntityWorldEvent.@Nullable Pre bridge$createSpongeEvent() { final Entity entity = ((EntityEvent) (Object) this).getEntity(); final ServerLevel toWorld = SpongeCommon.server().getLevel(this.dimension); return SpongeEventFactory.createChangeEntityWorldEventPre(PhaseTracker.getCauseStackManager().currentCause(), diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/entity/player/PlayerEvent_PlayerChangedDimensionEventMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/entity/player/PlayerEvent_PlayerChangedDimensionEventMixin_Neo.java index 734bec34483..6f77f65b151 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/entity/player/PlayerEvent_PlayerChangedDimensionEventMixin_Neo.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/entity/player/PlayerEvent_PlayerChangedDimensionEventMixin_Neo.java @@ -39,7 +39,7 @@ import org.spongepowered.neoforge.launch.bridge.event.NeoEventBridge_Neo; @Mixin(value = PlayerEvent.PlayerChangedDimensionEvent.class, remap = false) -public final class PlayerEvent_PlayerChangedDimensionEventMixin_Neo implements NeoEventBridge_Neo { +public final class PlayerEvent_PlayerChangedDimensionEventMixin_Neo implements NeoEventBridge_Neo { // @formatter:off @Shadow @Final private ResourceKey fromDim; diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/world/BlockEvent_BreakEventMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/world/BlockEvent_BreakEventMixin_Neo.java index 9b4d92a8da7..fa624f61ee3 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/world/BlockEvent_BreakEventMixin_Neo.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/world/BlockEvent_BreakEventMixin_Neo.java @@ -32,7 +32,6 @@ 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.Event; import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.event.block.ChangeBlockEvent; import org.spongepowered.api.world.server.ServerWorld; @@ -46,37 +45,34 @@ import java.util.Collections; @Mixin(value = BlockEvent.BreakEvent.class, remap = false) -public abstract class BlockEvent_BreakEventMixin_Neo extends BlockEventMixin_Neo implements NeoEventBridge_Neo { +public abstract class BlockEvent_BreakEventMixin_Neo extends BlockEventMixin_Neo implements NeoEventBridge_Neo, ICancellableEvent { @Override - public void bridge$syncFrom(final Event event) { - if (event instanceof ChangeBlockEvent.All) { - final ChangeBlockEvent.All changeBlockEventAll = (ChangeBlockEvent.All) event; - final Vector3i pos = VecHelper.toVector3i(this.shadow$getPos()); - if (changeBlockEventAll.isCancelled() || - changeBlockEventAll.transactions() - .stream() - .filter(x -> x.original().position().equals(pos)) - .anyMatch(x -> !x.isValid() || x.operation() != Operations.BREAK.get() || x.custom().isPresent())) { - ((ICancellableEvent) this).setCanceled(true); - } + public void bridge$syncFrom(final ChangeBlockEvent.All event) { + final Vector3i pos = VecHelper.toVector3i(this.shadow$getPos()); + if (event.isCancelled() || + event.transactions() + .stream() + .filter(x -> x.original().position().equals(pos)) + .anyMatch(x -> !x.isValid() || x.operation() != Operations.BREAK.get() || x.custom().isPresent())) { + this.setCanceled(true); } } @Override - public void bridge$syncTo(final Event event) { - if (event instanceof ChangeBlockEvent.All && ((ICancellableEvent) this).isCanceled()) { + public void bridge$syncTo(final ChangeBlockEvent.All event) { + if (this.isCanceled()) { final Vector3i pos = VecHelper.toVector3i(this.shadow$getPos()); - ((ChangeBlockEvent.All) event).transactions(Operations.BREAK.get()).filter(x -> x.original().position().equals(pos)) - .forEach(x -> x.setValid(false)); + event.transactions(Operations.BREAK.get()) + .filter(x -> x.original().position().equals(pos)) + .forEach(x -> x.setValid(false)); } } @Override - public @Nullable Event bridge$createSpongeEvent() { + public ChangeBlockEvent.@Nullable All bridge$createSpongeEvent() { final LevelAccessor accessor = this.shadow$getLevel(); - if (accessor instanceof ServerWorld) { - final ServerWorld serverWorld = (ServerWorld) accessor; + if (accessor instanceof ServerWorld serverWorld) { final BlockTransaction transaction = new BlockTransaction( SpongeBlockSnapshot.BuilderImpl .pooled() diff --git a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerPlayerMixin_Tracker.java b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerPlayerMixin_Tracker.java index 9ffbd536d43..2df6ee27bdb 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerPlayerMixin_Tracker.java +++ b/src/mixins/java/org/spongepowered/common/mixin/tracker/server/level/ServerPlayerMixin_Tracker.java @@ -24,17 +24,16 @@ */ package org.spongepowered.common.mixin.tracker.server.level; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.util.Mth; import net.minecraft.world.entity.item.ItemEntity; -import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.item.inventory.ItemStackSnapshot; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.common.bridge.world.entity.PlatformEntityBridge; import org.spongepowered.common.bridge.world.level.LevelBridge; import org.spongepowered.common.event.SpongeCommonEventFactory; @@ -55,7 +54,7 @@ public abstract class ServerPlayerMixin_Tracker extends PlayerMixin_Tracker { * @reason We inject a construct event for the item drop and conveniently * can redirect the super call. */ - @Redirect( + @WrapOperation( method = "drop(Lnet/minecraft/world/item/ItemStack;ZZ)Lnet/minecraft/world/entity/item/ItemEntity;", at = @At( value = "INVOKE", @@ -64,7 +63,8 @@ public abstract class ServerPlayerMixin_Tracker extends PlayerMixin_Tracker { ) @Nullable private ItemEntity tracker$throwItemDrop( - final Player thisPlayer, final ItemStack droppedItem, final boolean dropAround, final boolean traceItem + final ServerPlayer instance, final ItemStack droppedItem, final boolean dropAround, + final boolean traceItem, final Operation wrapped ) { if (droppedItem.isEmpty()) { return null; @@ -93,36 +93,7 @@ public abstract class ServerPlayerMixin_Tracker extends PlayerMixin_Tracker { if (item == null || item.isEmpty()) { return null; } - - // Here is where we would potentially perform item pre-merging (merge the item stacks with previously captured item stacks - // and only if those stacks can be stacked (count increased). Otherwise, we'll just continue to throw the entity item. - // For now, due to refactoring a majority of all of this code, pre-merging is disabled entirely. - - final ItemEntity itemEntity = new ItemEntity(this.shadow$level(), posX1, posY1, posZ1, item); - itemEntity.setPickUpDelay(40); - - if (traceItem) { - itemEntity.setThrower((ServerPlayer) (Object) this); - } - - if (dropAround) { - final float f = this.random.nextFloat() * 0.5F; - final float f1 = this.random.nextFloat() * ((float) Math.PI * 2F); - itemEntity.setDeltaMovement(-Mth.sin(f1) * f, 0.2F, Mth.cos(f1) * f); - } else { - final float f8 = Mth.sin(this.shadow$getXRot() * ((float) Math.PI / 180F)); - final float f2 = Mth.cos(this.shadow$getXRot() * ((float) Math.PI / 180F)); - final float f3 = Mth.sin(this.shadow$getYRot() * ((float) Math.PI / 180F)); - final float f4 = Mth.cos(this.shadow$getYRot() * ((float) Math.PI / 180F)); - final float f5 = this.random.nextFloat() * ((float) Math.PI * 2F); - final float f6 = 0.02F * this.random.nextFloat(); - itemEntity.setDeltaMovement( - (double) (-f3 * f2 * 0.3F) + Math.cos(f5) * (double) f6, - (-f8 * 0.3F + 0.1F + (this.random.nextFloat() - this.random.nextFloat()) * 0.1F), - (double) (f4 * f2 * 0.3F) + Math.sin(f5) * (double) f6 - ); - } - return itemEntity; + return wrapped.call(instance, item, dropAround, traceItem); } } } From d0193461c391010f0bcfe48c3d99baa719c8106d Mon Sep 17 00:00:00 2001 From: Gabriel Harris-Rouquette Date: Sat, 18 Jan 2025 22:10:13 -0800 Subject: [PATCH 3/5] feat: remove PlayerMixin.drop overwrite Using an event bridge, we can better manage when we throw the events on Sponge as a bridge. This has been verified through internal testing when using both a Sponge plugin and a mod event listener. --- .../entity/ItemTossEventMixin_Forge.java | 85 ++++++++++++++++++ .../resources/mixins.spongeforge.core.json | 1 + .../entity/item/ItemTossEventMixin_Neo.java | 86 +++++++++++++++++++ .../resources/mixins.spongeneo.core.json | 1 + .../core/world/entity/player/PlayerMixin.java | 14 --- 5 files changed, 173 insertions(+), 14 deletions(-) create mode 100644 forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/ItemTossEventMixin_Forge.java create mode 100644 neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/entity/item/ItemTossEventMixin_Neo.java diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/ItemTossEventMixin_Forge.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/ItemTossEventMixin_Forge.java new file mode 100644 index 00000000000..105a97f7b67 --- /dev/null +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/minecraftforge/event/entity/ItemTossEventMixin_Forge.java @@ -0,0 +1,85 @@ +/* + * 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; + +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraftforge.event.entity.item.ItemEvent; +import net.minecraftforge.event.entity.item.ItemTossEvent; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.event.EventContextKeys; +import org.spongepowered.api.event.SpongeEventFactory; +import org.spongepowered.api.event.cause.entity.SpawnTypes; +import org.spongepowered.api.event.item.inventory.DropItemEvent; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.forge.launch.bridge.event.ForgeEventBridge_Forge; + +import java.util.ArrayList; + +@Mixin(ItemTossEvent.class) +public abstract class ItemTossEventMixin_Forge extends ItemEvent implements ForgeEventBridge_Forge { + + //@formatter:off + @Shadow private @Final Player player; + //@formatter:on + + private ItemTossEventMixin_Forge(ItemEntity itemEntity) { + super(itemEntity); + } + + @Override + public void bridge$syncFrom(DropItemEvent.Dispense event) { + event.entities().stream() + .filter(e -> e instanceof ItemEntity) + .map(e -> (ItemEntity) e) + .findFirst() + .ifPresent(e -> this.getEntity().setItem(e.getItem())); + this.setCanceled(event.isCancelled()); + } + + @Override + public void bridge$syncTo(DropItemEvent.Dispense event) { + event.setCancelled(this.isCanceled()); + } + + @Override + public DropItemEvent.@Nullable Dispense bridge$createSpongeEvent() { + if (this.player.level().isClientSide()) { + return null; + } + try (final var frame = PhaseTracker.getInstance().pushCauseFrame()) { + frame.addContext(EventContextKeys.SPAWN_TYPE, SpawnTypes.DROPPED_ITEM); + final var cause = frame.currentCause(); + final var toSpawn = (Entity) this.getEntity(); + final var list = new ArrayList(); + list.add(toSpawn); + return SpongeEventFactory.createDropItemEventDispense(cause, list); + } + } +} diff --git a/forge/src/mixins/resources/mixins.spongeforge.core.json b/forge/src/mixins/resources/mixins.spongeforge.core.json index ae82ddbcc44..45d4c3888b0 100644 --- a/forge/src/mixins/resources/mixins.spongeforge.core.json +++ b/forge/src/mixins/resources/mixins.spongeforge.core.json @@ -11,6 +11,7 @@ "commands.CommandsMixin_Forge", "minecraftforge.MinecraftForgeMixin_Forge", "minecraftforge.event.entity.EntityTravelToDimensionEventMixin_Forge", + "minecraftforge.event.entity.ItemTossEventMixin_Forge", "minecraftforge.event.entity.player.PlayerEvent_PlayerChangedDimensionEventMixin_Forge", "minecraftforge.event.world.BlockEvent_BreakEventMixin_Forge", "minecraftforge.event.world.BlockEventMixin_Forge", diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/entity/item/ItemTossEventMixin_Neo.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/entity/item/ItemTossEventMixin_Neo.java new file mode 100644 index 00000000000..9d3da18aab0 --- /dev/null +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/neoforge/event/entity/item/ItemTossEventMixin_Neo.java @@ -0,0 +1,86 @@ +/* + * 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.neoforge.mixin.core.neoforge.event.entity.item; + +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.entity.player.Player; +import net.neoforged.bus.api.ICancellableEvent; +import net.neoforged.neoforge.event.entity.item.ItemEvent; +import net.neoforged.neoforge.event.entity.item.ItemTossEvent; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.event.EventContextKeys; +import org.spongepowered.api.event.SpongeEventFactory; +import org.spongepowered.api.event.cause.entity.SpawnTypes; +import org.spongepowered.api.event.item.inventory.DropItemEvent; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.common.event.tracking.PhaseTracker; +import org.spongepowered.neoforge.launch.bridge.event.NeoEventBridge_Neo; + +import java.util.ArrayList; + +@Mixin(ItemTossEvent.class) +public abstract class ItemTossEventMixin_Neo extends ItemEvent implements NeoEventBridge_Neo, ICancellableEvent { + + //@formatter:off + @Shadow private @Final Player player; + //@formatter:on + + private ItemTossEventMixin_Neo(ItemEntity itemEntity) { + super(itemEntity); + } + + @Override + public void bridge$syncFrom(DropItemEvent.Dispense event) { + event.entities().stream() + .filter(e -> e instanceof ItemEntity) + .map(e -> (ItemEntity) e) + .findFirst() + .ifPresent(e -> this.getEntity().setItem(e.getItem())); + this.setCanceled(event.isCancelled()); + } + + @Override + public void bridge$syncTo(DropItemEvent.Dispense event) { + event.setCancelled(this.isCanceled()); + } + + @Override + public DropItemEvent.@Nullable Dispense bridge$createSpongeEvent() { + if (this.player.level().isClientSide()) { + return null; + } + try (final var frame = PhaseTracker.getInstance().pushCauseFrame()) { + frame.addContext(EventContextKeys.SPAWN_TYPE, SpawnTypes.DROPPED_ITEM); + final var cause = frame.currentCause(); + final var toSpawn = (Entity) this.getEntity(); + final var list = new ArrayList(); + list.add(toSpawn); + return SpongeEventFactory.createDropItemEventDispense(cause, list); + } + } +} diff --git a/neoforge/src/mixins/resources/mixins.spongeneo.core.json b/neoforge/src/mixins/resources/mixins.spongeneo.core.json index 0bef1de107f..533d7b2abd2 100644 --- a/neoforge/src/mixins/resources/mixins.spongeneo.core.json +++ b/neoforge/src/mixins/resources/mixins.spongeneo.core.json @@ -8,6 +8,7 @@ "api.event.block.ChangeBlockEvent_AllMixin_Neo", "api.event.entity.ChangeEntityWorldEvent_PreMixin_Neo", "api.event.entity.ChangeEventWorldEvent_PostMixin_Neo", + "neoforge.event.entity.item.ItemTossEventMixin_Neo", "commands.CommandsMixin_Neo", "neoforge.NeoForgeMixin_Neo", "neoforge.event.entity.EntityTravelToDimensionEventMixin_Neo", diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin.java index ef859f8e032..b8908c61226 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin.java @@ -61,7 +61,6 @@ import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; -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; @@ -293,19 +292,6 @@ public abstract class PlayerMixin extends LivingEntityMixin implements PlayerBri return true; } - /** - * @author gabizou - June 13th, 2016 - * @author zidane - November 21st, 2020 - * @reason Reverts the method to flow through our systems, Forge patches - * this to throw an ItemTossEvent, but we'll be throwing it regardless in - * SpongeForge's handling. - */ - @Overwrite - @Nullable - public ItemEntity drop(final ItemStack itemStackIn, final boolean traceItem) { - return this.shadow$drop(itemStackIn, false, traceItem); - } - @Inject(method = "getFireImmuneTicks", at = @At(value = "HEAD"), cancellable = true) private void impl$useCustomFireImmuneTicks(final CallbackInfoReturnable ci) { if (this.impl$hasCustomFireImmuneTicks) { From 7d679e3d5fb8f756cc73f84e67c0e766162e4ea8 Mon Sep 17 00:00:00 2001 From: Gabriel Harris-Rouquette Date: Sat, 18 Jan 2025 22:18:10 -0800 Subject: [PATCH 4/5] fix: populate damage event modifiers on server only Enchantments can only reference a ServerLevel, so we keep the injection points in Player, but will override their injection points on ServerPlayer. This will still enable the event being thrown on the client, but only add the DamageModifiers on the server (where they belong). --- .../level/ServerPlayerMixin_Attack_impl.java | 78 +++ .../entity/LivingEntityMixin_Attack_Impl.java | 14 +- .../player/PlayerMixin_Attack_Impl.java | 140 ++-- src/mixins/resources/mixins.sponge.core.json | 617 +++++++++--------- 4 files changed, 450 insertions(+), 399 deletions(-) create mode 100644 src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerMixin_Attack_impl.java diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerMixin_Attack_impl.java b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerMixin_Attack_impl.java new file mode 100644 index 00000000000..4b2d0d15cee --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerPlayerMixin_Attack_impl.java @@ -0,0 +1,78 @@ +/* + * 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.common.mixin.core.server.level; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.common.mixin.core.world.entity.player.PlayerMixin_Attack_Impl; +import org.spongepowered.common.util.DamageEventUtil; + +import java.util.ArrayList; + +@Mixin(value = ServerPlayer.class, priority = 900) +public abstract class ServerPlayerMixin_Attack_impl extends PlayerMixin_Attack_Impl { + + @Override + protected void attackImpl$enchanttDamageFunc(Entity $$0, CallbackInfo ci) { + final var weapon = this.attackImpl$attack.weapon(); + // this.getEnchantedDamage(targetEntity, damage, damageSource) - damage; + final var functions = DamageEventUtil.createAttackEnchantmentFunction(weapon, this.attackImpl$attack.target(), this.attackImpl$attack.dmgSource()); + final var separateFunc = DamageEventUtil.provideSeparateEnchantmentFromBaseDamageFunction(this.attackImpl$attack.baseDamage(), weapon); + // enchantmentDamage *= attackStrength; + final var strengthScaleFunc = DamageEventUtil.provideCooldownEnchantmentStrengthFunction(weapon, this.attackImpl$attack.strengthScale()); + + this.attackImpl$attack.functions().addAll(functions); + this.attackImpl$attack.functions().add(separateFunc); + this.attackImpl$attack.functions().add(strengthScaleFunc); + } + + @Override + protected double attackImpl$beforeSweepHurt(Player instance, Entity sweepTarget) { + final var distanceToSqr = instance.distanceToSqr(sweepTarget); + if (!(distanceToSqr < 9.0)) { + return distanceToSqr; // Too far - no event + } + + final var mainAttack = this.attackImpl$attack; + final var mainAttackDamage = this.attackImpl$finalDamageAmounts.getOrDefault("minecraft:attack_damage", 0.0).floatValue(); + + var sweepAttack = new DamageEventUtil.Attack<>(mainAttack.sourceEntity(), sweepTarget, mainAttack.weapon(), mainAttack.dmgSource(), mainAttack.strengthScale(), 1, new ArrayList<>()); + // float sweepBaseDamage = 1.0F + (float)this.getAttributeValue(Attributes.SWEEPING_DAMAGE_RATIO) * attackDamage; + sweepAttack.functions().add(DamageEventUtil.provideSweepingDamageRatioFunction(mainAttack.weapon(), mainAttack.sourceEntity(), mainAttackDamage)); + // float sweepFullDamage = this.getEnchantedDamage(sweepTarget, sweepBaseDamage, $$3) * strengthScale; + sweepAttack.functions().addAll(DamageEventUtil.createAttackEnchantmentFunction(mainAttack.weapon(), sweepTarget, mainAttack.dmgSource())); + sweepAttack.functions().add(DamageEventUtil.provideCooldownEnchantmentStrengthFunction(mainAttack.weapon(), mainAttack.strengthScale())); + + this.attackImpl$attackEvent = DamageEventUtil.callPlayerAttackEntityEvent(sweepAttack, 1.0F); + if (attackImpl$attackEvent.isCancelled()) { + return Double.MAX_VALUE; + } + + return distanceToSqr; + } +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Attack_Impl.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Attack_Impl.java index 945960567fd..ffb70cdc94d 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Attack_Impl.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Attack_Impl.java @@ -243,7 +243,7 @@ public abstract class LivingEntityMixin_Attack_Impl extends EntityMixin implemen } @Inject(method = "actuallyHurt", at = @At(value = "INVOKE",target = "Lnet/minecraft/world/entity/LivingEntity;getDamageAfterArmorAbsorb(Lnet/minecraft/world/damagesource/DamageSource;F)F")) - public void attackImpl$startActuallyHurt(DamageSource damageSource, float originalDamage, CallbackInfo ci) { + protected void attackImpl$startActuallyHurt(DamageSource damageSource, float originalDamage, CallbackInfo ci) { // TODO check for direct call? this.attackImpl$actuallyHurt = new DamageEventUtil.ActuallyHurt((LivingEntity) (Object) this, new ArrayList<>(), damageSource, originalDamage); } @@ -254,7 +254,7 @@ public abstract class LivingEntityMixin_Attack_Impl extends EntityMixin implemen */ @Redirect(method = "getDamageAfterArmorAbsorb", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;hurtArmor(Lnet/minecraft/world/damagesource/DamageSource;F)V")) - public void attackImpl$onDamageAfterArmorAbsorb(final LivingEntity instance, final DamageSource $$0, final float $$1) { + protected void attackImpl$onDamageAfterArmorAbsorb(final LivingEntity instance, final DamageSource $$0, final float $$1) { if (this.attackImpl$actuallyHurt != null) { // prevents this.hurtArmor($$0, $$1); // $$1 = CombatRules.getDamageAfterAbsorb(this, $$1, $$0, (float)this.getArmorValue(), (float)this.getAttributeValue(Attributes.ARMOR_TOUGHNESS)); @@ -268,7 +268,7 @@ public abstract class LivingEntityMixin_Attack_Impl extends EntityMixin implemen */ @Inject(method = "getDamageAfterMagicAbsorb", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;getEffect(Lnet/minecraft/core/Holder;)Lnet/minecraft/world/effect/MobEffectInstance;")) - public void attackImpl$onDamageAfterMagicAbsorb(final DamageSource $$0, final float $$1, final CallbackInfoReturnable cir) { + protected void attackImpl$onDamageAfterMagicAbsorb(final DamageSource $$0, final float $$1, final CallbackInfoReturnable cir) { if (this.attackImpl$actuallyHurt != null) { var func = DamageEventUtil.createResistanceModifier(this.attackImpl$actuallyHurt.entity()); this.attackImpl$actuallyHurt.functions().add(func); @@ -281,7 +281,7 @@ public abstract class LivingEntityMixin_Attack_Impl extends EntityMixin implemen */ @Redirect(method = "getDamageAfterMagicAbsorb", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/damagesource/CombatRules;getDamageAfterMagicAbsorb(FF)F")) - public float attackImpl$onDetDamageProtection(final float damage, final float protection) { + protected float attackImpl$onDetDamageProtection(final float damage, final float protection) { if (this.attackImpl$actuallyHurt != null) { var func = DamageEventUtil.createEnchantmentModifiers(this.attackImpl$actuallyHurt.entity(), protection); this.attackImpl$actuallyHurt.functions().add(func); @@ -295,7 +295,7 @@ public abstract class LivingEntityMixin_Attack_Impl extends EntityMixin implemen * Then calls the DamageEntityEvent */ @Inject(method = "setAbsorptionAmount", cancellable = true, at = @At("HEAD")) - public void attackImpl$onSetAbsorptionAmount(final float newAmount, final CallbackInfo ci) { + protected void attackImpl$onSetAbsorptionAmount(final float newAmount, final CallbackInfo ci) { if (this.attackImpl$actuallyHurt != null) { ci.cancel(); // Always cancel this var oldAmount = this.shadow$getAbsorptionAmount(); @@ -331,7 +331,7 @@ public abstract class LivingEntityMixin_Attack_Impl extends EntityMixin implemen @ModifyVariable(method = "actuallyHurt", ordinal = 0, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;setAbsorptionAmount(F)V", ordinal = 0, shift = At.Shift.AFTER), argsOnly = true) - public float attackImpl$setFinalDamage(final float value) { + protected float attackImpl$setFinalDamage(final float value) { if (this.attackImpl$actuallyHurtResult.event().isCancelled()) { return 0; } @@ -388,7 +388,7 @@ public abstract class LivingEntityMixin_Attack_Impl extends EntityMixin implemen * also reverts {@link #attackImpl$beforeActuallyHurt} */ @Inject(method = "actuallyHurt", at = @At("RETURN")) - public void attackImpl$cleanupActuallyHurt(final DamageSource $$0, final float $$1, final CallbackInfo ci) { + protected void attackImpl$cleanupActuallyHurt(final DamageSource $$0, final float $$1, final CallbackInfo ci) { this.attackImpl$handlePostDamage(); this.attackImpl$actuallyHurt = null; this.attackImpl$actuallyHurtResult = null; diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin_Attack_Impl.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin_Attack_Impl.java index daa23d981be..c2745c3a433 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin_Attack_Impl.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin_Attack_Impl.java @@ -77,18 +77,18 @@ public abstract class PlayerMixin_Attack_Impl extends LivingEntityMixin_Attack_I } } - private DamageEventUtil.Attack attackImpl$attack; - private AttackEntityEvent attackImpl$attackEvent; - private Map attackImpl$finalDamageAmounts; + protected DamageEventUtil.Attack attackImpl$attack; + protected AttackEntityEvent attackImpl$attackEvent; + protected Map attackImpl$finalDamageAmounts; - private int attackImpl$attackStrengthTicker; - private boolean attackImpl$isStrongSprintAttack; + protected int attackImpl$attackStrengthTicker; + protected boolean attackImpl$isStrongSprintAttack; /** * Cleanup */ @Inject(method = "attack", at = @At("RETURN")) - public void attackImpl$onReturnCleanup(final Entity $$0, final CallbackInfo ci) { + protected void attackImpl$onReturnCleanup(final Entity $$0, final CallbackInfo ci) { this.attackImpl$attack = null; this.attackImpl$attackEvent = null; this.attackImpl$finalDamageAmounts = null; @@ -100,8 +100,8 @@ public abstract class PlayerMixin_Attack_Impl extends LivingEntityMixin_Attack_I * Reset {@link #attackImpl$isStrongSprintAttack} */ @Inject(method = "attack", locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/entity/player/Player;getEnchantedDamage(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F", shift = At.Shift.BEFORE)) - public void attackImpl$captureAttackStart(final Entity target, final CallbackInfo ci, final float baseDamage, final ItemStack weapon, final DamageSource source) { + target = "Lnet/minecraft/world/entity/player/Player;getEnchantedDamage(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F", shift = At.Shift.BEFORE)) + protected void attackImpl$captureAttackStart(final Entity target, final CallbackInfo ci, final float baseDamage, final ItemStack weapon, final DamageSource source) { final var strengthScale = this.shadow$getAttackStrengthScale(0.5F); this.attackImpl$attack = new DamageEventUtil.Attack<>((Player) (Object) this, target, weapon, source, strengthScale, baseDamage, new ArrayList<>()); this.attackImpl$attackStrengthTicker = this.attackStrengthTicker; @@ -112,18 +112,10 @@ public abstract class PlayerMixin_Attack_Impl extends LivingEntityMixin_Attack_I * Captures the enchantment damage calculations as functions */ @Inject(method = "attack", at = @At(value = "INVOKE", ordinal = 0, - target = "Lnet/minecraft/world/entity/player/Player;getEnchantedDamage(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F")) - public void attackImpl$enchanttDamageFunc(final Entity $$0, final CallbackInfo ci) { - final var weapon = this.attackImpl$attack.weapon(); - // this.getEnchantedDamage(targetEntity, damage, damageSource) - damage; - final var functions = DamageEventUtil.createAttackEnchantmentFunction(weapon, this.attackImpl$attack.target(), this.attackImpl$attack.dmgSource()); - final var separateFunc = DamageEventUtil.provideSeparateEnchantmentFromBaseDamageFunction(this.attackImpl$attack.baseDamage(), weapon); - // enchantmentDamage *= attackStrength; - final var strengthScaleFunc = DamageEventUtil.provideCooldownEnchantmentStrengthFunction(weapon, this.attackImpl$attack.strengthScale()); - - this.attackImpl$attack.functions().addAll(functions); - this.attackImpl$attack.functions().add(separateFunc); - this.attackImpl$attack.functions().add(strengthScaleFunc); + target = "Lnet/minecraft/world/entity/player/Player;getEnchantedDamage(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F")) + protected void attackImpl$enchanttDamageFunc(final Entity $$0, final CallbackInfo ci) { + // This is overridden in ServerPlayerMixin_Attack_Impl since enchantments can only be calculated on the + // server worlds. } @@ -131,23 +123,23 @@ public abstract class PlayerMixin_Attack_Impl extends LivingEntityMixin_Attack_I * Captures the attack-strength damage scaling as a function */ @Inject(method = "attack", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/entity/player/Player;resetAttackStrengthTicker()V")) - public void attackImpl$attackStrengthScalingDamageFunc(final Entity $$0, final CallbackInfo ci) { + target = "Lnet/minecraft/world/entity/player/Player;resetAttackStrengthTicker()V")) + protected void attackImpl$attackStrengthScalingDamageFunc(final Entity $$0, final CallbackInfo ci) { // damage *= 0.2F + attackStrength * attackStrength * 0.8F; - final var strengthScaleFunc = DamageEventUtil.provideCooldownAttackStrengthFunction((Player) (Object) this, this.attackImpl$attack.strengthScale()); + final var strengthScaleFunc = DamageEventUtil.provideCooldownAttackStrengthFunction((Player) (Object) this, this.attackImpl$attack.strengthScale()); this.attackImpl$attack.functions().add(strengthScaleFunc); } /** - * Prevents the {@link SoundEvents#PLAYER_ATTACK_KNOCKBACK} from playing before the event. - * Captures if {@link #attackImpl$isStrongSprintAttack} for later + * Prevents the {@link SoundEvents#PLAYER_ATTACK_KNOCKBACK} from playing before the event. + * Captures if {@link #attackImpl$isStrongSprintAttack} for later */ @Redirect(method = "attack", - slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;isSprinting()Z", ordinal = 0), - to = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/Item;getAttackDamageBonus(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F")), - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;playSound(Lnet/minecraft/world/entity/player/Player;DDDLnet/minecraft/sounds/SoundEvent;Lnet/minecraft/sounds/SoundSource;FF)V")) - public void attackImpl$preventSprintingAttackSound(final Level instance, final Player $$0, final double $$1, final double $$2, final double $$3, final SoundEvent $$4, - final SoundSource $$5, final float $$6, final float $$7) { + slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;isSprinting()Z", ordinal = 0), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/Item;getAttackDamageBonus(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F")), + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;playSound(Lnet/minecraft/world/entity/player/Player;DDDLnet/minecraft/sounds/SoundEvent;Lnet/minecraft/sounds/SoundSource;FF)V")) + protected void attackImpl$preventSprintingAttackSound(final Level instance, final Player $$0, final double $$1, final double $$2, final double $$3, final SoundEvent $$4, + final SoundSource $$5, final float $$6, final float $$7) { // prevent sound this.attackImpl$isStrongSprintAttack = true; } @@ -156,10 +148,10 @@ public abstract class PlayerMixin_Attack_Impl extends LivingEntityMixin_Attack_I * Captures the weapon bonus damage as a function */ @Inject(method = "attack", at = @At(value = "INVOKE", - target = "Lnet/minecraft/world/item/Item;getAttackDamageBonus(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F")) - public void attackImpl$attackDamageFunc(final Entity $$0, final CallbackInfo ci) { + target = "Lnet/minecraft/world/item/Item;getAttackDamageBonus(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F")) + protected void attackImpl$attackDamageFunc(final Entity $$0, final CallbackInfo ci) { // damage += weaponItem.getItem().getAttackDamageBonus(targetEntity, damage, damageSource); - final var bonusDamageFunc = DamageEventUtil.provideWeaponAttackDamageBonusFunction( this.attackImpl$attack.target(), this.attackImpl$attack.weapon(), this.attackImpl$attack.dmgSource()); + final var bonusDamageFunc = DamageEventUtil.provideWeaponAttackDamageBonusFunction(this.attackImpl$attack.target(), this.attackImpl$attack.weapon(), this.attackImpl$attack.dmgSource()); this.attackImpl$attack.functions().add(bonusDamageFunc); } @@ -171,10 +163,10 @@ public abstract class PlayerMixin_Attack_Impl extends LivingEntityMixin_Attack_I * returns false if canceled, appearing for vanilla as an invulnerable target. {@link #attackImpl$onNoDamageSound} */ @Redirect(method = "attack", - slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;getDeltaMovement()Lnet/minecraft/world/phys/Vec3;"), - to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getKnockback(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;)F")), - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;hurt(Lnet/minecraft/world/damagesource/DamageSource;F)Z")) - public boolean attackImpl$onHurt(final Entity targetEntity, final DamageSource damageSource, final float mcDamage) { + slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;getDeltaMovement()Lnet/minecraft/world/phys/Vec3;"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getKnockback(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;)F")), + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;hurt(Lnet/minecraft/world/damagesource/DamageSource;F)Z")) + protected boolean attackImpl$onHurt(final Entity targetEntity, final DamageSource damageSource, final float mcDamage) { float knockbackModifier = this.shadow$getKnockback(targetEntity, damageSource) + (this.attackImpl$isStrongSprintAttack ? 1.0F : 0.0F); this.attackImpl$attackEvent = DamageEventUtil.callPlayerAttackEntityEvent(this.attackImpl$attack, knockbackModifier); @@ -199,19 +191,19 @@ public abstract class PlayerMixin_Attack_Impl extends LivingEntityMixin_Attack_I * Set enchantment damage with value from event */ @ModifyVariable(method = "attack", ordinal = 1, - slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;hurt(Lnet/minecraft/world/damagesource/DamageSource;F)Z"), - to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;knockback(DDD)V", ordinal = 0)), - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getKnockback(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;)F")) - public float attackImpl$enchentmentDamageFromEvent(final float enchDmg) { + slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;hurt(Lnet/minecraft/world/damagesource/DamageSource;F)Z"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;knockback(DDD)V", ordinal = 0)), + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getKnockback(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;)F")) + protected float attackImpl$enchentmentDamageFromEvent(final float enchDmg) { return this.attackImpl$finalDamageAmounts.getOrDefault(ResourceKey.minecraft("attack_enchantment"), 0.0).floatValue(); } /** - * Redirects Player#getKnockback to the attack event value + * Redirects Player#getKnockback to the attack event value */ @Redirect(method = "attack", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getKnockback(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;)F")) - public float attackImpl$sweepHook(final Player instance, final Entity entity, final DamageSource damageSource) { + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getKnockback(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/damagesource/DamageSource;)F")) + protected float attackImpl$sweepHook(final Player instance, final Entity entity, final DamageSource damageSource) { return this.attackImpl$attackEvent.knockbackModifier(); } @@ -227,10 +219,10 @@ public abstract class PlayerMixin_Attack_Impl extends LivingEntityMixin_Attack_I * Prevents the {@link SoundEvents#PLAYER_ATTACK_NODAMAGE} when event was canceled */ @Redirect(method = "attack", - slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;causeFoodExhaustion(F)V")), - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;playSound(Lnet/minecraft/world/entity/player/Player;DDDLnet/minecraft/sounds/SoundEvent;Lnet/minecraft/sounds/SoundSource;FF)V")) - public void attackImpl$onNoDamageSound(final Level instance, final Player $$0, final double $$1, final double $$2, final double $$3, - final SoundEvent $$4, final SoundSource $$5, final float $$6, final float $$7) { + slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;causeFoodExhaustion(F)V")), + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;playSound(Lnet/minecraft/world/entity/player/Player;DDDLnet/minecraft/sounds/SoundEvent;Lnet/minecraft/sounds/SoundSource;FF)V")) + protected void attackImpl$onNoDamageSound(final Level instance, final Player $$0, final double $$1, final double $$2, final double $$3, + final SoundEvent $$4, final SoundSource $$5, final float $$6, final float $$7) { if (!this.attackImpl$attackEvent.isCancelled()) { this.impl$playAttackSound((Player) (Object) this, SoundEvents.PLAYER_ATTACK_NODAMAGE); } @@ -240,39 +232,19 @@ public abstract class PlayerMixin_Attack_Impl extends LivingEntityMixin_Attack_I * Call Sweep Attack Events */ @Redirect(method = "attack", - slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;getEntitiesOfClass(Ljava/lang/Class;Lnet/minecraft/world/phys/AABB;)Ljava/util/List;")), - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;distanceToSqr(Lnet/minecraft/world/entity/Entity;)D")) - public double attackImpl$beforeSweepHurt(final Player instance, final Entity sweepTarget) { - final var distanceToSqr = instance.distanceToSqr(sweepTarget); - if (!(distanceToSqr < 9.0)) { - return distanceToSqr; // Too far - no event - } - - final var mainAttack = this.attackImpl$attack; - final var mainAttackDamage = this.attackImpl$finalDamageAmounts.getOrDefault("minecraft:attack_damage", 0.0).floatValue(); - - var sweepAttack = new DamageEventUtil.Attack<>(mainAttack.sourceEntity(), sweepTarget, mainAttack.weapon(), mainAttack.dmgSource(), mainAttack.strengthScale(), 1, new ArrayList<>()); - // float sweepBaseDamage = 1.0F + (float)this.getAttributeValue(Attributes.SWEEPING_DAMAGE_RATIO) * attackDamage; - sweepAttack.functions().add(DamageEventUtil.provideSweepingDamageRatioFunction(mainAttack.weapon(), mainAttack.sourceEntity(), mainAttackDamage)); - // float sweepFullDamage = this.getEnchantedDamage(sweepTarget, sweepBaseDamage, $$3) * strengthScale; - sweepAttack.functions().addAll(DamageEventUtil.createAttackEnchantmentFunction(mainAttack.weapon(), sweepTarget, mainAttack.dmgSource())); - sweepAttack.functions().add(DamageEventUtil.provideCooldownEnchantmentStrengthFunction(mainAttack.weapon(), mainAttack.strengthScale())); - - this.attackImpl$attackEvent = DamageEventUtil.callPlayerAttackEntityEvent(sweepAttack, 1.0F); - if (attackImpl$attackEvent.isCancelled()) { - return Double.MAX_VALUE; - } - - return distanceToSqr; + slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;getEntitiesOfClass(Ljava/lang/Class;Lnet/minecraft/world/phys/AABB;)Ljava/util/List;")), + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;distanceToSqr(Lnet/minecraft/world/entity/Entity;)D")) + protected double attackImpl$beforeSweepHurt(final Player instance, final Entity sweepTarget) { + return instance.distanceToSqr(sweepTarget); } /** * Redirect Player#getEnchantedDamage to sweep event value */ @Redirect(method = "attack", - slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;getEntitiesOfClass(Ljava/lang/Class;Lnet/minecraft/world/phys/AABB;)Ljava/util/List;")), - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getEnchantedDamage(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F")) - public float attackImpl$beforeSweepHurt(final Player instance, final Entity $$0, final float $$1, final DamageSource $$2) { + slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;getEntitiesOfClass(Ljava/lang/Class;Lnet/minecraft/world/phys/AABB;)Ljava/util/List;")), + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getEnchantedDamage(Lnet/minecraft/world/entity/Entity;FLnet/minecraft/world/damagesource/DamageSource;)F")) + protected float attackImpl$beforeSweepHurt(final Player instance, final Entity $$0, final float $$1, final DamageSource $$2) { return (float) this.attackImpl$attackEvent.finalOutputDamage(); } @@ -280,10 +252,10 @@ public abstract class PlayerMixin_Attack_Impl extends LivingEntityMixin_Attack_I * Redirect {@link LivingEntity#knockback} to use modified event knockback */ @Redirect(method = "attack", - slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;getEntitiesOfClass(Ljava/lang/Class;Lnet/minecraft/world/phys/AABB;)Ljava/util/List;"), - to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;hurt(Lnet/minecraft/world/damagesource/DamageSource;F)Z")), - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;knockback(DDD)V")) - public void attackImpl$modifyKnockback(final LivingEntity instance, final double $$0, final double $$1, final double $$2) { + slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;getEntitiesOfClass(Ljava/lang/Class;Lnet/minecraft/world/phys/AABB;)Ljava/util/List;"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;hurt(Lnet/minecraft/world/damagesource/DamageSource;F)Z")), + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;knockback(DDD)V")) + protected void attackImpl$modifyKnockback(final LivingEntity instance, final double $$0, final double $$1, final double $$2) { instance.knockback($$0 * this.attackImpl$attackEvent.knockbackModifier(), $$1, $$2); } @@ -291,8 +263,8 @@ public abstract class PlayerMixin_Attack_Impl extends LivingEntityMixin_Attack_I * Captures inventory changes */ @Redirect(method = "attack", - at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;setItemInHand(Lnet/minecraft/world/InteractionHand;Lnet/minecraft/world/item/ItemStack;)V")) - public void attackImpl$causeInventoryCapture(final Player instance, final InteractionHand interactionHand, final ItemStack stack) { + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;setItemInHand(Lnet/minecraft/world/InteractionHand;Lnet/minecraft/world/item/ItemStack;)V")) + protected void attackImpl$causeInventoryCapture(final Player instance, final InteractionHand interactionHand, final ItemStack stack) { instance.setItemInHand(interactionHand, stack); // Capture... @@ -303,7 +275,7 @@ public abstract class PlayerMixin_Attack_Impl extends LivingEntityMixin_Attack_I } @Inject(method = "actuallyHurt", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getDamageAfterArmorAbsorb(Lnet/minecraft/world/damagesource/DamageSource;F)F")) - public void attackImpl$startActuallyHurt(DamageSource damageSource, float originalDamage, CallbackInfo ci) { + protected void attackImpl$startActuallyHurt(DamageSource damageSource, float originalDamage, CallbackInfo ci) { // TODO check for direct call? this.attackImpl$actuallyHurt = new DamageEventUtil.ActuallyHurt((LivingEntity) (Object) this, new ArrayList<>(), damageSource, originalDamage); } @@ -316,7 +288,7 @@ public abstract class PlayerMixin_Attack_Impl extends LivingEntityMixin_Attack_I @ModifyVariable(method = "actuallyHurt", ordinal = 0, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;setAbsorptionAmount(F)V", shift = At.Shift.AFTER), argsOnly = true) - public float attackImpl$setFinalDamage(final float value) { + protected float attackImpl$setFinalDamage(final float value) { if (this.attackImpl$actuallyHurtResult.event().isCancelled()) { return 0; } @@ -327,7 +299,7 @@ public abstract class PlayerMixin_Attack_Impl extends LivingEntityMixin_Attack_I * Cleanup */ @Inject(method = "actuallyHurt", at = @At("RETURN")) - public void attackImpl$afterActuallyHurt(final DamageSource $$0, final float $$1, final CallbackInfo ci) { + protected void attackImpl$afterActuallyHurt(final DamageSource $$0, final float $$1, final CallbackInfo ci) { this.attackImpl$handlePostDamage(); this.attackImpl$actuallyHurt = null; this.attackImpl$actuallyHurtResult = null; diff --git a/src/mixins/resources/mixins.sponge.core.json b/src/mixins/resources/mixins.sponge.core.json index 5bac0960c56..e71cd5bab3a 100644 --- a/src/mixins/resources/mixins.sponge.core.json +++ b/src/mixins/resources/mixins.sponge.core.json @@ -1,310 +1,311 @@ { - "required": true, - "parent": "mixins.sponge.parent.json", - "package": "org.spongepowered.common.mixin.core", - "mixinPriority": 1101, - "mixins": [ - "advancements.AdvancementMixin", - "advancements.AdvancementProgressMixin", - "advancements.CriteriaTriggersMixin", - "advancements.CriterionMixin", - "advancements.CriterionProgressMixin", - "advancements.CriterionTrigger_ListenerMixin", - "advancements.DisplayInfoMixin", - "advancements.TreeNodePositionMixin", - "adventure.KeyMixin", - "adventure.bossbar.HackyBossBarPlatformBridgeMixin", - "adventure.chat.SignedMessageMixin", - "adventure.text.AbstractComponentMixin", - "adventure.text.format.StyleImplMixin", - "block.BedBlockMixin", - "block.BlockMixin", - "block.DyeColorBlockPropertiesMixin", - "commands.CommandsMixin", - "commands.CommandSourceMixin", - "commands.CommandSourceStackMixin", - "commands.arguments.ComponentArgumentMixin", - "commands.arguments.DimensionArgumentMixin", - "commands.arguments.selector.EntitySelectorParserMixin", - "commands.execution.tasks.ExecuteCommandMixin", - "core.MappedRegistryMixin", - "core.Vec3iMixin", - "core.dispenser.ProjectileDispenseBehaviorMixin", - "data.SpongeDataHolderMixin", - "item.EmptyMapItemMixin", - "item.ThrowableInaccurateItemMixin", - "item.YeetableInaccurateItemMixin", - "network.ConnectionMixin", - "network.PacketEncoderMixin", - "network.chat.Component_SerializerMixin", - "network.chat.ComponentSerializationMixin", - "network.chat.StyleMixin", - "network.chat.TranslatableContentsMixin", - "network.protocol.PacketMixin", - "network.protocol.common.ServerboundCustomPayloadPacketMixin", - "network.protocol.game.ClientboundResourcePackPushPacketMixin", - "network.protocol.game.ClientboundSetObjectivePacketMixin", - "network.protocol.game.ServerboundChunkBatchReceivedPacketMixin", - "network.protocol.game.ServerboundClientCommandPacketMixin", - "network.protocol.login.ClientboundCustomQueryPacketMixin", - "network.protocol.login.ServerboundCustomQueryAnswerPacketMixin", - "network.syncher.EntityDataAccessorMixin", - "network.syncher.SynchedEntityData_BuilderMixin", - "network.syncher.SynchedEntityDataMixin", - "registries.BuiltInRegistriesMixin", - "resources.RegistryDataLoaderMixin", - "server.MainMixin", - "server.MinecraftServerMixin", - "server.PlayerAdvancementsMixin", - "server.ReloadableServerRegistriesMixin", - "server.ServerFunctionManagerMixin", - "server.ServerScoreboardMixin", - "server.WorldLoaderMixin", - "server.bossevents.CustomBossEventMixin", - "server.commands.BossBarCommandsMixin", - "server.commands.DifficultyCommandMixin", - "server.commands.GameRuleCommandMixin", - "server.commands.KickCommandMixin", - "server.commands.SaveAllCommandMixin", - "server.commands.TeleportCommandMixin", - "server.commands.WeatherCommandMixin", - "server.commands.WorldBorderCommandMixin", - "server.dedicated.DedicatedPlayerListMixin", - "server.level.ChunkHolderMixin", - "server.level.ChunkMap_TrackedEntityMixin", - "server.level.ChunkMapMixin", - "server.level.DistanceManagerMixin", - "server.level.GenerationChunkHolderMixin", - "server.level.ServerBossEventMixin", - "server.level.ServerChunkCacheMixin", - "server.level.ServerEntityMixin", - "server.level.ServerLevelMixin", - "server.level.ServerPlayer_ContainerListenerMixin", - "server.level.ServerPlayerGameModeMixin", - "server.level.ServerPlayerMixin", - "server.level.ServerPlayerMixin_HealthScale", - "server.level.TicketMixin", - "server.network.LegacyQueryHandlerMixin", - "server.network.ServerCommonPacketListenerImplMixin", - "server.network.ServerConfigurationPacketListenerImplMixin", - "server.network.ServerGamePacketListenerImpl_1Mixin", - "server.network.ServerGamePacketListenerImplMixin", - "server.network.ServerHandshakePacketListenerImplMixin", - "server.network.ServerLoginPacketListenerImplMixin", - "server.network.ServerStatusPacketListenerImplMixin", - "server.players.BanListEntryMixin", - "server.players.GameProfileCache_GameProfileInfoMixin", - "server.players.GameProfileCacheMixin", - "server.players.IpBanListEntryMixin", - "server.players.PlayerListMixin", - "server.players.SleepStatusMixin", - "server.players.StoredUserEntryMixin", - "server.players.StoredUserListMixin", - "server.rcon.RconConsoleSourceMixin", - "server.rcon.thread.QueryThreadGs4Mixin", - "server.rcon.thread.RconClientMixin", - "service.permission.SubjectMixin", - "util.ClassInstanceMultiMapMixin", - "util.datafix.schemas.V1125Mixin", - "world.BaseCommandBlockMixin", - "world.BossEventMixin", - "world.damagesource.DamageSourceMixin", - "world.damagesource.DamageSourcesMixin", - "world.entity.ActiveChunkReferentMixin", - "world.entity.AgableMobMixin", - "world.entity.EntityMaxAirMixin", - "world.entity.EntityMixin", - "world.entity.EntitySelectorMixin", - "world.entity.EntityTypeMixin", - "world.entity.ExperienceOrbMixin", - "world.entity.GrieferBridgeMixin", - "world.entity.LeashableMixin", - "world.entity.LightningBoltMixin", - "world.entity.LivingEntityMixin", - "world.entity.LivingEntityMixin_Attack_Impl", - "world.entity.MobMixin", - "world.entity.MobMixin_Attack_Impl", - "world.entity.PortalProcessorMixin", - "world.entity.ai.goal.BreakDoorGoalMixin", - "world.entity.ai.goal.BreedGoalMixin", - "world.entity.ai.goal.EatBlockGoalMixin", - "world.entity.ai.goal.GoalMixin", - "world.entity.ai.goal.GoalSelectorMixin", - "world.entity.ai.goal.RangedAttackGoalMixin", - "world.entity.ai.goal.RunAroundLikeCrazyGoalMixin", - "world.entity.ai.sensing.NearestLivingEntitySensorMixin", - "world.entity.ai.sensing.SensorMixin", - "world.entity.animal.AnimalMixin", - "world.entity.animal.Cat_CatRelaxOnOwnerGoalMixin", - "world.entity.animal.OcelotMixin", - "world.entity.animal.ParrotMixin", - "world.entity.animal.Rabbit_RaidGardenGoalMixin", - "world.entity.animal.WolfMixin", - "world.entity.animal.horse.AbstractHorseMixin", - "world.entity.boss.enderdragon.EndCrystalMixin", - "world.entity.decoration.ArmorStandMixin", - "world.entity.decoration.BlockAttachedEntityMixin", - "world.entity.decoration.HangingEntityMixin", - "world.entity.decoration.ItemFrameMixin", - "world.entity.item.ItemEntityMixin", - "world.entity.item.PrimedTntMixin", - "world.entity.monster.Blaze_BlazeAttackGoalMixin", - "world.entity.monster.CreeperMixin", - "world.entity.monster.EnderMan_EndermanLeaveBlockGoalMixin", - "world.entity.monster.EnderMan_EndermanTakeBlockGoalMixin", - "world.entity.monster.EnderManMixin", - "world.entity.monster.EndermiteMixin", - "world.entity.monster.Ghast_GhastShootFireballGoalMixin", - "world.entity.monster.MonsterMixin", - "world.entity.monster.ShulkerMixin", - "world.entity.monster.Silverfish_SilverfishMergeWithStoneGoalMixin", - "world.entity.monster.Silverfish_SilverfishWakeUpFriendsGoalMixin", - "world.entity.monster.ZombifiedPiglinMixin", - "world.entity.npc.AbstractVillagerMixin", - "world.entity.npc.VillagerMixin", - "world.entity.npc.WanderingTraderMixin", - "world.entity.player.PlayerMixin", - "world.entity.player.PlayerMixin_Attack_Impl", - "world.entity.projectile.AbstractArrowMixin", - "world.entity.projectile.AbstractHurtingProjectileMixin", - "world.entity.projectile.EyeOfEnderMixin", - "world.entity.projectile.FireworkRocketEntityMixin", - "world.entity.projectile.FishingHookMixin", - "world.entity.projectile.LargeFireballMixin", - "world.entity.projectile.LlamaSpitMixin", - "world.entity.projectile.ProjectileMixin", - "world.entity.projectile.ProjectileUtilMixin", - "world.entity.projectile.ShulkerBulletMixin", - "world.entity.projectile.SmallFireballMixin", - "world.entity.projectile.SnowballMixin", - "world.entity.projectile.ThrowableProjectileMixin", - "world.entity.projectile.ThrownEggMixin", - "world.entity.projectile.ThrownEnderpearlMixin", - "world.entity.projectile.ThrownTridentMixin", - "world.entity.projectile.WitherSkullMixin", - "world.entity.vehicle.AbstractMinecartContainerMixin", - "world.entity.vehicle.AbstractMinecartMixin", - "world.entity.vehicle.BoatMixin", - "world.entity.vehicle.MinecartCommandBlock_MinecartCommandBaseMixin", - "world.entity.vehicle.MinecartCommandBlockMixin", - "world.entity.vehicle.MinecartFurnaceMixin", - "world.entity.vehicle.MinecartTNTMixin", - "world.entity.vehicle.VehicleEntityMixin", - "world.food.FoodDataMixin", - "world.inventory.StonecutterMenuMixin", - "world.item.ChorusFruitItemMixin", - "world.item.EnderEyeItemMixin", - "world.item.FireworkRocketItemMixin", - "world.item.FishingRodItemMixin", - "world.item.ItemCooldownsMixin", - "world.item.ItemMixin", - "world.item.ItemStackMixin", - "world.item.MapItemMixin", - "world.item.ServerItemCooldownsMixin", - "world.item.crafting.AbstractCookingRecipeMixin", - "world.item.crafting.IngredientMixin", - "world.item.crafting.RecipeManagerMixin", - "world.item.crafting.ShapedRecipe_SerializerMixin", - "world.item.crafting.ShapedRecipeMixin", - "world.item.crafting.ShapelessRecipe_SerializerMixin", - "world.item.crafting.ShapelessRecipeMixin", - "world.item.crafting.SimpleCookingSerializerMixin", - "world.item.crafting.SimpleCraftingRecipeSerializerMixin", - "world.item.crafting.SingleItemRecipeMixin", - "world.item.crafting.SmithingTransformRecipe_SerializerMixin", - "world.item.crafting.SmithingTransformRecipeMixin", - "world.item.crafting.StonecutterRecipe_SerializerMixin", - "world.item.enchantment.EnchantmentMixin", - "world.level.BaseSpawnerMixin", - "world.level.BlockGetterMixin", - "world.level.ExplosionMixin", - "world.level.LevelMixin", - "world.level.NaturalSpawner_SpawnStateMixin", - "world.level.NaturalSpawnerMixin", - "world.level.SavedDataMixin", - "world.level.biome.BiomeSourceMixin", - "world.level.block.BaseFireBlockMixin", - "world.level.block.CactusBlockMixin", - "world.level.block.CampfireBlockMixin", - "world.level.block.CarvedPumpkinBlockMixin", - "world.level.block.CommandBlockMixin", - "world.level.block.EndPortalBlockMixin", - "world.level.block.FallingBlockMixin", - "world.level.block.FarmBlockMixin", - "world.level.block.FireBlockMixin", - "world.level.block.LeavesBlockMixin", - "world.level.block.MagmaBlockMixin", - "world.level.block.NetherPortalBlockMixin", - "world.level.block.NoteBlockMixin", - "world.level.block.PointedDripstoneBlockMixin", - "world.level.block.SweetBerryBushBlockMixin", - "world.level.block.entity.AbstractFurnaceBlockEntityMixin", - "world.level.block.entity.BannerBlockEntityMixin", - "world.level.block.entity.BaseContainerBlockEntityMixin", - "world.level.block.entity.BellBLockEntityMixin", - "world.level.block.entity.BlockEntityMixin", - "world.level.block.entity.BrewingStandBlockEntityMixin", - "world.level.block.entity.CampfireBlockEntityMixin", - "world.level.block.entity.CommandBlockEntity_Mixin", - "world.level.block.entity.CommandBlockEntityMixin", - "world.level.block.entity.EnchantmentTableBlockEntityMixin", - "world.level.block.entity.LecternBlockEntityMixin", - "world.level.block.entity.SignBlockEntityMixin", - "world.level.block.entity.SkullBlockEntityMixin", - "world.level.block.piston.PistonMovingBlockEntityMixin", - "world.level.block.state.BlockBehaviour_PropertiesMixin", - "world.level.block.state.BlockStateMixin", - "world.level.border.WorldBorderMixin", - "world.level.chunk.ChunkGeneratorMixin", - "world.level.chunk.ChunkSerializerMixin", - "world.level.chunk.ChunkStatusTasksMixin", - "world.level.chunk.LevelChunkMixin", - "world.level.chunk.storage.EntityStorageMixin", - "world.level.chunk.storage.IOWorkerMixin", - "world.level.chunk.storage.RegionFileMixin", - "world.level.dimension.DimensionTypeMixin", - "world.level.dimension.LevelStemMixin", - "world.level.entity.PersistentEntitySectionManagerMixin", - "world.level.levelgen.NoiseGeneratorSettingsMixin", - "world.level.levelgen.WorldDimensionsMixin", - "world.level.levelgen.WorldOptionsMixin", - "world.level.levelgen.flat.FlatLayerInfoMixin", - "world.level.levelgen.flat.FlatLevelGeneratorSettingsMixin", - "world.level.levelgen.structure.LegacyStructureDataHandlerMixin", - "world.level.saveddata.MapIdTrackerMixin", - "world.level.storage.DimensionDataStorageMixin", - "world.level.storage.LevelDataMixin", - "world.level.storage.LevelStorageSource_LevelStorageAccessMixin", - "world.level.storage.PlayerDataStorageMixin", - "world.level.storage.PrimaryLevelDataMixin", - "world.level.storage.ServerLevelDataMixin", - "world.scores.ObjectiveMixin", - "world.scores.PlayerTeamMixin", - "world.scores.ScoreboardMixin", - "world.ticks.LevelChunkTicksMixin", - "world.ticks.LevelTicksMixin", - "world.ticks.ScheduledTickMixin" - ], - "client": [ - "client.MinecraftMixin", - "client.multiplayer.ClientCommonPacketListenerImplMixin", - "client.multiplayer.ClientHandshakePacketListenerImplMixin", - "client.multiplayer.ClientLevelMixin", - "client.network.chat.TranslatableContentsMixin", - "client.player.AbstractClientPlayerMixin", - "client.player.LocalPlayerMixin", - "client.server.IntegratedPlayerListMixin", - "client.server.IntegratedServerMixin", - "server.network.MemoryServerHandshakePacketListenerImplMixin" - ], - "server": [ - "server.dedicated.DedicatedServerMixin", - "server.dedicated.ServerWatchdogMixin" - ], - "injectors": { - "maxShiftBy": 3 - }, - "overwrites": { - "conformVisibility": true - } + "required": true, + "parent": "mixins.sponge.parent.json", + "package": "org.spongepowered.common.mixin.core", + "mixinPriority": 1101, + "mixins": [ + "advancements.AdvancementMixin", + "advancements.AdvancementProgressMixin", + "advancements.CriteriaTriggersMixin", + "advancements.CriterionMixin", + "advancements.CriterionProgressMixin", + "advancements.CriterionTrigger_ListenerMixin", + "advancements.DisplayInfoMixin", + "advancements.TreeNodePositionMixin", + "adventure.KeyMixin", + "adventure.bossbar.HackyBossBarPlatformBridgeMixin", + "adventure.chat.SignedMessageMixin", + "adventure.text.AbstractComponentMixin", + "adventure.text.format.StyleImplMixin", + "block.BedBlockMixin", + "block.BlockMixin", + "block.DyeColorBlockPropertiesMixin", + "commands.CommandsMixin", + "commands.CommandSourceMixin", + "commands.CommandSourceStackMixin", + "commands.arguments.ComponentArgumentMixin", + "commands.arguments.DimensionArgumentMixin", + "commands.arguments.selector.EntitySelectorParserMixin", + "commands.execution.tasks.ExecuteCommandMixin", + "core.MappedRegistryMixin", + "core.Vec3iMixin", + "core.dispenser.ProjectileDispenseBehaviorMixin", + "data.SpongeDataHolderMixin", + "item.EmptyMapItemMixin", + "item.ThrowableInaccurateItemMixin", + "item.YeetableInaccurateItemMixin", + "network.ConnectionMixin", + "network.PacketEncoderMixin", + "network.chat.Component_SerializerMixin", + "network.chat.ComponentSerializationMixin", + "network.chat.StyleMixin", + "network.chat.TranslatableContentsMixin", + "network.protocol.PacketMixin", + "network.protocol.common.ServerboundCustomPayloadPacketMixin", + "network.protocol.game.ClientboundResourcePackPushPacketMixin", + "network.protocol.game.ClientboundSetObjectivePacketMixin", + "network.protocol.game.ServerboundChunkBatchReceivedPacketMixin", + "network.protocol.game.ServerboundClientCommandPacketMixin", + "network.protocol.login.ClientboundCustomQueryPacketMixin", + "network.protocol.login.ServerboundCustomQueryAnswerPacketMixin", + "network.syncher.EntityDataAccessorMixin", + "network.syncher.SynchedEntityData_BuilderMixin", + "network.syncher.SynchedEntityDataMixin", + "registries.BuiltInRegistriesMixin", + "resources.RegistryDataLoaderMixin", + "server.MainMixin", + "server.MinecraftServerMixin", + "server.PlayerAdvancementsMixin", + "server.ReloadableServerRegistriesMixin", + "server.ServerFunctionManagerMixin", + "server.ServerScoreboardMixin", + "server.WorldLoaderMixin", + "server.bossevents.CustomBossEventMixin", + "server.commands.BossBarCommandsMixin", + "server.commands.DifficultyCommandMixin", + "server.commands.GameRuleCommandMixin", + "server.commands.KickCommandMixin", + "server.commands.SaveAllCommandMixin", + "server.commands.TeleportCommandMixin", + "server.commands.WeatherCommandMixin", + "server.commands.WorldBorderCommandMixin", + "server.dedicated.DedicatedPlayerListMixin", + "server.level.ChunkHolderMixin", + "server.level.ChunkMap_TrackedEntityMixin", + "server.level.ChunkMapMixin", + "server.level.DistanceManagerMixin", + "server.level.GenerationChunkHolderMixin", + "server.level.ServerBossEventMixin", + "server.level.ServerChunkCacheMixin", + "server.level.ServerEntityMixin", + "server.level.ServerLevelMixin", + "server.level.ServerPlayer_ContainerListenerMixin", + "server.level.ServerPlayerGameModeMixin", + "server.level.ServerPlayerMixin", + "server.level.ServerPlayerMixin_Attack_impl", + "server.level.ServerPlayerMixin_HealthScale", + "server.level.TicketMixin", + "server.network.LegacyQueryHandlerMixin", + "server.network.ServerCommonPacketListenerImplMixin", + "server.network.ServerConfigurationPacketListenerImplMixin", + "server.network.ServerGamePacketListenerImpl_1Mixin", + "server.network.ServerGamePacketListenerImplMixin", + "server.network.ServerHandshakePacketListenerImplMixin", + "server.network.ServerLoginPacketListenerImplMixin", + "server.network.ServerStatusPacketListenerImplMixin", + "server.players.BanListEntryMixin", + "server.players.GameProfileCache_GameProfileInfoMixin", + "server.players.GameProfileCacheMixin", + "server.players.IpBanListEntryMixin", + "server.players.PlayerListMixin", + "server.players.SleepStatusMixin", + "server.players.StoredUserEntryMixin", + "server.players.StoredUserListMixin", + "server.rcon.RconConsoleSourceMixin", + "server.rcon.thread.QueryThreadGs4Mixin", + "server.rcon.thread.RconClientMixin", + "service.permission.SubjectMixin", + "util.ClassInstanceMultiMapMixin", + "util.datafix.schemas.V1125Mixin", + "world.BaseCommandBlockMixin", + "world.BossEventMixin", + "world.damagesource.DamageSourceMixin", + "world.damagesource.DamageSourcesMixin", + "world.entity.ActiveChunkReferentMixin", + "world.entity.AgableMobMixin", + "world.entity.EntityMaxAirMixin", + "world.entity.EntityMixin", + "world.entity.EntitySelectorMixin", + "world.entity.EntityTypeMixin", + "world.entity.ExperienceOrbMixin", + "world.entity.GrieferBridgeMixin", + "world.entity.LeashableMixin", + "world.entity.LightningBoltMixin", + "world.entity.LivingEntityMixin", + "world.entity.LivingEntityMixin_Attack_Impl", + "world.entity.MobMixin", + "world.entity.MobMixin_Attack_Impl", + "world.entity.PortalProcessorMixin", + "world.entity.ai.goal.BreakDoorGoalMixin", + "world.entity.ai.goal.BreedGoalMixin", + "world.entity.ai.goal.EatBlockGoalMixin", + "world.entity.ai.goal.GoalMixin", + "world.entity.ai.goal.GoalSelectorMixin", + "world.entity.ai.goal.RangedAttackGoalMixin", + "world.entity.ai.goal.RunAroundLikeCrazyGoalMixin", + "world.entity.ai.sensing.NearestLivingEntitySensorMixin", + "world.entity.ai.sensing.SensorMixin", + "world.entity.animal.AnimalMixin", + "world.entity.animal.Cat_CatRelaxOnOwnerGoalMixin", + "world.entity.animal.OcelotMixin", + "world.entity.animal.ParrotMixin", + "world.entity.animal.Rabbit_RaidGardenGoalMixin", + "world.entity.animal.WolfMixin", + "world.entity.animal.horse.AbstractHorseMixin", + "world.entity.boss.enderdragon.EndCrystalMixin", + "world.entity.decoration.ArmorStandMixin", + "world.entity.decoration.BlockAttachedEntityMixin", + "world.entity.decoration.HangingEntityMixin", + "world.entity.decoration.ItemFrameMixin", + "world.entity.item.ItemEntityMixin", + "world.entity.item.PrimedTntMixin", + "world.entity.monster.Blaze_BlazeAttackGoalMixin", + "world.entity.monster.CreeperMixin", + "world.entity.monster.EnderMan_EndermanLeaveBlockGoalMixin", + "world.entity.monster.EnderMan_EndermanTakeBlockGoalMixin", + "world.entity.monster.EnderManMixin", + "world.entity.monster.EndermiteMixin", + "world.entity.monster.Ghast_GhastShootFireballGoalMixin", + "world.entity.monster.MonsterMixin", + "world.entity.monster.ShulkerMixin", + "world.entity.monster.Silverfish_SilverfishMergeWithStoneGoalMixin", + "world.entity.monster.Silverfish_SilverfishWakeUpFriendsGoalMixin", + "world.entity.monster.ZombifiedPiglinMixin", + "world.entity.npc.AbstractVillagerMixin", + "world.entity.npc.VillagerMixin", + "world.entity.npc.WanderingTraderMixin", + "world.entity.player.PlayerMixin", + "world.entity.player.PlayerMixin_Attack_Impl", + "world.entity.projectile.AbstractArrowMixin", + "world.entity.projectile.AbstractHurtingProjectileMixin", + "world.entity.projectile.EyeOfEnderMixin", + "world.entity.projectile.FireworkRocketEntityMixin", + "world.entity.projectile.FishingHookMixin", + "world.entity.projectile.LargeFireballMixin", + "world.entity.projectile.LlamaSpitMixin", + "world.entity.projectile.ProjectileMixin", + "world.entity.projectile.ProjectileUtilMixin", + "world.entity.projectile.ShulkerBulletMixin", + "world.entity.projectile.SmallFireballMixin", + "world.entity.projectile.SnowballMixin", + "world.entity.projectile.ThrowableProjectileMixin", + "world.entity.projectile.ThrownEggMixin", + "world.entity.projectile.ThrownEnderpearlMixin", + "world.entity.projectile.ThrownTridentMixin", + "world.entity.projectile.WitherSkullMixin", + "world.entity.vehicle.AbstractMinecartContainerMixin", + "world.entity.vehicle.AbstractMinecartMixin", + "world.entity.vehicle.BoatMixin", + "world.entity.vehicle.MinecartCommandBlock_MinecartCommandBaseMixin", + "world.entity.vehicle.MinecartCommandBlockMixin", + "world.entity.vehicle.MinecartFurnaceMixin", + "world.entity.vehicle.MinecartTNTMixin", + "world.entity.vehicle.VehicleEntityMixin", + "world.food.FoodDataMixin", + "world.inventory.StonecutterMenuMixin", + "world.item.ChorusFruitItemMixin", + "world.item.EnderEyeItemMixin", + "world.item.FireworkRocketItemMixin", + "world.item.FishingRodItemMixin", + "world.item.ItemCooldownsMixin", + "world.item.ItemMixin", + "world.item.ItemStackMixin", + "world.item.MapItemMixin", + "world.item.ServerItemCooldownsMixin", + "world.item.crafting.AbstractCookingRecipeMixin", + "world.item.crafting.IngredientMixin", + "world.item.crafting.RecipeManagerMixin", + "world.item.crafting.ShapedRecipe_SerializerMixin", + "world.item.crafting.ShapedRecipeMixin", + "world.item.crafting.ShapelessRecipe_SerializerMixin", + "world.item.crafting.ShapelessRecipeMixin", + "world.item.crafting.SimpleCookingSerializerMixin", + "world.item.crafting.SimpleCraftingRecipeSerializerMixin", + "world.item.crafting.SingleItemRecipeMixin", + "world.item.crafting.SmithingTransformRecipe_SerializerMixin", + "world.item.crafting.SmithingTransformRecipeMixin", + "world.item.crafting.StonecutterRecipe_SerializerMixin", + "world.item.enchantment.EnchantmentMixin", + "world.level.BaseSpawnerMixin", + "world.level.BlockGetterMixin", + "world.level.ExplosionMixin", + "world.level.LevelMixin", + "world.level.NaturalSpawner_SpawnStateMixin", + "world.level.NaturalSpawnerMixin", + "world.level.SavedDataMixin", + "world.level.biome.BiomeSourceMixin", + "world.level.block.BaseFireBlockMixin", + "world.level.block.CactusBlockMixin", + "world.level.block.CampfireBlockMixin", + "world.level.block.CarvedPumpkinBlockMixin", + "world.level.block.CommandBlockMixin", + "world.level.block.EndPortalBlockMixin", + "world.level.block.FallingBlockMixin", + "world.level.block.FarmBlockMixin", + "world.level.block.FireBlockMixin", + "world.level.block.LeavesBlockMixin", + "world.level.block.MagmaBlockMixin", + "world.level.block.NetherPortalBlockMixin", + "world.level.block.NoteBlockMixin", + "world.level.block.PointedDripstoneBlockMixin", + "world.level.block.SweetBerryBushBlockMixin", + "world.level.block.entity.AbstractFurnaceBlockEntityMixin", + "world.level.block.entity.BannerBlockEntityMixin", + "world.level.block.entity.BaseContainerBlockEntityMixin", + "world.level.block.entity.BellBLockEntityMixin", + "world.level.block.entity.BlockEntityMixin", + "world.level.block.entity.BrewingStandBlockEntityMixin", + "world.level.block.entity.CampfireBlockEntityMixin", + "world.level.block.entity.CommandBlockEntity_Mixin", + "world.level.block.entity.CommandBlockEntityMixin", + "world.level.block.entity.EnchantmentTableBlockEntityMixin", + "world.level.block.entity.LecternBlockEntityMixin", + "world.level.block.entity.SignBlockEntityMixin", + "world.level.block.entity.SkullBlockEntityMixin", + "world.level.block.piston.PistonMovingBlockEntityMixin", + "world.level.block.state.BlockBehaviour_PropertiesMixin", + "world.level.block.state.BlockStateMixin", + "world.level.border.WorldBorderMixin", + "world.level.chunk.ChunkGeneratorMixin", + "world.level.chunk.ChunkSerializerMixin", + "world.level.chunk.ChunkStatusTasksMixin", + "world.level.chunk.LevelChunkMixin", + "world.level.chunk.storage.EntityStorageMixin", + "world.level.chunk.storage.IOWorkerMixin", + "world.level.chunk.storage.RegionFileMixin", + "world.level.dimension.DimensionTypeMixin", + "world.level.dimension.LevelStemMixin", + "world.level.entity.PersistentEntitySectionManagerMixin", + "world.level.levelgen.NoiseGeneratorSettingsMixin", + "world.level.levelgen.WorldDimensionsMixin", + "world.level.levelgen.WorldOptionsMixin", + "world.level.levelgen.flat.FlatLayerInfoMixin", + "world.level.levelgen.flat.FlatLevelGeneratorSettingsMixin", + "world.level.levelgen.structure.LegacyStructureDataHandlerMixin", + "world.level.saveddata.MapIdTrackerMixin", + "world.level.storage.DimensionDataStorageMixin", + "world.level.storage.LevelDataMixin", + "world.level.storage.LevelStorageSource_LevelStorageAccessMixin", + "world.level.storage.PlayerDataStorageMixin", + "world.level.storage.PrimaryLevelDataMixin", + "world.level.storage.ServerLevelDataMixin", + "world.scores.ObjectiveMixin", + "world.scores.PlayerTeamMixin", + "world.scores.ScoreboardMixin", + "world.ticks.LevelChunkTicksMixin", + "world.ticks.LevelTicksMixin", + "world.ticks.ScheduledTickMixin" + ], + "client": [ + "client.MinecraftMixin", + "client.multiplayer.ClientCommonPacketListenerImplMixin", + "client.multiplayer.ClientHandshakePacketListenerImplMixin", + "client.multiplayer.ClientLevelMixin", + "client.network.chat.TranslatableContentsMixin", + "client.player.AbstractClientPlayerMixin", + "client.player.LocalPlayerMixin", + "client.server.IntegratedPlayerListMixin", + "client.server.IntegratedServerMixin", + "server.network.MemoryServerHandshakePacketListenerImplMixin" + ], + "server": [ + "server.dedicated.DedicatedServerMixin", + "server.dedicated.ServerWatchdogMixin" + ], + "injectors": { + "maxShiftBy": 3 + }, + "overwrites": { + "conformVisibility": true + } } From 680b7f5737ff4c4a3cb5832b4e0c0e0a5d08c386 Mon Sep 17 00:00:00 2001 From: Gabriel Harris-Rouquette Date: Sat, 18 Jan 2025 22:21:14 -0800 Subject: [PATCH 5/5] chore: use empty ItemStacks where possible W --- .../common/event/SpongeCommonEventFactory.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/spongepowered/common/event/SpongeCommonEventFactory.java b/src/main/java/org/spongepowered/common/event/SpongeCommonEventFactory.java index acb07d0647e..6fb407f86a8 100644 --- a/src/main/java/org/spongepowered/common/event/SpongeCommonEventFactory.java +++ b/src/main/java/org/spongepowered/common/event/SpongeCommonEventFactory.java @@ -664,7 +664,7 @@ public static Optional detonateExplosive(fi * @param frame * @return The item if it is to be spawned, null if to be ignored */ - public static @Nullable ItemStack throwDropItemAndConstructEvent(final net.minecraft.world.entity.Entity entity, final double posX, final double posY, + public static ItemStack throwDropItemAndConstructEvent(final net.minecraft.world.entity.Entity entity, final double posX, final double posY, final double posZ, final ItemStackSnapshot snapshot, final List original, final CauseStackManager.StackFrame frame) { final ItemStack item; @@ -675,10 +675,10 @@ public static Optional detonateExplosive(fi ImmutableList.of(snapshot), original); SpongeCommon.post(dropEvent); if (dropEvent.isCancelled()) { - return null; + return ItemStack.EMPTY; } if (dropEvent.droppedItems().isEmpty()) { - return null; + return ItemStack.EMPTY; } // SECOND throw the ConstructEntityEvent @@ -687,12 +687,12 @@ public static Optional detonateExplosive(fi frame.removeContext(EventContextKeys.SPAWN_TYPE); SpongeCommon.post(event); if (event.isCancelled()) { - return null; + return ItemStack.EMPTY; } - item = event.isCancelled() ? null : ItemStackUtil.fromSnapshotToNative(dropEvent.droppedItems().get(0)); + item = event.isCancelled() ? ItemStack.EMPTY : ItemStackUtil.fromSnapshotToNative(dropEvent.droppedItems().get(0)); if (item == null) { - return null; + return ItemStack.EMPTY; } return item; }