chunkgen 0.2.0

Minecraft world generation library
Documentation
use std::fs;
use std::io::{Cursor, Read as _};
use std::sync::LazyLock;

use flate2::read::GzDecoder;
use parking_lot::RwLock;
use rustc_hash::FxHashMap;
use shulkr::util::Key;
use simdnbt::owned::{Nbt, read as read_nbt};

use crate::datapack::{self, Datapack};
use crate::datapack::{collect_json_files, path_to_id, read_and_parse_normalized};
use crate::world::worldgen::structure::templatesystem::structure_processor::StructureProcessorList;
use crate::world::worldgen::structure::templatesystem::structure_template::StructureTemplate;

pub struct StructureTemplateManager {
    pack: &'static Datapack,
    cache: RwLock<FxHashMap<Key, &'static StructureTemplate>>,
}

impl StructureTemplateManager {
    pub fn new(pack: &'static Datapack) -> Self {
        Self {
            pack,
            cache: RwLock::new(FxHashMap::default()),
        }
    }

    pub fn get_or_create(&self, key: &Key) -> &'static StructureTemplate {
        if let Some(template) = self.cache.read().get(key).copied() {
            return template;
        }

        let template: &'static StructureTemplate = Box::leak(Box::new(
            self.load(key).unwrap_or_else(StructureTemplate::empty),
        ));

        let mut cache = self.cache.write();
        cache.entry(key.clone()).or_insert(template)
    }

    fn load(&self, key: &Key) -> Option<StructureTemplate> {
        let path = self
            .pack
            .files(key.namespace(), &format!("structure/{}.nbt", key.path()))
            .pop()?;
        let compressed = fs::read(path).ok()?;
        let mut decoder = GzDecoder::new(&compressed[..]);
        let mut decompressed = Vec::new();
        decoder.read_to_end(&mut decompressed).ok()?;
        let Nbt::Some(nbt) = read_nbt(&mut Cursor::new(&decompressed[..])).ok()? else {
            return None;
        };
        Some(StructureTemplate::load(&nbt.as_compound()))
    }
}

static TEMPLATES: LazyLock<StructureTemplateManager> =
    LazyLock::new(|| StructureTemplateManager::new(datapack::active()));

pub fn templates() -> &'static StructureTemplateManager {
    &TEMPLATES
}

static PROCESSOR_LISTS: LazyLock<FxHashMap<Key, StructureProcessorList>> =
    LazyLock::new(|| load_processor_lists(datapack::active()));

pub fn processor_list(key: &Key) -> Option<&'static StructureProcessorList> {
    PROCESSOR_LISTS.get(key)
}

fn load_processor_lists(pack: &Datapack) -> FxHashMap<Key, StructureProcessorList> {
    let mut lists = FxHashMap::default();
    for (namespace, directory) in pack.namespaced_dirs("worldgen/processor_list") {
        for file in collect_json_files(&directory) {
            let key = Key::of(path_to_id(&namespace, &file, &directory));
            let list: StructureProcessorList = read_and_parse_normalized(&file)
                .unwrap_or_else(|error| panic!("failed to load {}: {error}", file.display()));
            lists.insert(key, list);
        }
    }
    lists
}