diff --git a/patches/minecraft/net/minecraft/world/entity/LivingEntity.java.patch b/patches/minecraft/net/minecraft/world/entity/LivingEntity.java.patch
index 139e3ebb6c..45641c4a44 100644
--- a/patches/minecraft/net/minecraft/world/entity/LivingEntity.java.patch
+++ b/patches/minecraft/net/minecraft/world/entity/LivingEntity.java.patch
@@ -123,6 +123,29 @@
if (this.m_6673_(p_21016_)) {
return false;
} else if (this.f_19853_.f_46443_) {
+@@ -998,9 +_,11 @@
+ boolean flag = false;
+ float f1 = 0.0F;
+ if (p_21017_ > 0.0F && this.m_21275_(p_21016_)) {
+- this.m_7909_(p_21017_);
+- f1 = p_21017_;
+- p_21017_ = 0.0F;
++ net.minecraftforge.event.entity.living.ShieldBlockEvent ev = net.minecraftforge.common.ForgeHooks.onShieldBlock(this, p_21016_, p_21017_);
++ if(!ev.isCanceled()) {
++ if(ev.shieldTakesDamage()) this.m_7909_(p_21017_);
++ f1 = ev.getBlockedDamage();
++ p_21017_ -= ev.getBlockedDamage();
+ if (!p_21016_.m_19360_()) {
+ Entity entity = p_21016_.m_7640_();
+ if (entity instanceof LivingEntity) {
+@@ -1010,6 +_,7 @@
+
+ flag = true;
+ }
++ }
+
+ this.f_20924_ = 1.5F;
+ boolean flag1 = true;
@@ -1044,11 +_,11 @@
if (entity1 instanceof Player) {
this.f_20889_ = 100;
diff --git a/src/main/java/net/minecraftforge/common/ForgeHooks.java b/src/main/java/net/minecraftforge/common/ForgeHooks.java
index 32fa24e675..59ddd6cf5e 100644
--- a/src/main/java/net/minecraftforge/common/ForgeHooks.java
+++ b/src/main/java/net/minecraftforge/common/ForgeHooks.java
@@ -153,6 +153,7 @@ import net.minecraftforge.event.entity.living.LivingHurtEvent;
import net.minecraftforge.event.entity.living.LivingKnockBackEvent;
import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent;
import net.minecraftforge.event.entity.living.LootingLevelEvent;
+import net.minecraftforge.event.entity.living.ShieldBlockEvent;
import net.minecraftforge.event.entity.player.AdvancementEvent;
import net.minecraftforge.event.entity.player.AnvilRepairEvent;
import net.minecraftforge.event.entity.player.AttackEntityEvent;
@@ -1257,7 +1258,14 @@ public class ForgeHooks
{
MinecraftForge.EVENT_BUS.post(new EntityEvent.EnteringSection(entity, packedOldPos, packedNewPos));
}
-
+
+ public static ShieldBlockEvent onShieldBlock(LivingEntity blocker, DamageSource source, float blocked)
+ {
+ ShieldBlockEvent e = new ShieldBlockEvent(blocker, source, blocked);
+ MinecraftForge.EVENT_BUS.post(e);
+ return e;
+ }
+
/**
* Called when dimension jsons are parsed.
* Creates a copy of the worldgen settings and its dimension registry,
diff --git a/src/main/java/net/minecraftforge/event/entity/living/ShieldBlockEvent.java b/src/main/java/net/minecraftforge/event/entity/living/ShieldBlockEvent.java
new file mode 100644
index 0000000000..6f6b0f9e01
--- /dev/null
+++ b/src/main/java/net/minecraftforge/event/entity/living/ShieldBlockEvent.java
@@ -0,0 +1,101 @@
+/*
+ * Minecraft Forge
+ * Copyright (c) 2016-2021.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package net.minecraftforge.event.entity.living;
+
+import net.minecraft.util.Mth;
+import net.minecraft.world.damagesource.DamageSource;
+import net.minecraft.world.entity.LivingEntity;
+import net.minecraftforge.eventbus.api.Cancelable;
+
+/**
+ * The ShieldBlockEvent is fired when an entity successfully blocks with a shield.
+ * Cancelling this event will have the same impact as if the shield was not eligible to block.
+ * The damage blocked cannot be set lower than zero or greater than the original value.
+ * Note: The shield item stack "should" be available from {@link LivingEntity#getUseItem()}
+ * at least for players.
+ */
+@Cancelable
+public class ShieldBlockEvent extends LivingEvent
+{
+ private final DamageSource source;
+ private final float originalBlocked;
+ private float dmgBlocked;
+ private boolean shieldTakesDamage = true;
+
+ public ShieldBlockEvent(LivingEntity blocker, DamageSource source, float blocked)
+ {
+ super(blocker);
+ this.source = source;
+ this.originalBlocked = blocked;
+ this.dmgBlocked = blocked;
+ }
+
+ /**
+ * @return The damage source.
+ */
+ public DamageSource getDamageSource()
+ {
+ return this.source;
+ }
+
+ /**
+ * @return The original amount of damage blocked, which is the same as the original
+ * incoming damage value.
+ */
+ public float getOriginalBlockedDamage()
+ {
+ return this.originalBlocked;
+ }
+
+ /**
+ * @return The current amount of damage blocked, as a result of this event.
+ */
+ public float getBlockedDamage()
+ {
+ return this.dmgBlocked;
+ }
+
+ /**
+ * Controls if {@link LivingEntity#hurtCurrentlyUsedShield} is called.
+ * @return If the shield item will take durability damage or not.
+ */
+ public boolean shieldTakesDamage()
+ {
+ return this.shieldTakesDamage;
+ }
+
+ /**
+ * Set how much damage is blocked by this action.
+ * Note that initially the blocked amount is the entire attack.
+ */
+ public void setBlockedDamage(float blocked)
+ {
+ this.dmgBlocked = Mth.clamp(blocked, 0, this.originalBlocked);
+ }
+
+ /**
+ * Set if the shield will take durability damage or not.
+ */
+ public void setShieldTakesDamage(boolean damage)
+ {
+ this.shieldTakesDamage = damage;
+ }
+
+}
diff --git a/src/test/java/net/minecraftforge/debug/item/ShieldBlockTest.java b/src/test/java/net/minecraftforge/debug/item/ShieldBlockTest.java
new file mode 100644
index 0000000000..a11fd2a5ca
--- /dev/null
+++ b/src/test/java/net/minecraftforge/debug/item/ShieldBlockTest.java
@@ -0,0 +1,52 @@
+/*
+ * Minecraft Forge
+ * Copyright (c) 2016-2021.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package net.minecraftforge.debug.item;
+
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.entity.projectile.AbstractArrow;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.Items;
+import net.minecraftforge.event.entity.living.ShieldBlockEvent;
+import net.minecraftforge.eventbus.api.SubscribeEvent;
+import net.minecraftforge.fml.common.Mod;
+
+/**
+ * The ShieldBlockTest is the test mod for the ShieldBlockEvent.
+ * If successful, this handler will trigger when an arrow is blocked by a player.
+ * The event will give the arrow to the player, and make the player receive half of the damage (instead of zero damage).
+ * Note this just gives them a normal arrow, retrieving the true arrow requires some reflection.
+ */
+@Mod(ShieldBlockTest.MOD_ID)
+@Mod.EventBusSubscriber
+public class ShieldBlockTest
+{
+ static final String MOD_ID = "shield_block_event";
+
+ @SubscribeEvent
+ public static void shieldBlock(ShieldBlockEvent event)
+ {
+ if (event.getDamageSource().getDirectEntity() instanceof AbstractArrow arrow && event.getEntityLiving() instanceof Player player)
+ {
+ player.getInventory().add(new ItemStack(Items.ARROW));
+ event.setBlockedDamage(event.getOriginalBlockedDamage() / 2);
+ arrow.discard();
+ }
+ }
+}
diff --git a/src/test/resources/META-INF/mods.toml b/src/test/resources/META-INF/mods.toml
index d305624ab0..53f90826ae 100644
--- a/src/test/resources/META-INF/mods.toml
+++ b/src/test/resources/META-INF/mods.toml
@@ -178,6 +178,8 @@ license="LGPL v2.1"
modId="duplicate_optional_tag_test"
[[mods]]
modId="custom_particle_type_test"
+[[mods]]
+ modId="shield_block_event"
[[mods]]
modId="renderable_test"
[[mods]]