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 level random_ticks.rs
use common::world::light::LightKind;
use common::world::shape::BlockView;
use common::world::{Block, ChunkPos, sky};
use glam::DVec3;
use super::Level;
impl Level {
/// `World.skylightSubtracted`: how much night and weather darken the sky light.
pub fn sky_darkness(&self) -> u32 {
sky::sky_darkness(
sky::celestial_angle(self.time, 1.0),
self.weather.rain_strength(1.0),
self.weather.thunder_strength(1.0),
)
}
/// `World.isDaytime`.
pub fn is_daytime(&self) -> bool {
self.sky_darkness() < 4
}
/// `World.getBlockLightValue`: the light with the current daylight, which slabs, stairs and
/// farmland take from their brightest neighbor.
pub fn daylight(&self, x: i32, y: i32, z: i32) -> u8 {
self.daylight_with(x, y, z, self.sky_darkness() as u8)
}
/// [`Self::daylight`] for a given sky darkness, as thunderstorms pretend for monsters.
pub fn daylight_with(&self, x: i32, y: i32, z: i32, darkness: u8) -> u8 {
if matches!(
BlockView::block(self, x, y, z),
Block::SLAB | Block::FARMLAND | Block::COBBLESTONE_STAIRS | Block::OAK_STAIRS
) {
[(0, 1, 0), (1, 0, 0), (-1, 0, 0), (0, 0, 1), (0, 0, -1)]
.into_iter()
.map(|(dx, dy, dz)| self.cell_daylight(x + dx, y + dy, z + dz, darkness))
.max()
.unwrap_or(0)
} else {
self.cell_daylight(x, y, z, darkness)
}
}
fn cell_daylight(&self, x: i32, y: i32, z: i32, darkness: u8) -> u8 {
if !super::in_bounds(x, z) {
return 15;
}
if y < 0 {
return 0;
}
let y = y.min(127);
self.saved_light(LightKind::Sky, x, y, z)
.saturating_sub(darkness)
.max(self.saved_light(LightKind::Block, x, y, z))
}
/// A random block of the chunk, picked by `World.distHashCounter`.
fn next_update_pos(&mut self, pos: ChunkPos) -> (i32, i32, i32) {
self.update_lcg = self.update_lcg.wrapping_mul(3).wrapping_add(1_013_904_223);
let bits = self.update_lcg >> 2;
let x = pos.min_block_x() + (bits & 15);
(x, bits >> 16 & 127, pos.min_block_z() + (bits >> 8 & 15))
}
/// The cave sound of `doRandomUpdateTicks`, in a dark empty spot near a player.
fn cave_ambience(&mut self, pos: ChunkPos) {
let (x, y, z) = self.next_update_pos(pos);
let empty = BlockView::block(self, x, y, z).is_air();
if empty
&& i32::from(self.full_light(x, y, z)) <= self.rand.next_int(8)
&& self.saved_light(LightKind::Sky, x, y, z) == 0
{
let center = DVec3::new(f64::from(x), f64::from(y), f64::from(z)) + 0.5;
if self
.nearest_player(center, 8.0)
.is_some_and(|player| player.pos.distance_squared(center) > 4.0)
{
let pitch = 0.8 + self.rand.next_float() * 0.2;
self.events.push(common::protocol::Packet::Sound {
name: "ambient.cave.cave".into(),
pos: center.to_array(),
volume: 0.7,
pitch,
});
self.ambient_countdown = self.rand.next_int(12_000) + 6_000;
}
}
}
/// The snow and ice of `doRandomUpdateTicks` in snowy biomes away from light.
fn freeze(&mut self, x: i32, z: i32) {
let y = self.top_solid_or_liquid(x, z);
if !self.biome(x, z).snows()
|| !(0..128).contains(&y)
|| self.saved_light(LightKind::Block, x, y, z) >= 10
{
return;
}
let below = self.block(x, y - 1, z);
let top = self.block(x, y, z);
if self.weather.is_raining()
&& top.is_air()
&& crate::blocks::snow_fits(self, x, y, z)
&& !below.is_air()
&& below != Block::ICE
&& below.material().is_solid()
{
self.set_block_notify(x, y, z, Block::SNOW_LAYER);
}
if below == Block::WATER && self.metadata(x, y - 1, z) == 0 {
self.set_block_notify(x, y - 1, z, Block::ICE);
}
}
pub fn tick_random_blocks(&mut self, players: &[ChunkPos]) {
let mut positions = std::mem::take(&mut self.random_chunks);
for ¢er in players {
positions.extend((-9..=9).flat_map(|dx| (-9..=9).map(move |dz| center.offset(dx, dz))));
}
positions.sort_unstable_by_key(|pos| (pos.x, pos.z));
positions.dedup();
if self.ambient_countdown > 0 {
self.ambient_countdown -= 1;
}
for &pos in &positions {
self.load_chunk(pos);
if self.ambient_countdown == 0 {
self.cave_ambience(pos);
}
if self.rand.next_int(100_000) == 0
&& self.weather.is_raining()
&& self.weather.is_thundering()
{
let (x, _, z) = self.next_update_pos(pos);
let y = self.top_solid_or_liquid(x, z);
if self.rained_on(x, y, z) {
self.strike_lightning(DVec3::new(f64::from(x), f64::from(y), f64::from(z)));
self.last_lightning = 2;
}
}
if self.rand.next_int(16) == 0 {
let (x, _, z) = self.next_update_pos(pos);
self.freeze(x, z);
}
for _ in 0..80 {
let (x, y, z) = self.next_update_pos(pos);
let (lx, ly, lz) = super::local(x, y, z);
let block = self.chunks[&pos].block(lx, ly, lz);
crate::blocks::random_tick(self, block, x, y, z);
}
}
positions.clear();
self.random_chunks = positions;
}
}