Theme

alex/beta-minecraft-rust

Minecraft Beta 1.7.3 Rewrite

BRANCH main
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
Download file

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 &center 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;
    }
}