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 / populate / lakes.rs

//! Port of `WorldGenLakes`.

use std::ops::Range;

use common::JavaRandom;
use common::world::{Block, Material};

use crate::level::{Level, LightKind};

const fn slot(x: usize, z: usize, y: usize) -> usize {
    (x * 16 + z) * 8 + y
}

/// The cells of the lake's box in the original loop order: x, then z, then y.
fn cells(xz: Range<usize>, y: Range<usize>) -> impl Iterator<Item = (usize, usize, usize)> {
    let z = xz.clone();
    xz.flat_map(move |i| {
        let y = y.clone();
        z.clone()
            .flat_map(move |j| y.clone().map(move |k| (i, j, k)))
    })
}

/// A 16x8x16 blob of overlapping ellipsoids: liquid in the lower half, air above.
pub fn generate(level: &mut Level, [x, y, z]: [i32; 3], rand: &mut JavaRandom, liquid: Block) {
    let (x, z) = (x - 8, z - 8);
    let mut y = y;
    while y > 0 && level.is_air(x, y, z) {
        y -= 1;
    }
    y -= 4;

    let mut shape = [false; 2048];
    for _ in 0..rand.next_int(4) + 4 {
        let size_x = rand.next_double() * 6.0 + 3.0;
        let size_y = rand.next_double() * 4.0 + 2.0;
        let size_z = rand.next_double() * 6.0 + 3.0;
        let center_x = rand.next_double() * (16.0 - size_x - 2.0) + 1.0 + size_x / 2.0;
        let center_y = rand.next_double() * (8.0 - size_y - 4.0) + 2.0 + size_y / 2.0;
        let center_z = rand.next_double() * (16.0 - size_z - 2.0) + 1.0 + size_z / 2.0;
        for (i, j, k) in cells(1..15, 1..7) {
            let dx = (i as f64 - center_x) / (size_x / 2.0);
            let dy = (k as f64 - center_y) / (size_y / 2.0);
            let dz = (j as f64 - center_z) / (size_z / 2.0);
            if dx * dx + dy * dy + dz * dz < 1.0 {
                shape[slot(i, j, k)] = true;
            }
        }
    }

    let border = |i: usize, j: usize, k: usize| {
        !shape[slot(i, j, k)]
            && (i < 15 && shape[slot(i + 1, j, k)]
                || i > 0 && shape[slot(i - 1, j, k)]
                || j < 15 && shape[slot(i, j + 1, k)]
                || j > 0 && shape[slot(i, j - 1, k)]
                || k < 7 && shape[slot(i, j, k + 1)]
                || k > 0 && shape[slot(i, j, k - 1)])
    };
    let at = |i: usize, j: usize, k: usize| (x + i as i32, y + k as i32, z + j as i32);

    for (i, j, k) in cells(0..16, 0..8).filter(|&(i, j, k)| border(i, j, k)) {
        let (bx, by, bz) = at(i, j, k);
        let material = level.material(bx, by, bz);
        if k >= 4 && material.is_liquid() {
            return;
        }
        if k < 4 && !material.is_solid() && level.block(bx, by, bz) != liquid {
            return;
        }
    }

    for (i, j, k) in cells(0..16, 0..8).filter(|&(i, j, k)| shape[slot(i, j, k)]) {
        let (bx, by, bz) = at(i, j, k);
        level.set_block(bx, by, bz, if k >= 4 { Block::AIR } else { liquid });
    }

    for (i, j, k) in cells(0..16, 4..8).filter(|&(i, j, k)| shape[slot(i, j, k)]) {
        let (bx, by, bz) = at(i, j, k);
        if level.block(bx, by - 1, bz) == Block::DIRT
            && level.saved_light(LightKind::Sky, bx, by, bz) > 0
        {
            level.set_block(bx, by - 1, bz, Block::GRASS);
        }
    }

    if liquid.material() == Material::Lava {
        for (i, j, k) in cells(0..16, 0..8).filter(|&(i, j, k)| border(i, j, k)) {
            let (bx, by, bz) = at(i, j, k);
            if (k < 4 || rand.next_int(2) != 0) && level.material(bx, by, bz).is_solid() {
                level.set_block(bx, by, bz, Block::STONE);
            }
        }
    }
}