fix(entity): let explosions push TNT (#2941)

`TNTEntity` claimed to be immune to explosions, and `damage_entities` drops
immune entities before it gets anywhere near the knockback, so a chained TNT
never got the outward kick. Vanilla's TNT entity does not override
`ignoreExplosion`. The TNT case for the knockback origin in `damage_entities`
was unreachable until now.

`tick` also snapshotted velocity before moving and wrote back from that stale
value, so a push landing during the awaits was thrown away. Entities tick
concurrently, so this happened often enough to matter. It reads the velocity
back after `move_entity` now, like vanilla.
This commit is contained in:
Maksas Gornostajus
2026-08-20 09:49:13 +03:00
committed by GitHub
parent ba58547485
commit 0844e92911

View File

@@ -41,13 +41,17 @@ impl EntityBase for TNTEntity {
) -> EntityBaseFuture<'a, ()> {
Box::pin(async move {
let entity = &self.entity;
let original_velo = entity.velocity.load();
let mut velo = original_velo;
let mut velo = entity.velocity.load();
velo.y -= self.get_gravity();
entity.move_entity(caller, velo).await;
entity.tick_block_collisions(caller, server).await;
// Read back what actually happened instead of reusing the pre-move
// value: `move_entity` clamps on collision, and an explosion may have
// pushed us while we were awaiting above
let velo = entity.velocity.load();
if entity.on_ground.load(Ordering::Relaxed) {
entity.velocity.store(velo.multiply(0.7, -0.5, 0.7));
} else {
@@ -120,8 +124,4 @@ impl EntityBase for TNTEntity {
fn cast_any(&self) -> &dyn std::any::Any {
self
}
fn is_immune_to_explosion(&self) -> bool {
true
}
}