ExplosionControl
Complete, per-source control over every explosion type in Minecraft, for Paper 1.21.11 / Java 21.
Author: zSouul
What it does
ExplosionControl gives server administrators independent control over 15 explosion
categories, each with the same five options in config.yml:
| Option | Type | Effect |
|---|---|---|
enabled |
true/false |
Master on/off switch — prevents the explosion as early as possible. |
damage-multiplier |
≥ 0.0 |
Scales the damage dealt to any single living entity (1.0 = vanilla, 0.0 = no damage). Applied to raw damage before armor, so armor/resistance/absorption still reduce it normally. |
radius-multiplier |
≥ 0.0 |
Scales the blast radius/power (1.0 = vanilla). |
knockback-multiplier |
≥ 0.0 |
Scales the knockback velocity applied to caught entities. |
block-damage |
true/false |
Whether the explosion is allowed to destroy/drop blocks. |
Categories: tnt, tnt-minecart, creeper, charged-creeper, wither-spawn,
wither-skull, ghast-fireball, fireball, blaze-fireball, dragon-fireball,
end-crystal, respawn-anchor, bed, wind-charge, and a catch-all other.
Reload changes at any time with:
/explosioncontrol reload
which requires the explosioncontrol.reload permission (default: op).
How each explosion type is handled
Every explosion source in the game is hooked at the earliest point the Paper API exposes for
that source, so enabled: false prevents as much of the explosion (damage, knockback, and
block damage) as technically possible — not just the block destruction.
| Category | Primary hook(s) | Notes |
|---|---|---|
tnt |
ExplosionPrimeEvent + EntityExplodeEvent |
Standard entity-explosion pipeline. |
tnt-minecart |
ExplosionPrimeEvent + EntityExplodeEvent |
Same pipeline; entity type ExplosiveMinecart. |
creeper |
ExplosionPrimeEvent + EntityExplodeEvent |
Creeper#isPowered() == false. |
charged-creeper |
ExplosionPrimeEvent + EntityExplodeEvent |
Creeper#isPowered() == true. radius-multiplier is applied on top of vanilla's own charged-creeper radius doubling. |
wither-spawn |
ExplosionPrimeEvent + EntityExplodeEvent |
The single explosion fired the moment a Wither finishes spawning; source entity is the Wither itself. |
wither-skull |
ExplosionPrimeEvent + EntityExplodeEvent |
Covers both the regular and blue/"charged" skull variants. |
ghast-fireball |
ExplosionPrimeEvent + EntityExplodeEvent |
LargeFireball whose getShooter() is a Ghast. |
fireball |
ExplosionPrimeEvent + EntityExplodeEvent |
Any other LargeFireball (e.g. dispenser-fired). |
blaze-fireball |
ExplosionPrimeEvent + EntityExplodeEvent |
See limitation below — inert on unmodified vanilla. |
dragon-fireball |
EnderDragonFireballHitEvent + EntityDamageEvent(DRAGON_BREATH) |
See limitation below — different mechanic entirely. |
end-crystal |
ExplosionPrimeEvent + EntityExplodeEvent |
Entity type EnderCrystal. |
wind-charge |
ExplosionPrimeEvent + EntityExplodeEvent |
Covers both thrown/dispensed WindCharge and Breeze-fired BreezeWindCharge (shared AbstractWindCharge supertype). |
respawn-anchor |
PlayerInteractEvent (pre-explosion) + BlockExplodeEvent |
See limitation below. |
bed |
PlayerBedFailEnterEvent (pre-explosion) + BlockExplodeEvent |
Fully preventable — see below. |
other |
Every hook above, as a fallback branch | Catches any future/unrecognised explosion source so nothing is ever left unconfigurable. |
damage-multiplier is enforced for every category above through a single shared
EntityDamageEvent listener (for ENTITY_EXPLOSION/BLOCK_EXPLOSION causes, plus
DRAGON_BREATH for the Dragon Fireball special case), which resolves the responsible
category via DamageSource#getCausingEntity() for entity sources, falls back to
ExplosionOriginRegistry (a short-lived cache populated when the explosion was primed, for
the rare case the source entity is already gone), and finally to reading the block still
present at DamageSource#getDamageLocation() for block sources (Bed/Respawn Anchor).
knockback-multiplier is enforced through EntityKnockbackEvent
(KnockbackCause.EXPLOSION). Since that event does not expose which explosion caused it, the
category resolved a moment earlier by the damage listener (for the same victim) is reused —
see PendingKnockbackCache for the full explanation of why and how this is safe.
Paper API limitations encountered, and how they were addressed
Bed and Respawn Anchor explosions apply entity damage/knockback before Bukkit fires
BlockExplodeEvent. Unlike entity-sourced explosions (which fire the cancellableExplosionPrimeEventbefore anything happens), Bukkit only exposesBlockExplodeEventfor these two sources — after damage and knockback have already been applied. CancellingBlockExplodeEventalone would therefore only stop block destruction, notenabled: falseas a whole.- Bed — addressed with the dedicated, cancellable Paper event
PlayerBedFailEnterEvent(willExplode() == true), which fires before vanilla starts the explosion. Cancelling it fully prevents the explosion (damage, knockback, and block damage). This is a complete fix, not a workaround. - Respawn Anchor — Paper does not expose an equivalent dedicated event. The closest
available alternative is intercepting the triggering
PlayerInteractEvent(a right-click on a charged anchor outside the Nether, without glowstone in hand) and cancelling it, which replicates vanilla's own trigger condition. This covers the normal player-driven case completely, but — unlike Bed — cannot guarantee prevention if some other means forces the anchor to explode outside that interaction.BlockExplodeListenerstill runs as a second line of defence for block damage in that edge case.
- Bed — addressed with the dedicated, cancellable Paper event
EntityKnockbackEventdoes not expose which entity or block caused the knockback — only a coarseKnockbackCause.EXPLOSIONvalue, with no accessor for the exploding entity/block. Addressed by correlating it with theEntityDamageEventfired for the same victim immediately beforehand (vanilla always damages-then-knocks-back each affected entity before moving to the next one), via a small short-lived cache keyed by entity UUID (PendingKnockbackCache). If no matching entry is found (e.g. another plugin cancelled the damage event first), vanilla knockback is left untouched rather than guessed at.Blaze Fireball (
blaze-fireball) does not actually explode in vanilla Minecraft. A Blaze's small fireball only ignites its target; there is no explosion for the Paper API to expose here at all. This section is still fully wired up (identical hooks to every other entity-sourced explosion) so that if a data pack, mod, or future Minecraft version ever makes small fireballs explosive, it is instantly configurable with no further plugin changes — it simply has nothing to do on an unmodified vanilla/Paper server today.Dragon Fireball (
dragon-fireball) is not a block-destroying explosion at all. On impact it spawns a lingeringAreaEffectCloud("dragon's breath") that dealsDRAGON_BREATH-cause damage over time; it never firesExplosionPrimeEventorEntityExplodeEvent, and has no knockback component. Addressed with a dedicated listener using the closest honest analogues available:enabledcancelsEnderDragonFireballHitEventoutright (no cloud ever appears),damage-multiplierscales each tick ofDRAGON_BREATHdamage, andradius-multiplierscales the resulting cloud's radius.block-damageandknockback-multiplierare intentionally ignored for this category and documented as such inconfig.yml, since vanilla dragon fireballs never destroy blocks or apply knockback in the first place — there's nothing for either option to meaningfully control.repo.papermc.ioand Maven Central are unreachable from the sandbox this project was authored in, so a compiled jar could not be produced or linked against the genuinepaper-apiartifact in that environment (see the note under Building above for exactly how correctness was still verified without it).
Permissions
| Permission | Default | Description |
|---|---|---|
explosioncontrol.reload |
op |
Allows running /explosioncontrol reload. |
Commands
| Command | Description |
|---|---|
/explosioncontrol reload |
Reloads config.yml without restarting the server. |

