diff --git a/patches/minecraft/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch b/patches/minecraft/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch index a6c8d5c0c1..6d2182e02e 100644 --- a/patches/minecraft/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch +++ b/patches/minecraft/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch @@ -25,6 +25,19 @@ this.f_9742_.m_129512_(new ClientboundMoveVehiclePacket(entity)); return; } +@@ -983,8 +_,10 @@ + case SWAP_ITEM_WITH_OFFHAND: + if (!this.f_9743_.m_5833_()) { + ItemStack itemstack = this.f_9743_.m_21120_(InteractionHand.OFF_HAND); +- this.f_9743_.m_21008_(InteractionHand.OFF_HAND, this.f_9743_.m_21120_(InteractionHand.MAIN_HAND)); +- this.f_9743_.m_21008_(InteractionHand.MAIN_HAND, itemstack); ++ var event = net.minecraftforge.common.ForgeHooks.onLivingSwapHandItems(this.f_9743_); ++ if (event.isCanceled()) return; ++ this.f_9743_.m_21008_(InteractionHand.OFF_HAND, event.getItemSwappedToOffHand()); ++ this.f_9743_.m_21008_(InteractionHand.MAIN_HAND, event.getItemSwappedToMainHand()); + this.f_9743_.m_5810_(); + } + @@ -1035,7 +_,7 @@ Vec3 vec3 = blockhitresult.m_82450_(); BlockPos blockpos = blockhitresult.m_82425_(); diff --git a/patches/minecraft/net/minecraft/world/entity/LivingEntity.java.patch b/patches/minecraft/net/minecraft/world/entity/LivingEntity.java.patch index 46ea1b357a..7f7a0a7856 100644 --- a/patches/minecraft/net/minecraft/world/entity/LivingEntity.java.patch +++ b/patches/minecraft/net/minecraft/world/entity/LivingEntity.java.patch @@ -370,6 +370,21 @@ if (!this.f_20911_ || this.f_20913_ >= this.m_21304_() / 2 || this.f_20913_ < 0) { this.f_20913_ = -1; this.f_20911_ = true; +@@ -1760,9 +_,11 @@ + } + + private void m_21312_() { +- ItemStack itemstack = this.m_6844_(EquipmentSlot.OFFHAND); +- this.m_8061_(EquipmentSlot.OFFHAND, this.m_6844_(EquipmentSlot.MAINHAND)); +- this.m_8061_(EquipmentSlot.MAINHAND, itemstack); ++ ItemStack itemstack = this.m_6844_(EquipmentSlot.OFFHAND); ++ var event = net.minecraftforge.common.ForgeHooks.onLivingSwapHandItems(this); ++ if (event.isCanceled()) return; ++ this.m_8061_(EquipmentSlot.OFFHAND, event.getItemSwappedToOffHand()); ++ this.m_8061_(EquipmentSlot.MAINHAND, event.getItemSwappedToMainHand()); + } + + protected void m_6088_() { @@ -1955,14 +_,17 @@ } diff --git a/src/main/java/net/minecraftforge/common/ForgeHooks.java b/src/main/java/net/minecraftforge/common/ForgeHooks.java index dbe5e69489..de57728712 100644 --- a/src/main/java/net/minecraftforge/common/ForgeHooks.java +++ b/src/main/java/net/minecraftforge/common/ForgeHooks.java @@ -160,6 +160,7 @@ import net.minecraftforge.event.entity.living.LivingChangeTargetEvent.ILivingTar import net.minecraftforge.event.entity.living.LivingFallEvent; import net.minecraftforge.event.entity.living.LivingHurtEvent; import net.minecraftforge.event.entity.living.LivingKnockBackEvent; +import net.minecraftforge.event.entity.living.LivingSwapItemsEvent; import net.minecraftforge.event.entity.living.LivingUseTotemEvent; import net.minecraftforge.event.entity.living.LootingLevelEvent; import net.minecraftforge.event.entity.living.ShieldBlockEvent; @@ -1226,6 +1227,13 @@ public class ForgeHooks return e; } + public static LivingSwapItemsEvent.Hands onLivingSwapHandItems(LivingEntity livingEntity) + { + LivingSwapItemsEvent.Hands event = new LivingSwapItemsEvent.Hands(livingEntity); + MinecraftForge.EVENT_BUS.post(event); + return event; + } + public static void writeAdditionalLevelSaveData(WorldData worldData, CompoundTag levelTag) { CompoundTag fmlData = new CompoundTag(); @@ -1536,7 +1544,7 @@ public class ForgeHooks * Handles living entities being under water. This fires the {@link LivingBreatheEvent} and if the entity's air supply * is less than or equal to zero also the {@link LivingDrownEvent}. Additionally when the entity is under water it will * dismount if {@link IForgeEntity#canBeRiddenUnderFluidType(FluidType, Entity)} returns false. - * + * * @param entity The living entity which is currently updated * @param consumeAirAmount The amount of air to consume when the entity is unable to breathe * @param refillAirAmount The amount of air to refill when the entity is able to breathe diff --git a/src/main/java/net/minecraftforge/event/entity/living/LivingSwapItemsEvent.java b/src/main/java/net/minecraftforge/event/entity/living/LivingSwapItemsEvent.java new file mode 100644 index 0000000000..a3616d96b6 --- /dev/null +++ b/src/main/java/net/minecraftforge/event/entity/living/LivingSwapItemsEvent.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.event.entity.living; + +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.eventbus.api.Cancelable; +import org.jetbrains.annotations.ApiStatus; + +public class LivingSwapItemsEvent extends LivingEvent +{ + + @ApiStatus.Internal + public LivingSwapItemsEvent(LivingEntity entity) + { + super(entity); + } + + /** + * This event is fired when a living entity is about to swap the items in their main and offhand. + * This event is executed in {@link ServerGamePacketListenerImpl#handlePlayerAction} + * + *

This event is {@linkplain Cancelable cancellable}, and does not {@linkplain HasResult have a result}. + */ + @Cancelable + public static class Hands extends LivingSwapItemsEvent + { + private ItemStack toMainHand; + private ItemStack toOffHand; + + @ApiStatus.Internal + public Hands(LivingEntity entity) + { + super(entity); + this.toMainHand = entity.getOffhandItem(); //the main hand will be swapped with the offhand + this.toOffHand = entity.getMainHandItem(); //the offhand will be swapped with the main hand + } + + /** + * Gets the item that will be swapped to the main hand of the entity. + * @return The item that will be swapped to the main hand. + */ + public ItemStack getItemSwappedToMainHand() + { + return toMainHand; + } + + /** + * Gets the item that will be swapped to the offhand of the entity. + * @return The item that will be swapped to the offhand. + */ + public ItemStack getItemSwappedToOffHand() + { + return toOffHand; + } + + /** + * Sets the item that will be swapped to the main hand of the entity. + * @param item The item to swap to the main hand. + */ + public void setItemSwappedToMainHand(ItemStack item) + { + this.toMainHand = item; + } + + /** + * Sets the item that will be swapped to the offhand of the entity. + * @param item The item to swap to the offhand. + */ + public void setItemSwappedToOffHand(ItemStack item) + { + this.toOffHand = item; + } + } +} diff --git a/src/test/java/net/minecraftforge/debug/entity/living/LivingSwapItemsEventTest.java b/src/test/java/net/minecraftforge/debug/entity/living/LivingSwapItemsEventTest.java new file mode 100644 index 0000000000..29c1b676bf --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/entity/living/LivingSwapItemsEventTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.debug.entity.living; + +import net.minecraft.world.item.Items; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.living.LivingSwapItemsEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@Mod("living_swap_items_event_test") +public class LivingSwapItemsEventTest +{ + public static final boolean ENABLE = true; + public static final Logger LOGGER = LogManager.getLogger(); + + public LivingSwapItemsEventTest() + { + if (ENABLE) + MinecraftForge.EVENT_BUS.register(this); + } + + @SubscribeEvent + public void livingSwapItems(LivingSwapItemsEvent.Hands event) + { + LOGGER.info("{} swapping hands. To offhand: {}, to main hand: {}", event.getEntity(), event.getItemSwappedToOffHand(), event.getItemSwappedToMainHand()); + if (event.getEntity().getMainHandItem().is(Items.DIAMOND)) + event.setCanceled(true); + } +} diff --git a/src/test/resources/META-INF/mods.toml b/src/test/resources/META-INF/mods.toml index c0066344f0..98eb540d13 100644 --- a/src/test/resources/META-INF/mods.toml +++ b/src/test/resources/META-INF/mods.toml @@ -35,6 +35,9 @@ modId="mdk_datagen" [[mods]] modId="level_sensitive_light_block_test" +[[mods]] + modId="living_swap_items_event_test" + # LEGACY TEST CASES ###### The mods below are from the old test framework and need to be yeeted later again.