From b6655d093f116933ce07e2131bfb0df1947e1f63 Mon Sep 17 00:00:00 2001 From: CraftBukkit/Spigot Date: Sat, 6 Jul 2024 17:19:45 +1000 Subject: [PATCH] SPIGOT-7799, #1436: Expose explosion world interaction in EntityExplodeEvent and BlockExplodeEvent By: antiPerson --- .../boss/enderdragon/EntityEnderDragon.patch | 35 +++++++++---------- .../net/minecraft/world/level/Explosion.patch | 15 ++++---- .../craftbukkit/CraftExplosionResult.java | 27 ++++++++++++++ .../craftbukkit/event/CraftEventFactory.java | 15 ++++++++ .../java/org/bukkit/ExplosionResultTest.java | 23 ++++++++++++ 5 files changed, 88 insertions(+), 27 deletions(-) create mode 100644 paper-server/src/main/java/org/bukkit/craftbukkit/CraftExplosionResult.java create mode 100644 paper-server/src/test/java/org/bukkit/ExplosionResultTest.java diff --git a/paper-server/nms-patches/net/minecraft/world/entity/boss/enderdragon/EntityEnderDragon.patch b/paper-server/nms-patches/net/minecraft/world/entity/boss/enderdragon/EntityEnderDragon.patch index 8d68b5bffd..2d60d6a8c6 100644 --- a/paper-server/nms-patches/net/minecraft/world/entity/boss/enderdragon/EntityEnderDragon.patch +++ b/paper-server/nms-patches/net/minecraft/world/entity/boss/enderdragon/EntityEnderDragon.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/boss/enderdragon/EntityEnderDragon.java +++ b/net/minecraft/world/entity/boss/enderdragon/EntityEnderDragon.java -@@ -52,6 +52,20 @@ +@@ -52,6 +52,21 @@ import net.minecraft.world.phys.Vec3D; import org.slf4j.Logger; @@ -16,12 +16,13 @@ +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.EntityRegainHealthEvent; +import org.bukkit.event.entity.EntityRemoveEvent; ++import org.bukkit.craftbukkit.event.CraftEventFactory; +// CraftBukkit end + public class EntityEnderDragon extends EntityInsentient implements IMonster { private static final Logger LOGGER = LogUtils.getLogger(); -@@ -89,6 +103,7 @@ +@@ -89,6 +104,7 @@ private final PathPoint[] nodes; private final int[] nodeAdjacency; private final Path openSet; @@ -29,7 +30,7 @@ public EntityEnderDragon(EntityTypes entitytypes, World world) { super(EntityTypes.ENDER_DRAGON, world); -@@ -110,6 +125,7 @@ +@@ -110,6 +126,7 @@ this.noPhysics = true; this.noCulling = true; this.phaseManager = new DragonControllerManager(this); @@ -37,7 +38,7 @@ } public void setDragonFight(EnderDragonBattle enderdragonbattle) { -@@ -257,7 +273,7 @@ +@@ -257,7 +274,7 @@ Vec3D vec3d1 = idragoncontroller.getFlyTargetLocation(); @@ -46,7 +47,7 @@ double d0 = vec3d1.x - this.getX(); double d1 = vec3d1.y - this.getY(); double d2 = vec3d1.z - this.getZ(); -@@ -404,7 +420,14 @@ +@@ -404,7 +421,14 @@ if (this.nearestCrystal.isRemoved()) { this.nearestCrystal = null; } else if (this.tickCount % 10 == 0 && this.getHealth() < this.getMaxHealth()) { @@ -62,7 +63,7 @@ } } -@@ -489,6 +512,9 @@ +@@ -489,6 +513,9 @@ int j1 = MathHelper.floor(axisalignedbb.maxZ); boolean flag = false; boolean flag1 = false; @@ -72,7 +73,7 @@ for (int k1 = i; k1 <= l; ++k1) { for (int l1 = j; l1 <= i1; ++l1) { -@@ -498,7 +524,11 @@ +@@ -498,7 +525,11 @@ if (!iblockdata.isAir() && !iblockdata.is(TagsBlock.DRAGON_TRANSPARENT)) { if (this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && !iblockdata.is(TagsBlock.DRAGON_IMMUNE)) { @@ -85,7 +86,7 @@ } else { flag = true; } -@@ -507,6 +537,51 @@ +@@ -507,6 +538,49 @@ } } @@ -95,9 +96,7 @@ + return flag; + } + -+ org.bukkit.entity.Entity bukkitEntity = this.getBukkitEntity(); -+ EntityExplodeEvent event = new EntityExplodeEvent(bukkitEntity, bukkitEntity.getLocation(), destroyedBlocks, 0F); -+ bukkitEntity.getServer().getPluginManager().callEvent(event); ++ EntityExplodeEvent event = CraftEventFactory.callEntityExplodeEvent(this, destroyedBlocks, 0F, explosionSource.getBlockInteraction()); + if (event.isCancelled()) { + // This flag literally means 'Dragon hit something hard' (Obsidian, White Stone or Bedrock) and will cause the dragon to slow down. + // We should consider adding an event extension for it, or perhaps returning true if the event is cancelled. @@ -137,7 +136,7 @@ if (flag1) { BlockPosition blockposition1 = new BlockPosition(i + this.random.nextInt(l - i + 1), j + this.random.nextInt(i1 - j + 1), k + this.random.nextInt(j1 - k + 1)); -@@ -562,7 +637,7 @@ +@@ -562,7 +636,7 @@ @Override public void kill() { @@ -146,7 +145,7 @@ this.gameEvent(GameEvent.ENTITY_DIE); if (this.dragonFight != null) { this.dragonFight.updateDragon(this); -@@ -571,6 +646,21 @@ +@@ -571,6 +645,21 @@ } @@ -168,7 +167,7 @@ @Override protected void tickDeath() { if (this.dragonFight != null) { -@@ -586,15 +676,20 @@ +@@ -586,15 +675,20 @@ this.level().addParticle(Particles.EXPLOSION_EMITTER, this.getX() + (double) f, this.getY() + 2.0D + (double) f1, this.getZ() + (double) f2, 0.0D, 0.0D, 0.0D); } @@ -190,7 +189,7 @@ EntityExperienceOrb.award((WorldServer) this.level(), this.position(), MathHelper.floor((float) short0 * 0.08F)); } -@@ -605,7 +700,7 @@ +@@ -605,7 +699,7 @@ this.move(EnumMoveType.SELF, new Vec3D(0.0D, 0.10000000149011612D, 0.0D)); if (this.dragonDeathTime == 200 && this.level() instanceof WorldServer) { @@ -199,7 +198,7 @@ EntityExperienceOrb.award((WorldServer) this.level(), this.position(), MathHelper.floor((float) short0 * 0.2F)); } -@@ -613,7 +708,7 @@ +@@ -613,7 +707,7 @@ this.dragonFight.setDragonKilled(this); } @@ -208,7 +207,7 @@ this.gameEvent(GameEvent.ENTITY_DIE); } -@@ -826,6 +921,7 @@ +@@ -826,6 +920,7 @@ super.addAdditionalSaveData(nbttagcompound); nbttagcompound.putInt("DragonPhase", this.phaseManager.getCurrentPhase().getPhase().getId()); nbttagcompound.putInt("DragonDeathTime", this.dragonDeathTime); @@ -216,7 +215,7 @@ } @Override -@@ -839,6 +935,11 @@ +@@ -839,6 +934,11 @@ this.dragonDeathTime = nbttagcompound.getInt("DragonDeathTime"); } diff --git a/paper-server/nms-patches/net/minecraft/world/level/Explosion.patch b/paper-server/nms-patches/net/minecraft/world/level/Explosion.patch index 8d191c26b2..76c609ba3f 100644 --- a/paper-server/nms-patches/net/minecraft/world/level/Explosion.patch +++ b/paper-server/nms-patches/net/minecraft/world/level/Explosion.patch @@ -112,13 +112,12 @@ entity.setDeltaMovement(entity.getDeltaMovement().add(vec3d1)); if (entity instanceof EntityHuman) { EntityHuman entityhuman = (EntityHuman) entity; -@@ -291,9 +350,65 @@ +@@ -291,9 +350,62 @@ SystemUtils.shuffle(this.toBlow, this.level.random); ObjectListIterator objectlistiterator = this.toBlow.iterator(); + // CraftBukkit start + org.bukkit.World bworld = this.level.getWorld(); -+ org.bukkit.entity.Entity explode = this.source == null ? null : this.source.getBukkitEntity(); + Location location = new Location(bworld, this.x, this.y, this.z); + + List blockList = new ObjectArrayList<>(); @@ -132,17 +131,15 @@ + + List bukkitBlocks; + -+ if (explode != null) { -+ EntityExplodeEvent event = new EntityExplodeEvent(explode, location, blockList, this.yield); -+ this.level.getCraftServer().getPluginManager().callEvent(event); ++ if (this.source != null) { ++ EntityExplodeEvent event = CraftEventFactory.callEntityExplodeEvent(this.source, blockList, this.yield, getBlockInteraction()); + this.wasCanceled = event.isCancelled(); + bukkitBlocks = event.blockList(); + this.yield = event.getYield(); + } else { + org.bukkit.block.Block block = location.getBlock(); + org.bukkit.block.BlockState blockState = (damageSource.getDirectBlockState() != null) ? damageSource.getDirectBlockState() : block.getState(); -+ BlockExplodeEvent event = new BlockExplodeEvent(block, blockState, blockList, this.yield); -+ this.level.getCraftServer().getPluginManager().callEvent(event); ++ BlockExplodeEvent event = CraftEventFactory.callBlockExplodeEvent(block, blockState, blockList, this.yield, getBlockInteraction()); + this.wasCanceled = event.isCancelled(); + bukkitBlocks = event.blockList(); + this.yield = event.getYield(); @@ -178,7 +175,7 @@ this.level.getBlockState(blockposition).onExplosionHit(this.level, blockposition, this, (itemstack, blockposition1) -> { addOrAppendStack(list, itemstack, blockposition1); -@@ -318,7 +433,11 @@ +@@ -318,7 +430,11 @@ BlockPosition blockposition1 = (BlockPosition) objectlistiterator1.next(); if (this.random.nextInt(3) == 0 && this.level.getBlockState(blockposition1).isAir() && this.level.getBlockState(blockposition1.below()).isSolidRender(this.level, blockposition1.below())) { @@ -191,7 +188,7 @@ } } } -@@ -326,6 +445,7 @@ +@@ -326,6 +442,7 @@ } private static void addOrAppendStack(List> list, ItemStack itemstack, BlockPosition blockposition) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftExplosionResult.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftExplosionResult.java new file mode 100644 index 0000000000..bbbb2f1223 --- /dev/null +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftExplosionResult.java @@ -0,0 +1,27 @@ +package org.bukkit.craftbukkit; + +import com.google.common.base.Preconditions; +import net.minecraft.world.level.Explosion; +import org.bukkit.ExplosionResult; + +public final class CraftExplosionResult { + + private CraftExplosionResult() {} + + public static ExplosionResult toBukkit(Explosion.Effect effect) { + Preconditions.checkArgument(effect != null, "explosion effect cannot be null"); + + switch (effect) { + case KEEP: + return ExplosionResult.KEEP; + case DESTROY: + return ExplosionResult.DESTROY; + case DESTROY_WITH_DECAY: + return ExplosionResult.DESTROY_WITH_DECAY; + case TRIGGER_BLOCK: + return ExplosionResult.TRIGGER_BLOCK; + default: + throw new IllegalArgumentException("There is no ExplosionResult which matches " + effect); + } + } +} 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 f69ff48eb4..3b29eaade2 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 @@ -86,6 +86,7 @@ import org.bukkit.block.Sign; import org.bukkit.block.sign.Side; import org.bukkit.craftbukkit.CraftChunk; import org.bukkit.craftbukkit.CraftEquipmentSlot; +import org.bukkit.craftbukkit.CraftExplosionResult; import org.bukkit.craftbukkit.CraftLootTable; import org.bukkit.craftbukkit.CraftRaid; import org.bukkit.craftbukkit.CraftServer; @@ -143,6 +144,7 @@ import org.bukkit.event.block.BellRingEvent; import org.bukkit.event.block.BlockDamageAbortEvent; import org.bukkit.event.block.BlockDamageEvent; import org.bukkit.event.block.BlockDropItemEvent; +import org.bukkit.event.block.BlockExplodeEvent; import org.bukkit.event.block.BlockFadeEvent; import org.bukkit.event.block.BlockFormEvent; import org.bukkit.event.block.BlockGrowEvent; @@ -176,6 +178,7 @@ import org.bukkit.event.entity.EntityDamageEvent.DamageModifier; import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.entity.EntityEnterLoveModeEvent; import org.bukkit.event.entity.EntityExhaustionEvent; +import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.entity.EntityInteractEvent; import org.bukkit.event.entity.EntityKnockbackByEntityEvent; import org.bukkit.event.entity.EntityKnockbackEvent; @@ -1835,6 +1838,18 @@ public class CraftEventFactory { return !event.isCancelled(); } + public static EntityExplodeEvent callEntityExplodeEvent(Entity entity, List blocks, float yield, Explosion.Effect effect) { + EntityExplodeEvent event = new EntityExplodeEvent(entity.getBukkitEntity(), entity.getBukkitEntity().getLocation(), blocks, yield, CraftExplosionResult.toBukkit(effect)); + Bukkit.getPluginManager().callEvent(event); + return event; + } + + public static BlockExplodeEvent callBlockExplodeEvent(Block block, BlockState state, List blocks, float yield, Explosion.Effect effect) { + BlockExplodeEvent event = new BlockExplodeEvent(block, state, blocks, yield, CraftExplosionResult.toBukkit(effect)); + Bukkit.getPluginManager().callEvent(event); + return event; + } + public static ExplosionPrimeEvent callExplosionPrimeEvent(Explosive explosive) { ExplosionPrimeEvent event = new ExplosionPrimeEvent(explosive); Bukkit.getPluginManager().callEvent(event); diff --git a/paper-server/src/test/java/org/bukkit/ExplosionResultTest.java b/paper-server/src/test/java/org/bukkit/ExplosionResultTest.java new file mode 100644 index 0000000000..3e8c7ffb0a --- /dev/null +++ b/paper-server/src/test/java/org/bukkit/ExplosionResultTest.java @@ -0,0 +1,23 @@ +package org.bukkit; + +import static org.junit.jupiter.api.Assertions.*; +import net.minecraft.world.level.Explosion; +import org.bukkit.craftbukkit.CraftExplosionResult; +import org.junit.jupiter.api.Test; + +public class ExplosionResultTest { + + @Test + public void testMatchingEnum() { + for (ExplosionResult result : ExplosionResult.values()) { + assertNotNull(Explosion.Effect.valueOf(result.name()), "No NMS enum for Bukkit result " + result); + } + } + + @Test + public void testToBukkit() { + for (Explosion.Effect effect : Explosion.Effect.values()) { + assertNotNull(CraftExplosionResult.toBukkit(effect), "No Bukkit enum for NMS explosion effect " + effect); + } + } +}