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 / mod.rs

//! Port of `ChunkProviderGenerate.populate` and the `WorldGenerator` features it places.

mod big_tree;
mod dungeons;
mod lakes;
mod ores;
mod plants;
mod springs;
pub(crate) mod trees;

use common::JavaRandom;
use common::biome::Biome;
use common::world::{Block, ChunkPos, Material};

use crate::level::Level;
use crate::terrain::{Generator, OverworldGenerator};

mod nether;

/// `IChunkProvider.populate` of the level's dimension.
pub fn populate(level: &mut Level, generator: &Generator, pos: ChunkPos) {
    level.instant_falling = true;
    match generator {
        Generator::Overworld(generator) => populate_overworld(level, generator, pos),
        Generator::Nether(_) => nether::populate(level, pos),
    }
    level.instant_falling = false;
}

/// Decorates the 16x16 area offset by 8 blocks from the chunk origin, so features spill
/// into the three neighbors that must already be loaded.
fn populate_overworld(level: &mut Level, generator: &OverworldGenerator, pos: ChunkPos) {
    let (x0, z0) = (pos.min_block_x(), pos.min_block_z());
    let biome = generator.climate.sample(x0 + 16, z0 + 16, 1, 1).biomes[0];
    let seed = level.seed();
    let mut rand = JavaRandom::new(seed);
    let a = rand.next_long() / 2 * 2 + 1;
    let b = rand.next_long() / 2 * 2 + 1;
    rand.set_seed(
        i64::from(pos.x)
            .wrapping_mul(a)
            .wrapping_add(i64::from(pos.z).wrapping_mul(b))
            ^ seed,
    );
    let rand = &mut rand;
    let spread = |rand: &mut JavaRandom, base: i32| base + rand.next_int(16) + 8;
    let point = |rand: &mut JavaRandom| [spread(rand, x0), rand.next_int(128), spread(rand, z0)];
    let corner = |rand: &mut JavaRandom| {
        [
            x0 + rand.next_int(16),
            rand.next_int(128),
            z0 + rand.next_int(16),
        ]
    };

    if rand.next_int(4) == 0 {
        lakes::generate(level, point(rand), rand, Block::WATER);
    }
    if rand.next_int(8) == 0 {
        let x = spread(rand, x0);
        let bound = rand.next_int(120) + 8;
        let y = rand.next_int(bound);
        let z = spread(rand, z0);
        if y < 64 || rand.next_int(10) == 0 {
            lakes::generate(level, [x, y, z], rand, Block::LAVA);
        }
    }
    for _ in 0..8 {
        dungeons::generate(level, point(rand), rand);
    }
    for _ in 0..10 {
        ores::clay(level, corner(rand), rand, 32);
    }
    let veins: [(Block, i32, i32, i32); 8] = [
        (Block::DIRT, 32, 20, 128),
        (Block::GRAVEL, 32, 10, 128),
        (Block::COAL_ORE, 16, 20, 128),
        (Block::IRON_ORE, 8, 20, 64),
        (Block::GOLD_ORE, 8, 2, 32),
        (Block::REDSTONE_ORE, 7, 8, 16),
        (Block::DIAMOND_ORE, 7, 1, 16),
        (Block::LAPIS_ORE, 6, 1, 0),
    ];
    for (ore, size, count, max_y) in veins {
        for _ in 0..count {
            let x = x0 + rand.next_int(16);
            let y = if ore == Block::LAPIS_ORE {
                rand.next_int(16) + rand.next_int(16)
            } else {
                rand.next_int(max_y)
            };
            let z = z0 + rand.next_int(16);
            ores::vein(level, [x, y, z], rand, ore, size);
        }
    }

    let noise = generator
        .tree_noise
        .sample_2d(f64::from(x0) * 0.5, f64::from(z0) * 0.5);
    let density = ((noise / 8.0 + rand.next_double() * 4.0 + 4.0) / 3.0) as i32;
    let mut trees = i32::from(rand.next_int(10) == 0);
    trees += match biome {
        Biome::Forest | Biome::Rainforest | Biome::Taiga => density + 5,
        Biome::SeasonalForest => density + 2,
        Biome::Desert | Biome::Tundra | Biome::Plains => -20,
        _ => 0,
    };
    for _ in 0..trees {
        let (x, z) = (spread(rand, x0), spread(rand, z0));
        let tree = trees::Kind::pick(biome, rand);
        let y = level.height(x, z);
        tree.generate(level, [x, y, z], rand);
    }

    let dandelions = match biome {
        Biome::Forest | Biome::Taiga => 2,
        Biome::SeasonalForest => 4,
        Biome::Plains => 3,
        _ => 0,
    };
    for _ in 0..dandelions {
        plants::flowers(level, point(rand), rand, Block::DANDELION);
    }
    let grass = match biome {
        Biome::Forest | Biome::SeasonalForest => 2,
        Biome::Rainforest | Biome::Plains => 10,
        Biome::Taiga => 1,
        _ => 0,
    };
    for _ in 0..grass {
        let fern = biome == Biome::Rainforest && rand.next_int(3) != 0;
        plants::tall_grass(level, point(rand), rand, if fern { 2 } else { 1 });
    }
    let (dead_bushes, cacti) = if biome == Biome::Desert {
        (2, 10)
    } else {
        (0, 0)
    };
    for _ in 0..dead_bushes {
        plants::dead_bushes(level, point(rand), rand);
    }
    for (chance, plant) in [
        (2, Block::ROSE),
        (4, Block::BROWN_MUSHROOM),
        (8, Block::RED_MUSHROOM),
    ] {
        if rand.next_int(chance) == 0 {
            plants::flowers(level, point(rand), rand, plant);
        }
    }
    for _ in 0..10 {
        plants::sugar_cane(level, point(rand), rand);
    }
    if rand.next_int(32) == 0 {
        plants::pumpkins(level, point(rand), rand);
    }
    for _ in 0..cacti {
        plants::cacti(level, point(rand), rand);
    }
    for _ in 0..50 {
        let x = spread(rand, x0);
        let bound = rand.next_int(120) + 8;
        let y = rand.next_int(bound);
        let z = spread(rand, z0);
        springs::generate(level, [x, y, z], Block::FLOWING_WATER);
    }
    for _ in 0..20 {
        let x = spread(rand, x0);
        let inner = rand.next_int(112) + 8;
        let bound = rand.next_int(inner) + 8;
        let y = rand.next_int(bound);
        let z = spread(rand, z0);
        springs::generate(level, [x, y, z], Block::FLOWING_LAVA);
    }

    let temperatures = generator.climate.temperatures(x0 + 8, z0 + 8, 16, 16);
    for (dx, x) in (x0 + 8..x0 + 24).enumerate() {
        for (dz, z) in (z0 + 8..z0 + 24).enumerate() {
            let y = level.top_solid_or_liquid(x, z);
            let temperature = temperatures[dx * 16 + dz] - f64::from(y - 64) / 64.0 * 0.3;
            if temperature < 0.5
                && y > 0
                && y < 128
                && level.is_air(x, y, z)
                && level.material(x, y - 1, z).is_solid()
                && level.material(x, y - 1, z) != Material::Ice
            {
                level.set_block_notify(x, y, z, Block::SNOW_LAYER);
            }
        }
    }
}