Last active
July 27, 2024 17:52
-
-
Save slyedoc/4e8ff426db12beadabdef9010d702ef8 to your computer and use it in GitHub Desktop.
build script with load folders
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /// Its a bad idea, but works | |
| /// This build script generates the Assets Enums and GameAssets file which contains the | |
| use std::env; | |
| use std::{ | |
| fs::{self}, | |
| io::{self, Read, Write}, | |
| path::{Path, PathBuf}, | |
| }; | |
| // print macro | |
| macro_rules! print_warning { | |
| ($($tokens: tt)*) => { | |
| println!("cargo:warning={}", format!($($tokens)*)) | |
| } | |
| } | |
| // Note: Yea this is out f control | |
| fn main() -> io::Result<()> { | |
| let out_dir = env::var("OUT_DIR").unwrap(); | |
| let file_path = Path::new(&out_dir).join("build_enums.rs"); | |
| let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); | |
| let assets_dir = PathBuf::from(&manifest_dir).join("assets"); | |
| let existing_contents = if file_path.exists() { | |
| fs::File::open(&file_path) | |
| .and_then(|mut file| { | |
| let mut contents = String::new(); | |
| file.read_to_string(&mut contents)?; | |
| Ok(contents) | |
| }) | |
| .unwrap_or_default() | |
| } else { | |
| String::new() | |
| }; | |
| // Tell cargo to rerun this build script | |
| println!("cargo:rerun-if-changed=build.rs"); | |
| println!("cargo:rerun-if-changed=assets/",); | |
| // Generate file | |
| let mut f = Vec::<u8>::new(); | |
| // number of assets to much, this is out of control | |
| // loop though the assets and generate the enums | |
| let mut settings = vec![ | |
| AssetSettings::new( | |
| "fonts", | |
| "FontFile", | |
| "ttf", | |
| "fonts", | |
| "Handle<Font>", | |
| ), | |
| AssetSettings::new( | |
| "levels", | |
| "Level", | |
| "glb", | |
| "levels", | |
| "Handle<Gltf>", | |
| ), //.with_ext("Scene0"), | |
| AssetSettings::new( | |
| "blueprints", | |
| "Blueprint", | |
| "glb", | |
| "blueprints", | |
| "Handle<Gltf>", | |
| ), | |
| AssetSettings::new( | |
| "materials", | |
| "MaterialFile", | |
| "glb", | |
| "materials", | |
| "Handle<Gltf>", | |
| ), | |
| AssetSettings::new( | |
| "game_icons", | |
| "GameIcon", | |
| "png", | |
| "icons/game", | |
| "Handle<Image>", | |
| ), | |
| AssetSettings::new( | |
| "skyboxes", | |
| "SkyboxFile", | |
| "ktx2", | |
| "skyboxes", | |
| "Handle<Image>", | |
| ), | |
| AssetSettings::new( | |
| "musics", | |
| "Music", | |
| "ogg", | |
| "music", | |
| "Handle<bevy_kira_audio::AudioSource>", | |
| ), | |
| AssetSettings::new( | |
| "sounds", | |
| "SoundFile", | |
| "ogg", | |
| "sounds", | |
| "Handle<bevy_kira_audio::AudioSource>", | |
| ), | |
| AssetSettings::new( | |
| "sound_interface", | |
| "SoundInterface", | |
| "ogg", | |
| "sounds/interface", | |
| "Handle<bevy_kira_audio::AudioSource>", | |
| ), | |
| AssetSettings::new( | |
| "textures", | |
| "TextureFile", | |
| "png", | |
| "textures", | |
| "Handle<Image>", | |
| ), | |
| AssetSettings::new( | |
| "models", | |
| "ModelFile", | |
| "glb", | |
| "models", | |
| "Handle<Gltf>", | |
| ), | |
| ]; | |
| for a in settings.iter_mut() { | |
| a.write_enum(&mut f, &assets_dir)?; | |
| a.write_impl(&mut f)?; | |
| } | |
| // Write GameAssets | |
| write!( | |
| f, | |
| r#" | |
| #[derive(AssetCollection, Resource, Debug, Reflect)] | |
| pub struct GameAssets {{ | |
| "# | |
| )?; | |
| for a in settings { | |
| write!( | |
| f, | |
| r#" | |
| #[asset(paths({list}), collection(typed, mapped))] | |
| pub {asset_name}: HashMap<String, {type_str}>, | |
| "#, | |
| list = a.list.unwrap(), | |
| asset_name = a.prop_name, | |
| type_str = a.type_str | |
| )?; | |
| } | |
| // Write GameAssets | |
| write!( | |
| f, | |
| r#" | |
| }}"# | |
| )?; | |
| let new_contents = String::from_utf8(f).unwrap(); | |
| let changed = !new_contents.eq(&existing_contents); | |
| print_warning!("build file changed: {}", changed); | |
| print_warning!("generated file path: {:?}", &file_path); | |
| // Only write to file if the content is different... | |
| if changed { | |
| let mut file = fs::File::create(&file_path)?; | |
| file.write_all(new_contents.as_bytes())?; | |
| } | |
| Ok(()) | |
| } | |
| struct AssetSettings { | |
| prop_name: &'static str, | |
| enum_name: &'static str, | |
| extension: &'static str, | |
| path: &'static str, | |
| type_str: &'static str, | |
| list: Option<String>, | |
| post_ext: Option<&'static str>, | |
| } | |
| impl AssetSettings { | |
| pub fn new( | |
| asset_name: &'static str, | |
| enum_name: &'static str, | |
| extension: &'static str, | |
| path: &'static str, | |
| type_str: &'static str, | |
| ) -> Self { | |
| Self { | |
| prop_name: asset_name, | |
| enum_name, | |
| extension, | |
| path, | |
| type_str, | |
| list: None, | |
| post_ext: None, | |
| } | |
| } | |
| #[allow(dead_code)] | |
| pub fn with_ext(mut self, post_ext: &'static str) -> Self { | |
| self.post_ext = Some(post_ext); | |
| self | |
| } | |
| pub fn write_enum( | |
| &mut self, | |
| mut f: impl io::Write, | |
| assets_dir: &PathBuf, | |
| ) -> Result<(), io::Error> { | |
| write!(f, "#[derive(Debug, clap::ValueEnum, EnumIter, EnumString, EnumProperty, Default, Component, States, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Reflect)]\n")?; | |
| write!( | |
| f, | |
| "#[reflect(Default, Serialize, Deserialize)]\n" | |
| )?; | |
| write!(f, "pub enum {} {{\n", self.enum_name)?; | |
| write!(f, " #[default]\n")?; | |
| let dir_path = assets_dir.clone().join(self.path); | |
| let mut entries = fs::read_dir(dir_path)? | |
| .filter_map(|e| e.ok()) | |
| .filter(|e| { | |
| e.path() | |
| .extension() | |
| .map_or(false, |ext| ext == self.extension) | |
| }) | |
| .collect::<Vec<_>>(); | |
| entries.sort_by(|a, b| a.path().file_stem().cmp(&b.path().file_stem())); | |
| let mut files = Vec::<String>::new(); | |
| for entry in entries { | |
| let file_stem = entry | |
| .path() | |
| .file_stem() | |
| .unwrap() | |
| .to_str() | |
| .unwrap() | |
| .to_owned(); | |
| let sanitized_name = sanitize_file_name(&file_stem); | |
| // Create a path relative to the project root | |
| let file_path = entry.path(); | |
| let relative_path = file_path.strip_prefix(assets_dir).unwrap(); | |
| // Save the mapping from enum variant to file path | |
| let mut path_str = relative_path.display().to_string().replace("\\", "/"); | |
| if let Some(post_ext) = self.post_ext { | |
| path_str = format!("{}#{}", path_str, post_ext); | |
| } | |
| write!( | |
| f, | |
| r#" #[strum(props(path = "{path_str}"))] | |
| {sanitized_name}, | |
| "# | |
| )?; | |
| files.push(path_str); | |
| } | |
| write!(f, "}}\n")?; // Close enum definition | |
| self.list = Some( | |
| files | |
| .iter() | |
| .map(|x| format!("\"{}\"", x)) | |
| .collect::<Vec<_>>() | |
| .join(", "), | |
| ); | |
| Ok(()) | |
| } | |
| pub fn write_impl(&mut self, mut f: impl io::Write) -> Result<(), io::Error> { | |
| write!( | |
| f, | |
| r#"impl {enum_name} {{ | |
| pub fn asset(&self, assets: &GameAssets) -> {type_str} {{ | |
| let file = self.get_str("path").unwrap(); | |
| let file_trimmed = file.split('#').next().unwrap_or(file); | |
| assets.{prop} | |
| .get(file_trimmed) | |
| .unwrap_or_else(|| panic!("{enum_name} {{:?}} not found", self)) | |
| .clone() | |
| }} | |
| }} | |
| "#, | |
| enum_name = self.enum_name, | |
| type_str = self.type_str, | |
| prop = self.prop_name | |
| )?; | |
| // impl Music { | |
| // pub fn asset(&self, assets: &GameAssets) -> | |
| // Handle<bevy_kira_audio::AudioSource> { | |
| // let file = self.get_str("path").unwrap(); | |
| // let file_trimmed = file.split('#').next().unwrap_or(file); | |
| // assets.musics | |
| // .get(file_trimmed) | |
| // .unwrap_or_else(|| panic!("Music {:?} not found", self)) | |
| // .clone() | |
| // } | |
| // } | |
| Ok(()) | |
| } | |
| } | |
| fn sanitize_file_name(name: &String) -> String { | |
| name.split(|c: char| !c.is_alphanumeric()) | |
| .filter(|part| !part.is_empty()) | |
| .map(|part| { | |
| let mut chars = part.chars(); | |
| chars | |
| .next() | |
| .unwrap() | |
| .to_uppercase() | |
| .chain(chars.flat_map(|c| c.to_lowercase())) | |
| .collect::<String>() | |
| }) | |
| .collect() | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment