use std::fs::File; use std::io::BufReader; use paste::paste; pub use level_entity_config::LevelEntityConfigData; pub use misc_data::*; pub mod node_data; pub mod pb_components; pub mod text_map_data; mod misc_data; #[derive(thiserror::Error, Debug)] pub enum LoadDataError { #[error("I/O error: {0}")] Io(#[from] std::io::Error), #[error("Failed to parse json: {0}")] Json(#[from] serde_json::Error), } macro_rules! json_data { ($($table_type:ident;)*) => { $(paste! { mod [<$table_type:snake>]; pub use [<$table_type:snake>]::[<$table_type Data>]; })* $(paste! { pub mod [<$table_type:snake _data>] { use std::sync::OnceLock; type Data = super::[<$table_type Data>]; pub(crate) static TABLE: OnceLock> = OnceLock::new(); pub fn iter() -> std::slice::Iter<'static, Data> { TABLE.get().unwrap().iter() } } })* fn load_json_data(base_path: &str) -> Result<(), LoadDataError> { $(paste! { let path = format!("{}/{}.json", base_path, stringify!($table_type)); tracing::debug!("Loading data started: {path}"); let file = File::open(&path)?; let reader = BufReader::new(file); let _ = [<$table_type:snake _data>]::TABLE.set(serde_json::from_reader(reader)?); tracing::info!("Loading data finished: {path}"); })* Ok(()) } }; } macro_rules! json_hash_table_data { ($($table_type:ident, $key_param:expr, $key_type:ty;)*) => { $(paste! { mod [<$table_type:snake>]; pub use [<$table_type:snake>]::[<$table_type Data>]; })* $(paste! { pub mod [<$table_type:snake _data>] { use std::collections::HashMap; use std::sync::OnceLock; pub(crate) type Data = super::[<$table_type Data>]; pub(crate) static TABLE: OnceLock> = OnceLock::new(); pub fn iter() -> std::collections::hash_map::Iter<'static, $key_type, Data> { TABLE.get().unwrap().iter() } pub fn get(k: &$key_type) -> Option<&'static Data> { TABLE.get().unwrap().get(k) } } })* fn load_json_hash_table_data(base_path: &str) -> Result<(), LoadDataError> { $(paste! { let path = format!("{}/{}.json", base_path, stringify!($table_type)); tracing::debug!("Loading data started: {path}"); let file = File::open(&path)?; let reader = BufReader::new(file); // Key.clone() is required for String keys, for other types like ints, it doesn't // have any effect since clone is *value let _ = [<$table_type:snake _data>]::TABLE.set( serde_json::from_reader::, Vec<[<$table_type:snake _data>]::Data>>(reader)? .into_iter() .map(|element| (element.$key_param.clone(), element)) .collect::>() ); tracing::info!("Loading data finished: {path}"); })* Ok(()) } }; } pub fn load_all_json_data(base_path: &str) -> Result<(), LoadDataError> { load_json_data(base_path)?; load_json_hash_table_data(base_path)?; load_json_entity_level_config_data(base_path)?; Ok(()) } json_data! { Achievement; AdventureTask; Area; BaseProperty; CalabashDevelopReward; CalabashLevel; Damage; DungeonDetection; ExchangeReward; ExchangeShared; ExploreProgress; ExploreTools; FavorGoods; FavorLevel; FavorStory; FavorWord; ForgeFormula; FunctionCondition; Gacha; GachaPool; GachaViewInfo; GuideGroup; GuideTutorial; InstanceDungeon; ItemExchangeContent; // LevelPlayData; LevelPlayNodeData; LivenessTask; LordGym; MonsterDetection; MonsterPropertyGrowth; Motion; QuestNodeData; ResonanceAmplification; ResonantChain; RoleBreach; RoleExpItem; RoleInfo; RoleLevelConsume; RolePropertyGrowth; RoleSkin; SilentAreaDetection; SynthesisFormula; Teleporter; WeaponBreach; WeaponConf; WeaponExpItem; WeaponLevel; WeaponPropertyGrowth; WeaponReson; } json_hash_table_data! { AiBase, id, i32; AiStateMachineConfig, id, String; BlueprintConfig, blueprint_type, String; DragonPool, id, i32; DropPackage, id, i32; TemplateConfig, blueprint_type, String; SummonCfg, blueprint_type, String; Buff, id, i64; } mod level_entity_config; pub mod level_entity_config_data { use std::collections::HashMap; use std::sync::OnceLock; pub(crate) type Data = super::LevelEntityConfigData; pub(crate) static TABLE: OnceLock> = OnceLock::new(); pub fn iter() -> std::collections::hash_map::Iter<'static, String, Data> { TABLE.get().unwrap().iter() } pub fn get(map_id: i32, entity_id: i64) -> Option<&'static Data> { TABLE .get() .unwrap() .get(&create_key_internal(map_id, entity_id)) } #[inline(always)] pub fn create_key(element: &Data) -> String { create_key_internal(element.map_id, element.entity_id) } #[inline(always)] fn create_key_internal(map_id: i32, entity_id: i64) -> String { format!("{}_{}", map_id, entity_id) } } fn load_json_entity_level_config_data(base_path: &str) -> Result<(), LoadDataError> { let path = format!("{}/LevelEntityConfig.json", base_path); tracing::debug!("Loading data started: {path}"); let file = File::open(&path)?; let reader = BufReader::new(file); let _ = level_entity_config_data::TABLE.set( serde_json::from_reader::, Vec>(reader)? .into_iter() .map(|element| (level_entity_config_data::create_key(&element), element)) .collect::>(), ); tracing::info!("Loading data finished: {path}"); Ok(()) }