//! Shared game code: data definitions, components, map loading and network protocol. pub mod components; pub mod data; pub mod grid; pub mod map; pub mod protocol; use std::path::{Path, PathBuf}; use bevy::prelude::*; pub use data::GameData; /// Map loaded when no other map is configured, relative to the assets directory. pub const DEFAULT_MAP: &str = "maps/station.tmx"; pub const DEFAULT_PORT: u16 = 5000; /// Bump when the protocol changes in incompatible ways. pub const PROTOCOL_ID: u64 = 1; /// Locates the `assets` directory. /// /// Uses `GAME_ASSETS_DIR` if set, otherwise searches upwards from the working /// directory and the executable for a directory named `assets` containing `data/`. pub fn assets_dir() -> PathBuf { if let Some(dir) = std::env::var_os("GAME_ASSETS_DIR") { return dir.into(); } let starts = [ std::env::current_dir().ok(), std::env::current_exe().ok(), Some(PathBuf::from(env!("CARGO_MANIFEST_DIR"))), ]; starts .into_iter() .flatten() .find_map(|start| { start .ancestors() .map(|p| p.join("assets")) .find(|p| p.join("data").is_dir()) }) .unwrap_or_else(|| PathBuf::from("assets")) } /// Absolute path for an asset-relative path. pub fn asset_path(relative: impl AsRef) -> PathBuf { assets_dir().join(relative) } /// Loads [`GameData`] and registers the network protocol. pub struct CorePlugin; impl Plugin for CorePlugin { fn build(&self, app: &mut App) { let dir = assets_dir().join("data"); let data = GameData::load_dir(&dir).unwrap_or_else(|e| panic!("{e}")); info!( "loaded {} items, {} recipes, {} buildings, {} skills from `{}`", data.items.len(), data.recipes.len(), data.buildings.len(), data.skills.len(), dir.display() ); app.insert_resource(data) .add_plugins(protocol::ProtocolPlugin); } }