Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
import org.bukkit.event.Event;
import org.bukkit.event.Event.Result;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent;
Expand Down Expand Up @@ -129,6 +130,7 @@
import org.bukkit.event.entity.EntityKnockbackByEntityEvent;
import org.bukkit.event.entity.EntityPickupItemEvent;
import org.bukkit.event.entity.EntityTameEvent;
import org.bukkit.event.entity.EntityTargetEvent;
import org.bukkit.event.entity.EntityUnleashEvent;
import org.bukkit.event.entity.ExpBottleEvent;
import org.bukkit.event.entity.LingeringPotionSplashEvent;
Expand Down Expand Up @@ -399,6 +401,19 @@ private static <T extends EntityEvent & Cancellable> void handleKnockback(T eve
Events.fireToCancel(event, eventToFire);
}

@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onEntityTarget(EntityTargetEvent event) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be in WorldGuardEntityListener, EventAbstractionListener isn't the right place for this one as it's not calling out to an internal event.

if (!(event.getEntity() instanceof Creeper creeper)) {
return;
}

if (event.getTarget() instanceof Player player) {
Cause.trackParentCause(creeper, player);
} else if (event.getReason() != EntityTargetEvent.TargetReason.TARGET_DIED) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit concerned about the potential side effects this could have. The PR is made from the assumption that a player death is directly caused by the creeper itself, and therefore likely to trigger further block explosions afterwards. In most cases though, the creeper is likely to not actually die afterwards and therefore potentially forever have a stale reference around to a player.

Would potentially solving this in ExplosionPrimeEvent rather than the entity target event be a narrower approach? It still has the potential to leak a player if the explosion never occurs, but it at least is adding it at a more causally-linked location rather than for every random target action

@Almost10 Almost10 Nov 1, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, the target event was too broad. I looked into this and found why it broke: on 26.x, Creeper#getTarget() returns null once the target is dead, so the player is already gone by the time the explode event runs.

I'm thinking of doing this:

  • In ExplosionPrimeEvent: If the creeper's target is a player, track them as the parent cause.

  • In EntityRemoveEvent: Clear the stored player reference. The creeper is removed right after it explodes, and this event also covers death, despawn, and chunk unloads—ensuring the player is only stored for that single explosion. (I'd rather avoid clearing in EntityExplodeEvent, since that never fires for creepers when mobGriefing is set to false.)

  • Handler location: Both handlers would go in WorldGuardEntityListener.

Does that sound good, or would you rather avoid metadata entirely (e.g., with a weak-key map in Cause)?

Cause.untrackParentCause(creeper);
}
}

@SuppressWarnings("UnstableApiUsage")
@EventHandler(ignoreCancelled = true)
public void onEntityExplode(EntityExplodeEvent event) {
Expand Down