From 7fc77d4c0fd49e1fe6f25b21306d21d00739ea96 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Wed, 29 Oct 2025 10:14:17 +0100 Subject: [PATCH 1/2] Add EntityPushByExplodeEvent --- .../entity/EntityPushByExplodeEvent.java | 90 +++++++++++++++++++ .../0001-Moonrise-optimisation-patches.patch | 8 +- .../world/level/ServerExplosion.java.patch | 7 +- .../craftbukkit/event/CraftEventFactory.java | 7 ++ 4 files changed, 107 insertions(+), 5 deletions(-) create mode 100644 paper-api/src/main/java/org/bukkit/event/entity/EntityPushByExplodeEvent.java diff --git a/paper-api/src/main/java/org/bukkit/event/entity/EntityPushByExplodeEvent.java b/paper-api/src/main/java/org/bukkit/event/entity/EntityPushByExplodeEvent.java new file mode 100644 index 000000000000..5968f07b204a --- /dev/null +++ b/paper-api/src/main/java/org/bukkit/event/entity/EntityPushByExplodeEvent.java @@ -0,0 +1,90 @@ +package org.bukkit.event.entity; + +import com.google.common.base.Preconditions; +import org.bukkit.entity.Entity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +/** + * Called when an entity explodes interacting with not living entities. + */ +@NullMarked +public class EntityPushByExplodeEvent extends EntityEvent implements Cancellable { + + private static final HandlerList HANDLER_LIST = new HandlerList(); + + private final Entity pusher; + private final Entity pushee; + private Vector knockback; + + private boolean cancelled; + + @ApiStatus.Internal + public EntityPushByExplodeEvent(final Entity pusher, final Entity pushee, final Vector knockback) { + super(pusher); + this.pusher = pusher; + this.pushee = pushee; + this.knockback = knockback; + } + + /** + * The Entity that is responsible for pushing the other Entity. + * + * @return the Entity + */ + public Entity getPusher() { + return pusher; + } + + /** + * The Entity that is pushed by the explosion. + * + * @return the pushed Entity + */ + public Entity getPushee() { + return pushee; + } + + /** + * Gets the knockback force that will be applied to the entity.
+ * This value is read-only, changes made to it will not have any + * effect on the final knockback received. Use {@link #setKnockback(Vector)} + * to make changes. + * + * @return the knockback + */ + public Vector getKnockback() { + return this.knockback.clone(); + } + + /** + * Sets the knockback force that will be applied to the entity. + * + * @param knockback the knockback + */ + public void setKnockback(final Vector knockback) { + this.knockback = knockback.clone(); + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; + } + + @Override + public HandlerList getHandlers() { + return HANDLER_LIST; + } + + public static HandlerList getHandlerList() { + return HANDLER_LIST; + } +} diff --git a/paper-server/patches/features/0001-Moonrise-optimisation-patches.patch b/paper-server/patches/features/0001-Moonrise-optimisation-patches.patch index 1b852cf73de2..ed51da7a3224 100644 --- a/paper-server/patches/features/0001-Moonrise-optimisation-patches.patch +++ b/paper-server/patches/features/0001-Moonrise-optimisation-patches.patch @@ -30172,7 +30172,7 @@ index 93110c815abe7baef61b1eaacfe28ebb1fc821f7..564c5064ec105a4f59476f2d21d3664b ChunkAccess getChunk(int x, int z, ChunkStatus chunkStatus, boolean requireChunk); diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java -index c132a3bfff7cf020e7fe4be616daa763b83e4b25..1c521f9f32340cf75310686c90777e521ac3ae5c 100644 +index a36ec4bee10d445f31e98ea3eb035be469a24a84..374cd50b855ecb470ca76b8e85b66e03de6cb5c5 100644 --- a/net/minecraft/world/level/ServerExplosion.java +++ b/net/minecraft/world/level/ServerExplosion.java @@ -62,6 +62,249 @@ public class ServerExplosion implements Explosion { @@ -30575,7 +30575,7 @@ index c132a3bfff7cf020e7fe4be616daa763b83e4b25..1c521f9f32340cf75310686c90777e52 } private void hurtEntities() { -@@ -346,6 +628,14 @@ public class ServerExplosion implements Explosion { +@@ -351,6 +633,14 @@ public class ServerExplosion implements Explosion { } public int explode() { @@ -30590,7 +30590,7 @@ index c132a3bfff7cf020e7fe4be616daa763b83e4b25..1c521f9f32340cf75310686c90777e52 this.level.gameEvent(this.source, GameEvent.EXPLODE, this.center); List list = this.calculateExplodedPositions(); this.hurtEntities(); -@@ -360,6 +650,13 @@ public class ServerExplosion implements Explosion { +@@ -365,6 +655,13 @@ public class ServerExplosion implements Explosion { this.createFire(list); } @@ -30604,7 +30604,7 @@ index c132a3bfff7cf020e7fe4be616daa763b83e4b25..1c521f9f32340cf75310686c90777e52 return list.size(); } -@@ -449,12 +746,12 @@ public class ServerExplosion implements Explosion { +@@ -454,12 +751,12 @@ public class ServerExplosion implements Explosion { // Paper start - Optimize explosions private float getBlockDensity(Vec3 vec3d, Entity entity) { if (!this.level.paperConfig().environment.optimizeExplosions) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/ServerExplosion.java.patch b/paper-server/patches/sources/net/minecraft/world/level/ServerExplosion.java.patch index b58ac2ad2cdd..5fdffa250790 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/ServerExplosion.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/ServerExplosion.java.patch @@ -75,7 +75,7 @@ if (!entity.ignoreExplosion(this)) { double d = Math.sqrt(entity.distanceToSqr(this.center)) / f; if (!(d > 1.0)) { -@@ -185,18 +_,54 @@ +@@ -185,18 +_,59 @@ Vec3 vec31 = vec3.subtract(this.center).normalize(); boolean shouldDamageEntity = this.damageCalculator.shouldDamageEntity(this, entity); float knockbackMultiplier = this.damageCalculator.getKnockbackMultiplier(entity); @@ -124,6 +124,11 @@ + io.papermc.paper.event.entity.EntityKnockbackEvent event = CraftEventFactory.callEntityKnockbackEvent((org.bukkit.craftbukkit.entity.CraftLivingEntity) entity.getBukkitEntity(), this.source, this.damageSource.getEntity() != null ? this.damageSource.getEntity() : this.source, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.EXPLOSION, d2, vec32); + vec32 = event.isCancelled() ? Vec3.ZERO : org.bukkit.craftbukkit.util.CraftVector.toVec3(event.getKnockback()); + // Paper end - knockback events ++ } else { ++ // Paper start - push entity events ++ org.bukkit.event.entity.EntityPushByExplodeEvent event = CraftEventFactory.callEntityPushByExplodeEvent(this.damageSource.getEntity() != null ? this.damageSource.getEntity() : this.source, entity, vec32); ++ vec32 = event.isCancelled() ? Vec3.ZERO : org.bukkit.craftbukkit.util.CraftVector.toVec3(event.getKnockback()); ++ // Paper end - push entity events + } + // CraftBukkit end entity.push(vec32); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java index 00af7ce338ea..6190764f6ed7 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java @@ -185,6 +185,7 @@ import org.bukkit.event.entity.EntityPlaceEvent; import org.bukkit.event.entity.EntityPortalEvent; import org.bukkit.event.entity.EntityPotionEffectEvent; +import org.bukkit.event.entity.EntityPushByExplodeEvent; import org.bukkit.event.entity.EntityRemoveEvent; import org.bukkit.event.entity.EntityShootBowEvent; import org.bukkit.event.entity.EntitySpawnEvent; @@ -2043,6 +2044,12 @@ public static ExplosionPrimeEvent callExplosionPrimeEvent(Entity nmsEntity, floa return event; } + public static EntityPushByExplodeEvent callEntityPushByExplodeEvent(Entity pusher, Entity pushee, Vec3 knockback) { + EntityPushByExplodeEvent event = new EntityPushByExplodeEvent(pusher.getBukkitEntity(), pushee.getBukkitEntity(), CraftVector.toBukkit(knockback)); + Bukkit.getPluginManager().callEvent(event); + return event; + } + public static io.papermc.paper.event.entity.EntityKnockbackEvent callEntityKnockbackEvent(CraftLivingEntity entity, Entity pusher, Entity attacker, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause cause, double force, Vec3 knockback) { Vector apiKnockback = CraftVector.toBukkit(knockback); From 20ef3155f4bd8b3bc99a2047d9de6e2bc830aa56 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Mon, 3 Nov 2025 10:23:49 +0100 Subject: [PATCH 2/2] Remove EntityPushByExplodeEvent Update CraftEventFactory.callEntityKnockbackEvent --- .../entity/EntityPushByExplodeEvent.java | 90 ------------------- .../0001-Moonrise-optimisation-patches.patch | 8 +- .../world/entity/LivingEntity.java.patch | 2 +- .../world/level/ServerExplosion.java.patch | 17 ++-- .../craftbukkit/event/CraftEventFactory.java | 38 ++++---- 5 files changed, 29 insertions(+), 126 deletions(-) delete mode 100644 paper-api/src/main/java/org/bukkit/event/entity/EntityPushByExplodeEvent.java diff --git a/paper-api/src/main/java/org/bukkit/event/entity/EntityPushByExplodeEvent.java b/paper-api/src/main/java/org/bukkit/event/entity/EntityPushByExplodeEvent.java deleted file mode 100644 index 5968f07b204a..000000000000 --- a/paper-api/src/main/java/org/bukkit/event/entity/EntityPushByExplodeEvent.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.bukkit.event.entity; - -import com.google.common.base.Preconditions; -import org.bukkit.entity.Entity; -import org.bukkit.event.Cancellable; -import org.bukkit.event.HandlerList; -import org.bukkit.util.Vector; -import org.jetbrains.annotations.ApiStatus; -import org.jspecify.annotations.NullMarked; - -/** - * Called when an entity explodes interacting with not living entities. - */ -@NullMarked -public class EntityPushByExplodeEvent extends EntityEvent implements Cancellable { - - private static final HandlerList HANDLER_LIST = new HandlerList(); - - private final Entity pusher; - private final Entity pushee; - private Vector knockback; - - private boolean cancelled; - - @ApiStatus.Internal - public EntityPushByExplodeEvent(final Entity pusher, final Entity pushee, final Vector knockback) { - super(pusher); - this.pusher = pusher; - this.pushee = pushee; - this.knockback = knockback; - } - - /** - * The Entity that is responsible for pushing the other Entity. - * - * @return the Entity - */ - public Entity getPusher() { - return pusher; - } - - /** - * The Entity that is pushed by the explosion. - * - * @return the pushed Entity - */ - public Entity getPushee() { - return pushee; - } - - /** - * Gets the knockback force that will be applied to the entity.
- * This value is read-only, changes made to it will not have any - * effect on the final knockback received. Use {@link #setKnockback(Vector)} - * to make changes. - * - * @return the knockback - */ - public Vector getKnockback() { - return this.knockback.clone(); - } - - /** - * Sets the knockback force that will be applied to the entity. - * - * @param knockback the knockback - */ - public void setKnockback(final Vector knockback) { - this.knockback = knockback.clone(); - } - - @Override - public boolean isCancelled() { - return cancelled; - } - - @Override - public void setCancelled(boolean cancel) { - this.cancelled = cancel; - } - - @Override - public HandlerList getHandlers() { - return HANDLER_LIST; - } - - public static HandlerList getHandlerList() { - return HANDLER_LIST; - } -} diff --git a/paper-server/patches/features/0001-Moonrise-optimisation-patches.patch b/paper-server/patches/features/0001-Moonrise-optimisation-patches.patch index ed51da7a3224..2e11faee7e64 100644 --- a/paper-server/patches/features/0001-Moonrise-optimisation-patches.patch +++ b/paper-server/patches/features/0001-Moonrise-optimisation-patches.patch @@ -30172,7 +30172,7 @@ index 93110c815abe7baef61b1eaacfe28ebb1fc821f7..564c5064ec105a4f59476f2d21d3664b ChunkAccess getChunk(int x, int z, ChunkStatus chunkStatus, boolean requireChunk); diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java -index a36ec4bee10d445f31e98ea3eb035be469a24a84..374cd50b855ecb470ca76b8e85b66e03de6cb5c5 100644 +index 7a819ee03a1fc674cd17433ac03cbc59d466913c..05f4521084d5c93e166ea58bda04b7292916f2e9 100644 --- a/net/minecraft/world/level/ServerExplosion.java +++ b/net/minecraft/world/level/ServerExplosion.java @@ -62,6 +62,249 @@ public class ServerExplosion implements Explosion { @@ -30575,7 +30575,7 @@ index a36ec4bee10d445f31e98ea3eb035be469a24a84..374cd50b855ecb470ca76b8e85b66e03 } private void hurtEntities() { -@@ -351,6 +633,14 @@ public class ServerExplosion implements Explosion { +@@ -344,6 +626,14 @@ public class ServerExplosion implements Explosion { } public int explode() { @@ -30590,7 +30590,7 @@ index a36ec4bee10d445f31e98ea3eb035be469a24a84..374cd50b855ecb470ca76b8e85b66e03 this.level.gameEvent(this.source, GameEvent.EXPLODE, this.center); List list = this.calculateExplodedPositions(); this.hurtEntities(); -@@ -365,6 +655,13 @@ public class ServerExplosion implements Explosion { +@@ -358,6 +648,13 @@ public class ServerExplosion implements Explosion { this.createFire(list); } @@ -30604,7 +30604,7 @@ index a36ec4bee10d445f31e98ea3eb035be469a24a84..374cd50b855ecb470ca76b8e85b66e03 return list.size(); } -@@ -454,12 +751,12 @@ public class ServerExplosion implements Explosion { +@@ -447,12 +744,12 @@ public class ServerExplosion implements Explosion { // Paper start - Optimize explosions private float getBlockDensity(Vec3 vec3d, Entity entity) { if (!this.level.paperConfig().environment.optimizeExplosions) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch index 6065b2410d00..13e832d8e383 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch @@ -985,7 +985,7 @@ deltaMovement.z / 2.0 - vec3.z ); + Vec3 diff = finalVelocity.subtract(deltaMovement); -+ io.papermc.paper.event.entity.EntityKnockbackEvent event = CraftEventFactory.callEntityKnockbackEvent((org.bukkit.craftbukkit.entity.CraftLivingEntity) this.getBukkitEntity(), attacker, attacker, eventCause, strength, diff); ++ io.papermc.paper.event.entity.EntityKnockbackEvent event = CraftEventFactory.callEntityKnockbackEvent(this.getBukkitEntity(), attacker, attacker, eventCause, strength, diff); + // Paper end - knockback events + if (event.isCancelled()) { + return; diff --git a/paper-server/patches/sources/net/minecraft/world/level/ServerExplosion.java.patch b/paper-server/patches/sources/net/minecraft/world/level/ServerExplosion.java.patch index 5fdffa250790..6f93ff46cca8 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/ServerExplosion.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/ServerExplosion.java.patch @@ -75,7 +75,7 @@ if (!entity.ignoreExplosion(this)) { double d = Math.sqrt(entity.distanceToSqr(this.center)) / f; if (!(d > 1.0)) { -@@ -185,18 +_,59 @@ +@@ -185,18 +_,52 @@ Vec3 vec31 = vec3.subtract(this.center).normalize(); boolean shouldDamageEntity = this.damageCalculator.shouldDamageEntity(this, entity); float knockbackMultiplier = this.damageCalculator.getKnockbackMultiplier(entity); @@ -119,17 +119,10 @@ + double d2 = entity instanceof Player && this.level.paperConfig().environment.disableExplosionKnockback ? 0 : (1.0 - d) * f1 * knockbackMultiplier * (1.0 - d1); // Paper Vec3 vec32 = vec31.scale(d2); + // CraftBukkit start - Call EntityKnockbackEvent -+ if (entity instanceof LivingEntity) { -+ // Paper start - knockback events -+ io.papermc.paper.event.entity.EntityKnockbackEvent event = CraftEventFactory.callEntityKnockbackEvent((org.bukkit.craftbukkit.entity.CraftLivingEntity) entity.getBukkitEntity(), this.source, this.damageSource.getEntity() != null ? this.damageSource.getEntity() : this.source, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.EXPLOSION, d2, vec32); -+ vec32 = event.isCancelled() ? Vec3.ZERO : org.bukkit.craftbukkit.util.CraftVector.toVec3(event.getKnockback()); -+ // Paper end - knockback events -+ } else { -+ // Paper start - push entity events -+ org.bukkit.event.entity.EntityPushByExplodeEvent event = CraftEventFactory.callEntityPushByExplodeEvent(this.damageSource.getEntity() != null ? this.damageSource.getEntity() : this.source, entity, vec32); -+ vec32 = event.isCancelled() ? Vec3.ZERO : org.bukkit.craftbukkit.util.CraftVector.toVec3(event.getKnockback()); -+ // Paper end - push entity events -+ } ++ // Paper start - knockback events ++ io.papermc.paper.event.entity.EntityKnockbackEvent event = CraftEventFactory.callEntityKnockbackEvent(entity.getBukkitEntity(), this.source, this.damageSource.getEntity() != null ? this.damageSource.getEntity() : this.source, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.EXPLOSION, d2, vec32); ++ vec32 = event.isCancelled() ? Vec3.ZERO : org.bukkit.craftbukkit.util.CraftVector.toVec3(event.getKnockback()); ++ // Paper end - knockback events + // CraftBukkit end entity.push(vec32); if (entity.getType().is(EntityTypeTags.REDIRECTABLE_PROJECTILE) && entity instanceof Projectile projectile) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java index 6190764f6ed7..209101b1c3a0 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java @@ -185,7 +185,6 @@ import org.bukkit.event.entity.EntityPlaceEvent; import org.bukkit.event.entity.EntityPortalEvent; import org.bukkit.event.entity.EntityPotionEffectEvent; -import org.bukkit.event.entity.EntityPushByExplodeEvent; import org.bukkit.event.entity.EntityRemoveEvent; import org.bukkit.event.entity.EntityShootBowEvent; import org.bukkit.event.entity.EntitySpawnEvent; @@ -2044,30 +2043,31 @@ public static ExplosionPrimeEvent callExplosionPrimeEvent(Entity nmsEntity, floa return event; } - public static EntityPushByExplodeEvent callEntityPushByExplodeEvent(Entity pusher, Entity pushee, Vec3 knockback) { - EntityPushByExplodeEvent event = new EntityPushByExplodeEvent(pusher.getBukkitEntity(), pushee.getBukkitEntity(), CraftVector.toBukkit(knockback)); - Bukkit.getPluginManager().callEvent(event); - return event; - } - - public static io.papermc.paper.event.entity.EntityKnockbackEvent callEntityKnockbackEvent(CraftLivingEntity entity, Entity pusher, Entity attacker, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause cause, double force, Vec3 knockback) { + public static io.papermc.paper.event.entity.EntityKnockbackEvent callEntityKnockbackEvent(CraftEntity entity, Entity pusher, Entity attacker, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause cause, double force, Vec3 knockback) { Vector apiKnockback = CraftVector.toBukkit(knockback); - final Vector currentVelocity = entity.getVelocity(); - final Vector legacyFinalKnockback = currentVelocity.clone().add(apiKnockback); - final org.bukkit.event.entity.EntityKnockbackEvent.KnockbackCause legacyCause = org.bukkit.event.entity.EntityKnockbackEvent.KnockbackCause.valueOf(cause.name()); - EntityKnockbackEvent legacyEvent; - if (pusher != null) { - legacyEvent = new EntityKnockbackByEntityEvent(entity, pusher.getBukkitEntity(), legacyCause, force, apiKnockback, legacyFinalKnockback); - } else { - legacyEvent = new EntityKnockbackEvent(entity, legacyCause, force, apiKnockback, legacyFinalKnockback); + + EntityKnockbackEvent legacyEvent = null; + if (entity instanceof CraftLivingEntity) { + CraftLivingEntity livingEntity = (CraftLivingEntity) entity; + final Vector legacyFinalKnockback = currentVelocity.clone().add(apiKnockback); + final org.bukkit.event.entity.EntityKnockbackEvent.KnockbackCause legacyCause = org.bukkit.event.entity.EntityKnockbackEvent.KnockbackCause.valueOf(cause.name()); + if (pusher != null) { + legacyEvent = new EntityKnockbackByEntityEvent(livingEntity, pusher.getBukkitEntity(), legacyCause, force, apiKnockback, legacyFinalKnockback); + } else { + legacyEvent = new EntityKnockbackEvent(livingEntity, legacyCause, force, apiKnockback, legacyFinalKnockback); + } + legacyEvent.callEvent(); } - legacyEvent.callEvent(); final io.papermc.paper.event.entity.EntityKnockbackEvent event; - apiKnockback = legacyEvent.getFinalKnockback().subtract(currentVelocity); + apiKnockback = legacyEvent != null ? legacyEvent.getFinalKnockback().subtract(currentVelocity) : apiKnockback; if (attacker != null) { - event = new com.destroystokyo.paper.event.entity.EntityKnockbackByEntityEvent(entity, attacker.getBukkitEntity(), cause, (float) force, apiKnockback); + if (entity instanceof CraftLivingEntity) { + event = new com.destroystokyo.paper.event.entity.EntityKnockbackByEntityEvent((CraftLivingEntity) entity, attacker.getBukkitEntity(), cause, (float) force, apiKnockback); + } else { + event = new io.papermc.paper.event.entity.EntityPushedByEntityAttackEvent(entity, cause, pusher.getBukkitEntity(), apiKnockback); + } } else { event = new io.papermc.paper.event.entity.EntityKnockbackEvent(entity, cause, apiKnockback); }