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 / examples / debug_world.rs

//! Writes a debug world into the client's saves folder: a flat platform at spawn with double
//! chests holding a full stack of every item and block variant.
//!
//! `cargo run --release -p server --example debug_world -- [--game-dir DIR] [--seed SEED]`
//!
//! The game directory defaults to the client's `~/.minecraft`.

use common::item::{Item, ItemStack};
use common::world::chunk;
use common::world::{Block, CHEST_SIZE, Chunk, ChunkPos, TileEntity};
use server::save::{WorldInfo, WorldSave};
use std::path::PathBuf;

const NAME: &str = "Debug World";
const GROUND: usize = 63;
/// The platform covers these chunks on both axes.
const CHUNKS: std::ops::RangeInclusive<i32> = -2..=1;

fn variants(item: Item) -> i32 {
    match item.block() {
        Some(Block::WOOL) => 16,
        Some(Block::LOG | Block::LEAVES | Block::SAPLING | Block::TALL_GRASS) => 3,
        Some(Block::SLAB) => 4,
        _ if item == Item::DYE => 16,
        _ if item == Item::COAL => 2,
        _ => 1,
    }
}

fn stacks() -> Vec<ItemStack> {
    (1..=i16::MAX)
        .map(Item)
        .filter_map(|item| item.properties().map(|p| (item, p.max_stack)))
        .flat_map(|(item, max)| {
            (0..variants(item)).map(move |damage| ItemStack::new(item, i32::from(max), damage))
        })
        .collect()
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut game_dir = std::env::var_os("HOME")
        .map_or_else(|| PathBuf::from("."), PathBuf::from)
        .join(".minecraft");
    let mut seed = 0;
    let mut args = std::env::args().skip(1);
    while let Some(arg) = args.next() {
        let mut value = || args.next().ok_or(format!("missing value for {arg}"));
        match arg.as_str() {
            "--game-dir" => game_dir = PathBuf::from(value()?),
            "--seed" => seed = value()?.parse()?,
            _ => return Err("usage: debug_world [--game-dir DIR] [--seed SEED]".into()),
        }
    }
    // `GuiCreateWorld`: a taken folder name gets dashes appended
    let mut dir = game_dir.join("saves").join(NAME);
    while dir.exists() {
        dir.as_mut_os_string().push("-");
    }

    // Double chests along x at z = 0, 2, 4, ..., one block apart
    let stacks = stacks();
    let chests: Vec<_> = stacks
        .chunks(CHEST_SIZE)
        .zip(0..)
        .map(|(items, i)| {
            let (pair, half) = (i / 2, i % 2);
            ([(pair % 5) * 3 + half - 7, (pair / 5) * 2 - 6], items)
        })
        .collect();

    let save = WorldSave::open(&dir)?;
    for cx in CHUNKS {
        for cz in CHUNKS {
            let pos = ChunkPos::new(cx, cz);
            let mut chunk = Chunk::new(pos, chunk::empty_blocks());
            chunk.populated = true;
            for x in 0..16 {
                for z in 0..16 {
                    chunk.swap_block(x, 0, z, Block::BEDROCK);
                    for y in 1..GROUND - 3 {
                        chunk.swap_block(x, y, z, Block::STONE);
                    }
                    for y in GROUND - 3..GROUND {
                        chunk.swap_block(x, y, z, Block::DIRT);
                    }
                    chunk.swap_block(x, GROUND, z, Block::GRASS);
                }
            }
            for &([x, z], items) in &chests {
                if ChunkPos::containing(x, z) != pos {
                    continue;
                }
                let (x, y, z) = ((x & 15) as usize, GROUND + 1, (z & 15) as usize);
                chunk.swap_block(x, y, z, Block::CHEST);
                let slots = Box::new(std::array::from_fn(|i| items.get(i).copied()));
                chunk
                    .tile_entities
                    .insert((x << 11 | z << 7 | y) as u16, TileEntity::Chest(slots));
            }
            chunk.generate_sky_light();
            save.overworld.save(&chunk, &[], 0)?;
        }
    }
    save.overworld.flush()?;

    let mut info = WorldInfo::new(seed, NAME);
    info.spawn = [0, GROUND as i32 + 1, 0];
    save.save_info(&mut info)?;
    println!("Wrote {} chests to {}", chests.len(), dir.display());
    Ok(())
}