alex/beta-minecraft-rust
Minecraft Beta 1.7.3 Rewrite
BRANCH main
SNAPSHOT Draw terrain GPU-driven from packed per-section buffers with GPU occlusion culling, add a render distance slider (2-128) with a quad/disk shape, cut chunk bookkeeping at high render distances, and fix a light underflow at y=0
DOWNLOAD
root crates server src entity explosion.rs
use common::entity::Entity;
use common::math::floor_f64;
use common::physics::Aabb;
use common::protocol::Packet;
use common::world::shape::{self, BlockView};
use common::world::{Block, ChunkPos};
use glam::DVec3;
use rustc_hash::FxHashSet;
use crate::level::Level;
fn density(level: &Level, origin: DVec3, bounds: &Aabb) -> f32 {
let step = DVec3::ONE / ((bounds.max - bounds.min) * 2.0 + DVec3::ONE);
let mut clear = 0;
let mut total = 0;
let mut x = 0.0_f32;
while x <= 1.0 {
let mut y = 0.0_f32;
while y <= 1.0 {
let mut z = 0.0_f32;
while z <= 1.0 {
let point = bounds.min
+ (bounds.max - bounds.min)
* DVec3::new(f64::from(x), f64::from(y), f64::from(z));
clear += i32::from(shape::ray_trace(level, point, origin, false, false).is_none());
total += 1;
z = (f64::from(z) + step.z) as f32;
}
y = (f64::from(y) + step.y) as f32;
}
x = (f64::from(x) + step.x) as f32;
}
clear as f32 / total as f32
}
impl Level {
/// `World.newExplosion`; a flaming one also sets fire to blocks within its reach.
/// The creeper that explodes is the attacker; TNT and fireballs have none.
pub fn explode(&mut self, origin: DVec3, size: f32, attacker: Option<&Entity>, flaming: bool) {
let source = attacker.map(|entity| entity.id);
let mut affected = FxHashSet::default();
for x in 0..16 {
for y in 0..16 {
for z in 0..16 {
if x != 0 && x != 15 && y != 0 && y != 15 && z != 0 && z != 15 {
continue;
}
let direction = DVec3::from_array(
[x, y, z].map(|v| f64::from(v as f32 / 15.0 * 2.0 - 1.0)),
)
.normalize();
let mut strength = size * (0.7 + self.rand.next_float() * 0.6);
let mut point = origin;
while strength > 0.0 {
let [x, y, z] = point.to_array().map(floor_f64);
// Outside the world's height there is only air
if !self.is_loaded(ChunkPos::containing(x, z)) {
break;
}
let block = BlockView::block(self, x, y, z);
if !block.is_air() {
strength -= (block.properties().map_or(0.0, |p| p.resistance / 5.0)
+ 0.3)
* 0.3;
}
if strength > 0.0 {
affected.insert([x, y, z]);
}
point += direction * f64::from(0.3_f32);
strength -= 0.3 * (12.0 / 16.0);
}
}
}
}
let radius = f64::from(size * 2.0);
let victims: Vec<_> = self
.entities
.iter()
.filter(|e| {
Some(e.entity.id) != source
&& e.entity.pos.distance_squared(origin) <= radius * radius
})
.map(|e| (e.entity.id, e.entity.pos, e.entity.bb))
.collect();
for (id, position, bounds) in victims {
let delta = position - origin;
let exposure =
(1.0 - delta.length() / radius) * f64::from(density(self, origin, &bounds));
self.attack_entity(
id,
attacker,
((exposure * exposure + exposure) / 2.0 * 8.0 * radius + 1.0) as i32,
);
if let Some(stored) = self.entities.get_mut(id) {
stored.entity.motion += delta.normalize_or_zero() * exposure;
self.events.push(super::velocity_packet(&stored.entity));
}
}
let mut affected: Vec<_> = affected.into_iter().collect();
affected.sort_unstable();
if flaming {
for &[x, y, z] in affected.iter().rev() {
if self.block(x, y, z).is_air()
&& self.block(x, y - 1, z).is_opaque_cube()
&& self.rand.next_int(3) == 0
{
self.set_block_notify(x, y, z, Block::FIRE);
}
}
}
let pitch = (1.0 + (self.rand.next_float() - self.rand.next_float()) * 0.2) * 0.7;
self.events.push(Packet::Sound {
name: "random.explode".into(),
pos: origin.to_array(),
volume: 4.0,
pitch,
});
for &[x, y, z] in &affected {
let point =
DVec3::from_array([x, y, z].map(|v| f64::from(v as f32 + self.rand.next_float())));
let delta = point - origin;
let speed = 0.5 / (delta.length() / f64::from(size) + 0.1)
* f64::from(self.rand.next_float() * self.rand.next_float() + 0.3);
let velocity = delta.normalize_or_zero() * speed;
self.events.push(Packet::Particle {
name: "explode".into(),
pos: ((point + origin) / 2.0).to_array(),
velocity: velocity.to_array(),
});
self.events.push(Packet::Particle {
name: "smoke".into(),
pos: point.to_array(),
velocity: velocity.to_array(),
});
let block = BlockView::block(self, x, y, z);
if block.is_air() {
continue;
}
let metadata = BlockView::metadata(self, x, y, z);
if block != Block::TNT {
crate::blocks::drop_with_chance(self, block, [x, y, z], metadata, 0.3);
}
self.set_block_notify(x, y, z, Block::AIR);
if block == Block::TNT {
self.prime_tnt(DVec3::from_array([x, y, z].map(f64::from)) + 0.5, true);
}
}
self.events.push(Packet::Explosion {
x: origin.x,
y: origin.y,
z: origin.z,
size,
blocks: affected
.into_iter()
.map(|p| std::array::from_fn(|i| (p[i] - origin[i] as i32) as i8))
.collect(),
});
}
}