diff options
Diffstat (limited to 'patches/server/1066-Fix-incorrect-invulnerability-damage-reduction.patch')
-rw-r--r-- | patches/server/1066-Fix-incorrect-invulnerability-damage-reduction.patch | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/patches/server/1066-Fix-incorrect-invulnerability-damage-reduction.patch b/patches/server/1066-Fix-incorrect-invulnerability-damage-reduction.patch new file mode 100644 index 0000000000..3b3dd3d144 --- /dev/null +++ b/patches/server/1066-Fix-incorrect-invulnerability-damage-reduction.patch @@ -0,0 +1,115 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Bjarne Koll <[email protected]> +Date: Mon, 11 Nov 2024 21:35:27 +0100 +Subject: [PATCH] Fix incorrect invulnerability damage reduction + +Fixes incorrect spigot handling of the invulnerability damage +reduction applied when an already invulnerable entity is damaged with a +larger damage amount than the initial damage. +Vanilla still damages entities even if invulnerable if the damage to be +applied is larger than the previous damage taken. In that case, vanilla +applies the difference between the previous damage taken and the +proposed damage. + +Spigot's damage modifier API takes over the computation of damage +reducing effects, however spigot invokes this handling with the initial +damage before computing the difference to the previous damage amount. +This leads to the reduction values to generally be larger than expected, +as they are computed on the not-yet-reduced value. +Spigot applies these reductions after calling the EntityDamageEvent and +*then* subtracts the previous damage point, leading to the final damage +amount being smaller than expected. + +This patch cannot simply call the EntityDamageEvent with the reduced +damage, as that would lead to EntityDamageEvent#getDamage() returning +the already reduced damage, which breaks its method contract. +Instead, this patch makes use of the DamageModifier API, implementing +the last-damage-reduction as a DamageModifier. + +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index 6a3a8f0466998409a01223bc0c16d92b96e50118..51f913a495e7fda7e0e72439c6d7cc9607bd4af8 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -1505,12 +1505,12 @@ public abstract class LivingEntity extends Entity implements Attackable { + } + + // Paper start - only call damage event when actuallyHurt will be called - move call logic down +- event = this.handleEntityDamage(source, amount); ++ event = this.handleEntityDamage(source, amount, this.lastHurt); // Paper - fix invulnerability reduction in EntityDamageEvent - pass lastDamage reduction + amount = computeAmountFromEntityDamageEvent(event); + // Paper end - only call damage event when actuallyHurt will be called - move call logic down + + // CraftBukkit start +- if (!this.actuallyHurt(world, source, (float) event.getFinalDamage() - this.lastHurt, event)) { ++ if (!this.actuallyHurt(world, source, (float) event.getFinalDamage(), event)) { // Paper - fix invulnerability reduction in EntityDamageEvent - no longer subtract lastHurt, that is part of the damage event calc now + return false; + } + if (this instanceof ServerPlayer && event.getDamage() == 0 && originalAmount == 0) return false; // Paper - revert to vanilla damage - players are not affected by damage that is 0 - skip damage if the vanilla damage is 0 and was not modified by plugins in the event. +@@ -1519,7 +1519,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + flag1 = false; + } else { + // Paper start - only call damage event when actuallyHurt will be called - move call logic down +- event = this.handleEntityDamage(source, amount); ++ event = this.handleEntityDamage(source, amount, 0); // Paper - fix invulnerability reduction in EntityDamageEvent - pass lastDamage reduction (none in this branch) + amount = computeAmountFromEntityDamageEvent(event); + // Paper end - only call damage event when actuallyHurt will be called - move call logic down + // CraftBukkit start +@@ -2322,8 +2322,19 @@ public abstract class LivingEntity extends Entity implements Attackable { + } + + // CraftBukkit start +- private EntityDamageEvent handleEntityDamage(final DamageSource damagesource, float f) { ++ private EntityDamageEvent handleEntityDamage(final DamageSource damagesource, float f, final float invulnerabilityRelatedLastDamage) { // Paper - fix invulnerability reduction in EntityDamageEvent + float originalDamage = f; ++ // Paper start - fix invulnerability reduction in EntityDamageEvent ++ final com.google.common.base.Function<Double, Double> invulnerabilityReductionEquation = d -> { ++ if (invulnerabilityRelatedLastDamage == 0) return 0D; // no last damage, no reduction ++ // last damage existed, this means the reduction *technically* is (new damage - last damage). ++ // If the event damage was changed to something less than invul damage, hard lock it at 0. ++ if (d < invulnerabilityRelatedLastDamage) return 0D; ++ return (double) -invulnerabilityRelatedLastDamage; ++ }; ++ final float originalInvulnerabilityReduction = invulnerabilityReductionEquation.apply((double) f).floatValue(); ++ f += originalInvulnerabilityReduction; ++ // Paper end - fix invulnerability reduction in EntityDamageEvent + + com.google.common.base.Function<Double, Double> freezing = new com.google.common.base.Function<Double, Double>() { + @Override +@@ -2400,7 +2411,12 @@ public abstract class LivingEntity extends Entity implements Attackable { + }; + float absorptionModifier = absorption.apply((double) f).floatValue(); + +- return CraftEventFactory.handleLivingEntityDamageEvent(this, damagesource, originalDamage, freezingModifier, hardHatModifier, blockingModifier, armorModifier, resistanceModifier, magicModifier, absorptionModifier, freezing, hardHat, blocking, armor, resistance, magic, absorption); ++ // Paper start - fix invulnerability reduction in EntityDamageEvent ++ return CraftEventFactory.handleLivingEntityDamageEvent(this, damagesource, originalDamage, freezingModifier, hardHatModifier, blockingModifier, armorModifier, resistanceModifier, magicModifier, absorptionModifier, freezing, hardHat, blocking, armor, resistance, magic, absorption, (damageModifierDoubleMap, damageModifierFunctionMap) -> { ++ damageModifierFunctionMap.put(DamageModifier.INVULNERABILITY_REDUCTION, invulnerabilityReductionEquation); ++ damageModifierDoubleMap.put(DamageModifier.INVULNERABILITY_REDUCTION, (double) originalInvulnerabilityReduction); ++ }); ++ // Paper end - fix invulnerability reduction in EntityDamageEvent + } + + protected boolean actuallyHurt(ServerLevel worldserver, final DamageSource damagesource, float f, final EntityDamageEvent event) { // void -> boolean, add final +diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +index deba03eb37012c638e08e20cd1c98e9db190c790..e37aaf77f94b97b736cc20ef070cefdff0400188 100644 +--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +@@ -1218,6 +1218,11 @@ public class CraftEventFactory { + private static final Function<? super Double, Double> ZERO = Functions.constant(-0.0); + + public static EntityDamageEvent handleLivingEntityDamageEvent(Entity damagee, DamageSource source, double rawDamage, double freezingModifier, double hardHatModifier, double blockingModifier, double armorModifier, double resistanceModifier, double magicModifier, double absorptionModifier, Function<Double, Double> freezing, Function<Double, Double> hardHat, Function<Double, Double> blocking, Function<Double, Double> armor, Function<Double, Double> resistance, Function<Double, Double> magic, Function<Double, Double> absorption) { ++ // Paper start - fix invulnerability reduction in EntityDamageEvent ++ return handleLivingEntityDamageEvent(damagee, source, rawDamage, freezingModifier, hardHatModifier, blockingModifier, armorModifier, resistanceModifier, magicModifier, absorptionModifier, freezing, hardHat, blocking, armor, resistance, magic, absorption, null); ++ } ++ public static EntityDamageEvent handleLivingEntityDamageEvent(Entity damagee, DamageSource source, double rawDamage, double freezingModifier, double hardHatModifier, double blockingModifier, double armorModifier, double resistanceModifier, double magicModifier, double absorptionModifier, Function<Double, Double> freezing, Function<Double, Double> hardHat, Function<Double, Double> blocking, Function<Double, Double> armor, Function<Double, Double> resistance, Function<Double, Double> magic, Function<Double, Double> absorption, java.util.function.BiConsumer<Map<DamageModifier, Double>, Map<DamageModifier, Function<? super Double, Double>>> callback) { ++ // Paper end - fix invulnerability reduction in EntityDamageEvent + Map<DamageModifier, Double> modifiers = new EnumMap<>(DamageModifier.class); + Map<DamageModifier, Function<? super Double, Double>> modifierFunctions = new EnumMap<>(DamageModifier.class); + modifiers.put(DamageModifier.BASE, rawDamage); +@@ -1242,6 +1247,7 @@ public class CraftEventFactory { + modifierFunctions.put(DamageModifier.MAGIC, magic); + modifiers.put(DamageModifier.ABSORPTION, absorptionModifier); + modifierFunctions.put(DamageModifier.ABSORPTION, absorption); ++ if (callback != null) callback.accept(modifiers, modifierFunctions); // Paper - fix invulnerability reduction in EntityDamageEvent + return CraftEventFactory.handleEntityDamageEvent(damagee, source, modifiers, modifierFunctions); + } + |