Leave dropped items alone when a mob's blast keeps blocks

ItemEntity.ignoreExplosion asks ServerExplosion.shouldAffectBlocklikeEntities, so with
mobGriefing off a blast that keeps blocks (a creeper's or a ghast's) never touches a
dropped item. Horizon hurt and pushed every item inside the blast whatever the rule.
This commit is contained in:
MSedgeMC committed 2026-11-01 18:33:10 +00:00
1 parent 455b6916b4
commit 37fc39e48a
1 file changed
+29
@@ -100,6 +100,15 @@ pub(super) fn blast_reaches(center: [f64; 3], diameter: f64, at: [f64; 3]) -> bo
d / diameter <= 1.0
}
/// `ItemEntity.ignoreExplosion` through
/// `ServerExplosion.shouldAffectBlocklikeEntities`: an explosion that does not
/// affect blocklike entities is ignored entirely by a dropped item. With
/// `mobGriefing` off that is every blast that keeps blocks; with it on, none
/// (no wind charge goes through `server_explode`).
fn items_ignore_explosion(mob_griefing: bool, interaction: BlockInteraction) -> bool {
!mob_griefing && interaction == BlockInteraction::Keep
}
/// Whether a blast leaves item frames, paintings, cushions and armour stands
/// alone. Without `mobGriefing`, a blast that keeps blocks is ignored
/// (`ServerExplosion.shouldAffectBlocklikeEntities` through their
@@ -593,9 +602,15 @@ impl Game<'_> {
}
self.mobs = mobs;
// Dropped items: 5 health, destroyed by the explosion damage (nether stars resist).
// `ItemEntity.ignoreExplosion` asks `shouldAffectBlocklikeEntities`
// first, so a blast that keeps blocks leaves every item untouched.
let items_ignore = items_ignore_explosion(self.ui.rules.bool("mob_griefing"), interaction);
let before = self.dropped_ids();
let mut items = std::mem::take(&mut self.dropped);
items.retain_mut(|item| {
if items_ignore {
return true;
}
let feet = [item.position.x, item.position.y, item.position.z];
let dist = distance(feet) / diameter;
if dist > 1.0 {
@@ -1168,6 +1183,20 @@ fn add_or_append_stack(
mod tests {
use super::*;
/// A dropped item survives a mob's blast untouched while `mobGriefing` is
/// off, and is thrown and hurt as usual by one that destroys blocks or by
/// any blast with the rule back on.
#[test]
fn items_ignore_a_blast_that_keeps_blocks_with_griefing_off() {
assert!(items_ignore_explosion(false, BlockInteraction::Keep));
assert!(!items_ignore_explosion(false, BlockInteraction::Destroy));
assert!(!items_ignore_explosion(
false,
BlockInteraction::DestroyWithDecay
));
assert!(!items_ignore_explosion(true, BlockInteraction::Keep));
}
#[test]
fn blasts_reach_entities_within_their_diameter() {
// A TNT blast: radius 4, so the entity pass reaches 8 blocks out.