Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ExplodeEvent and Improvement in Spigot Explode events #11840

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package io.papermc.paper.event.world;

import org.bukkit.ExplosionResult;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Entity;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.util.List;

/**
* Called when an explodes happen. this includes explosion who not affect blocks and are just visual.
*/
@ApiStatus.Experimental
@NullMarked
public class ExplodeEvent extends Event implements Cancellable {

private static final HandlerList HANDLER_LIST = new HandlerList();

private final Location location;
private final List<Block> blocks;
private final ExplosionResult result;
private @Nullable BlockState blockState;
private @Nullable Entity entity;
private float yield;
private boolean cancelled;

@ApiStatus.Internal
public ExplodeEvent(final Location location, final List<Block> blocks, final float yield, final ExplosionResult result) {
this.location = location;
this.blocks = blocks;
this.yield = yield;
this.result = result;
this.cancelled = false;
}

@ApiStatus.Internal
public ExplodeEvent(final Entity entityExploded, final Location location, final List<Block> blocks, final float yield, final ExplosionResult result) {
this(location, blocks, yield, result);
this.entity = entityExploded;
}

@ApiStatus.Internal
public ExplodeEvent(final BlockState blockStateExploded, final Location location, final List<Block> blocks, final float yield, final ExplosionResult result) {
this(location, blocks, yield, result);
this.blockState = blockStateExploded;
}

/**
* Returns the result of the explosion if it is not cancelled.
*
* @return the result of the explosion
*/
public ExplosionResult getExplosionResult() {
return result;
}

/**
* Returns the location where the explosion happened.
* <p>
* It is not possible to get this value from the Entity as the Entity no
* longer exists in the world.
*
* @return The location of the explosion
*/
public Location getLocation() {
return this.location.clone();
}

/**
* Returns the entity that exploded, if exists.
*
* @return the entity, if is the source of explosion. {@code null} if it has not.
*/
@Nullable
public Entity getEntity() {
return this.entity;
}

/**
* Returns the captured BlockState of the block that exploded.
*
* @return the block state, if is the source of explosion. {@code null} if it has not.
*/
@Nullable
public BlockState getBlockState() {
return this.blockState;
}

/**
* Returns the list of blocks that would have been caught in the explosion.
* <br>
* <b>Note:</b> the behaviours with these blocks depends on {@link #getExplosionResult()}
*
* @return All blocks caught in the explosion
*/
public List<Block> blockList() {
Doc94 marked this conversation as resolved.
Show resolved Hide resolved
return this.blocks;
}

/**
* Returns the percentage of blocks to drop from this explosion.
* <br>
* <b>Note:</b> this behaviour depends on {@link #getExplosionResult()}
*
* @return The yield.
*/
public float getYield() {
return this.yield;
}

/**
* Sets the percentage of blocks to drop from this explosion
*
* @param yield The new yield percentage
*
* @see #getYield()
*/
public void setYield(float yield) {
this.yield = yield;
}

@Override
public boolean isCancelled() {
return this.cancelled;
}

@Override
public void setCancelled(final boolean cancel) {
this.cancelled = cancel;
}

@Override
public HandlerList getHandlers() {
return HANDLER_LIST;
}

public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
}
8 changes: 4 additions & 4 deletions paper-api/src/main/java/org/bukkit/ExplosionResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ public enum ExplosionResult {

/**
* Represents an explosion where no change took place.
*
* <br>
* This is the case when {@link org.bukkit.GameRule#MOB_GRIEFING} is
* disabled.
* disabled or the {@link org.bukkit.enchantments.Enchantment#WIND_BURST}.
*/
KEEP,
/**
* Represents an explosion where all destroyed blocks drop their items.
*
* <br>
* This is the case when
* {@link org.bukkit.GameRule#TNT_EXPLOSION_DROP_DECAY} or
* {@link org.bukkit.GameRule#BLOCK_EXPLOSION_DROP_DECAY} is disabled.
Expand All @@ -29,7 +29,7 @@ public enum ExplosionResult {
DESTROY_WITH_DECAY,
/**
* Represents an explosion where a block change/update has happened.
*
* <br>
* For example, when a wind charge is used it will cause nearby buttons,
* levers and bells to be activated.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.bukkit.block.BlockState;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;

/**
Expand All @@ -17,7 +18,9 @@
* <p>
* The event isn't called if the {@link org.bukkit.GameRule#MOB_GRIEFING}
* is disabled as no block interaction will occur.
* @see io.papermc.paper.event.world.ExplodeEvent
*/
@ApiStatus.Obsolete
public class BlockExplodeEvent extends BlockEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private boolean cancel;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
import org.bukkit.entity.Entity;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;

/**
* Called when an entity explodes interacting with blocks. The
* event isn't called if the {@link org.bukkit.GameRule#MOB_GRIEFING}
* is disabled as no block interaction will occur.
* @see io.papermc.paper.event.world.ExplodeEvent
*/
@ApiStatus.Obsolete
public class EntityExplodeEvent extends EntityEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private boolean cancel;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23552,7 +23552,7 @@ index 2b46ca9a2a046063cad422bec00d76107537b091..9aa664537cc37e44db46d5a2a64ae311
thread1 -> {
DedicatedServer dedicatedServer1 = new DedicatedServer(
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index b92a3da5c325e69f5601416d4205fb33429742b3..d967d605c2e4227ae980c30f1c8b86edbc680d6d 100644
index 244e2eae7e52c3f92d085a7cb7f961df1a38168f..acc669108e5efa0c742820676be5d4bba9b27027 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -173,7 +173,7 @@ import net.minecraft.world.phys.Vec2;
Expand Down Expand Up @@ -27496,7 +27496,7 @@ index 192977dd661ee795ada13db895db770293e9b402..95a4e37a3c93f9b3c56c7a7376ed521c
}

diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 097ec55166b9e9269142be58992c29687122fe28..aeabb79512aabd7a9e8af1be72e1745f0e7eefe4 100644
index 472693a7d10dc67b116db02e3e4472adbc5a4d62..422db52e8a0a08350542670bfc9ba94ad9481d0c 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -178,7 +178,7 @@ import net.minecraft.world.scores.Team;
Expand Down Expand Up @@ -30419,10 +30419,10 @@ index 2709803b9266ff4a2034d83321cd0ba4e30fc0aa..26c8c1e5598daf3550aef05b12218c47
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 3619509d51ebd2e5e36fe4b67e76c94a8d272d1b..7b132c55caf9d3c3df3b0a123f4b5bfc7ae35984 100644
index 377439ab6c0d122415abdd2094f3f2afeb974426..a5f561d8a6a732c6f95acf38f4e14a7f67398934 100644
--- a/net/minecraft/world/level/ServerExplosion.java
+++ b/net/minecraft/world/level/ServerExplosion.java
@@ -63,6 +63,249 @@ public class ServerExplosion implements Explosion {
@@ -59,6 +59,249 @@ public class ServerExplosion implements Explosion {
public float yield;
// CraftBukkit end
public boolean excludeSourceFromDamage = true; // Paper - Allow explosions to damage source
Expand Down Expand Up @@ -30672,7 +30672,7 @@ index 3619509d51ebd2e5e36fe4b67e76c94a8d272d1b..7b132c55caf9d3c3df3b0a123f4b5bfc

public ServerExplosion(
ServerLevel level,
@@ -134,63 +377,102 @@ public class ServerExplosion implements Explosion {
@@ -130,63 +373,102 @@ public class ServerExplosion implements Explosion {
}

private List<BlockPos> calculateExplodedPositions() {
Expand Down Expand Up @@ -30822,7 +30822,7 @@ index 3619509d51ebd2e5e36fe4b67e76c94a8d272d1b..7b132c55caf9d3c3df3b0a123f4b5bfc
}

private void hurtEntities() {
@@ -371,6 +653,14 @@ public class ServerExplosion implements Explosion {
@@ -344,6 +626,14 @@ public class ServerExplosion implements Explosion {
return;
}
// CraftBukkit end
Expand All @@ -30837,7 +30837,7 @@ index 3619509d51ebd2e5e36fe4b67e76c94a8d272d1b..7b132c55caf9d3c3df3b0a123f4b5bfc
this.level.gameEvent(this.source, GameEvent.EXPLODE, this.center);
List<BlockPos> list = this.calculateExplodedPositions();
this.hurtEntities();
@@ -384,6 +674,13 @@ public class ServerExplosion implements Explosion {
@@ -367,6 +657,13 @@ public class ServerExplosion implements Explosion {
if (this.fire) {
this.createFire(list);
}
Expand All @@ -30851,7 +30851,7 @@ index 3619509d51ebd2e5e36fe4b67e76c94a8d272d1b..7b132c55caf9d3c3df3b0a123f4b5bfc
}

private static void addOrAppendStack(List<ServerExplosion.StackCollector> stackCollectors, ItemStack stack, BlockPos pos) {
@@ -474,12 +771,12 @@ public class ServerExplosion implements Explosion {
@@ -457,12 +754,12 @@ public class ServerExplosion implements Explosion {
// Paper start - Optimize explosions
private float getBlockDensity(Vec3 vec3d, Entity entity) {
if (!this.level.paperConfig().environment.optimizeExplosions) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--- a/net/minecraft/world/item/enchantment/effects/ExplodeEffect.java
+++ b/net/minecraft/world/item/enchantment/effects/ExplodeEffect.java
@@ -80,7 +_,7 @@
@Nullable
private DamageSource getDamageSource(Entity entity, Vec3 pos) {
if (this.damageType.isEmpty()) {
- return null;
+ return entity.level().damageSources().explosion(null).customEventDamager(entity); // Paper - copy null DamageSource behaviour from ServerExplosion::new to set causing entity
} else {
return this.attributeToUser ? new DamageSource(this.damageType.get(), entity) : new DamageSource(this.damageType.get(), pos);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
--- a/net/minecraft/world/level/ServerExplosion.java
+++ b/net/minecraft/world/level/ServerExplosion.java
@@ -33,6 +_,17 @@
@@ -33,6 +_,13 @@
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;

Expand All @@ -9,10 +9,6 @@
+import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
+import net.minecraft.world.level.block.Blocks;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.craftbukkit.util.CraftLocation;
+import org.bukkit.event.entity.EntityExplodeEvent;
+import org.bukkit.Location;
+import org.bukkit.event.block.BlockExplodeEvent;
+// CraftBukkit end
+
public class ServerExplosion implements Explosion {
Expand Down Expand Up @@ -149,45 +145,22 @@
this.hitPlayers.put(player, vec3);
}
}
@@ -225,7 +_,61 @@
@@ -225,7 +_,38 @@
List<ServerExplosion.StackCollector> list = new ArrayList<>();
Util.shuffle(blocks, this.level.random);

+ // CraftBukkit start
+ org.bukkit.World bworld = this.level.getWorld();
+ Location location = CraftLocation.toBukkit(this.center, bworld);
+
+ List<org.bukkit.block.Block> blockList = new ObjectArrayList<>();
+ for (int i1 = blocks.size() - 1; i1 >= 0; i1--) {
+ BlockPos cpos = blocks.get(i1);
+ org.bukkit.block.Block bblock = bworld.getBlockAt(cpos.getX(), cpos.getY(), cpos.getZ());
+ if (!bblock.getType().isAir()) {
+ blockList.add(bblock);
+ }
+ }
+
+ List<org.bukkit.block.Block> bukkitBlocks;
+
+ if (this.source != null) {
+ EntityExplodeEvent event = CraftEventFactory.callEntityExplodeEvent(this.source, blockList, this.yield, this.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 = (this.damageSource.getDirectBlockState() != null) ? this.damageSource.getDirectBlockState() : block.getState();
+ BlockExplodeEvent event = CraftEventFactory.callBlockExplodeEvent(block, blockState, blockList, this.yield, this.getBlockInteraction());
+ this.wasCanceled = event.isCancelled();
+ bukkitBlocks = event.blockList();
+ this.yield = event.getYield();
+ }
+
+ List<BlockPos> bukkitBlocks = CraftEventFactory.handleExplodeEvent(this, blocks);
+ blocks.clear();
+ blocks.addAll(bukkitBlocks);
+
+ for (org.bukkit.block.Block bblock : bukkitBlocks) {
+ BlockPos coords = new BlockPos(bblock.getX(), bblock.getY(), bblock.getZ());
+ blocks.add(coords);
+ }
+ // Paper start - call Explode Event
+ io.papermc.paper.event.world.ExplodeEvent explodeEvent = CraftEventFactory.callExplodeEvent(this, blocks);
+ this.wasCanceled = explodeEvent.isCancelled();
+ this.yield = explodeEvent.getYield();
+ blocks.clear();
+ blocks.addAll(explodeEvent.blockList().stream().map(bblock -> new BlockPos(bblock.getX(), bblock.getY(), bblock.getZ())).toList());
+ // Paper end
+
+ if (this.wasCanceled) {
+ return;
Expand Down Expand Up @@ -234,6 +207,23 @@
this.level.gameEvent(this.source, GameEvent.EXPLODE, this.center);
List<BlockPos> list = this.calculateExplodedPositions();
this.hurtEntities();
@@ -254,6 +_,16 @@
this.interactWithBlocks(list);
profilerFiller.pop();
}
+ // Paper start - handle not interacts in explosion events
+ else {
+ Util.shuffle(list, this.level.random); // Paper - Copy from calculateExplodedPositions
+ io.papermc.paper.event.world.ExplodeEvent explodeEvent = CraftEventFactory.callExplodeEvent(this, list);
+ this.wasCanceled = explodeEvent.isCancelled();
+ this.yield = explodeEvent.getYield();
+ list.clear();
+ list.addAll(explodeEvent.blockList().stream().map(bblock -> new BlockPos(bblock.getX(), bblock.getY(), bblock.getZ())).toList());
+ }
+ // Paper end

if (this.fire) {
this.createFire(list);
@@ -261,6 +_,7 @@
}

Expand Down
Loading
Loading