chunkgen 0.2.0

Minecraft world generation library
Documentation
use std::collections::BTreeMap;
use std::sync::{Arc, OnceLock};

use serde_json::Value;

use crate::datapack::{Datapack, DatapackError, collect_json_files, path_to_id, read};
use crate::world::worldgen::synth::normal_noise::{NormalNoise, Parameters};

use super::function::Df;
use super::parse::ParseContext;
use serde::de;
use std::path::Path;

#[derive(Default)]
pub struct DensityRegistry {
    pub noises: BTreeMap<String, Arc<NormalNoise>>,
    pub holders: BTreeMap<String, Arc<OnceLock<Df>>>,
}

fn parse_error(path: &Path, message: String) -> DatapackError {
    DatapackError::Parse {
        path: path.to_path_buf(),
        source: de::Error::custom(message),
    }
}

impl DensityRegistry {
    pub fn load(pack: &Datapack) -> Result<Self, DatapackError> {
        let mut registry = Self::default();
        for (namespace, dir) in pack.namespaced_dirs("worldgen/noise") {
            for file in collect_json_files(&dir) {
                let content = read(&file)?;
                let params: Parameters =
                    serde_json::from_str(&content).map_err(|source| DatapackError::Parse {
                        path: file.clone(),
                        source,
                    })?;
                registry.noises.insert(
                    path_to_id(&namespace, &file, &dir),
                    Arc::new(NormalNoise::new(params)),
                );
            }
        }

        let mut sources = Vec::new();
        for (namespace, dir) in pack.namespaced_dirs("worldgen/density_function") {
            for file in collect_json_files(&dir) {
                let id = path_to_id(&namespace, &file, &dir);
                registry
                    .holders
                    .entry(id.clone())
                    .or_insert_with(|| Arc::new(OnceLock::new()));
                sources.push((id, file));
            }
        }

        let mut parsed = BTreeMap::new();
        for (id, file) in sources {
            let content = read(&file)?;
            let value: Value =
                serde_json::from_str(&content).map_err(|source| DatapackError::Parse {
                    path: file.clone(),
                    source,
                })?;
            let df = registry.parse(&value).map_err(|e| parse_error(&file, e))?;
            parsed.insert(id, df);
        }
        for (id, df) in parsed {
            let _ = registry.holders[&id].set(df);
        }
        Ok(registry)
    }

    pub fn parse(&self, value: &Value) -> Result<Df, String> {
        ParseContext {
            holders: &self.holders,
            noises: &self.noises,
        }
        .parse(value)
    }

    pub fn holder(&self, key: &str) -> Result<Df, String> {
        ParseContext {
            holders: &self.holders,
            noises: &self.noises,
        }
        .holder(key)
    }
}