use std::collections::HashMap; use std::fs::File; use std::io::Read; use std::num::NonZeroU32; use std::path::Path; use alligator_console::Console; use alligator_profiler as profile; use alligator_scripts::ScriptManager; use alligator_sprites::SpriteManager; use alligator_sys::{Renderer, RendererConfig, Window, WindowConfig, WindowEvent}; use clap::Parser; use pollster::FutureExt; use serde::Deserialize; use sha3::{Digest, Sha3_256}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)] enum ScriptType { Wasm, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)] enum ConfigWindowMode { Windowed, BorderlessFullscreen, } #[derive(Debug, Clone, Deserialize)] struct ConfigScript { path: Box, script_type: ScriptType, hash: Option, } #[derive(Debug, Clone, Deserialize)] struct ConfigSprite { texture: String, x: f32, y: f32, z: f32, } #[derive(Debug, Clone, Deserialize)] struct ConfigTexture { size: usize, path: Box, } #[derive(Debug, Clone, Deserialize)] struct Scene { initial_sprites: HashMap, initial_scripts: Vec, } #[derive(Debug, Clone, Deserialize)] pub struct Config { alligator_version: String, scenes: HashMap, textures: HashMap, scripts: HashMap, default_scene: String, sprite_manager_capacity: u32, default_window_width: Option, default_window_height: Option, default_window_mode: ConfigWindowMode, window_title: String, vsync: bool, } #[derive(Debug, Clone, Parser)] #[command(version, about, long_about = None)] pub struct CommandLineArguments { /// The configuration, represented as a JSON object. If not specified, /// this is retrieved from the game.json in the executable's directory. #[arg(short, long)] config: Option>, /// Send and receive console commands through the stdio #[arg(short, long)] debug: bool, } fn sprite_manager(config: &Config) -> SpriteManager { SpriteManager::with_capacity(config.sprite_manager_capacity as usize) } fn script_manager(config: &Config) -> ScriptManager { let mut scripts = ScriptManager::new(); for (key, script) in config.scripts.iter() { let path = script.path.clone(); let trusted = if let Some(hash) = &script.hash { let mut bytes = Vec::new(); let mut msg = File::open(&path).unwrap(); msg.read_to_end(&mut bytes).unwrap(); let mut hasher = Sha3_256::new(); hasher.update(bytes); let result = hasher.finalize(); hash.as_bytes() == &result[..] } else { false }; scripts .add_wasm_script(key.clone().into_boxed_str(), path, trusted) .unwrap(); } scripts } fn window(config: &Config) -> Window { let config = WindowConfig { title: config.window_title.clone(), // TODO set window size properly default_width: config.default_window_width.unwrap().get(), default_height: config.default_window_height.unwrap().get(), default_x: 200, default_y: 200, borderless_fullscreen: config.default_window_mode == ConfigWindowMode::BorderlessFullscreen, visible: false, }; Window::new(config) } fn renderer(config: &Config, window: &Window) -> Renderer { let config = RendererConfig { // TODO set window size properly width: config.default_window_width.unwrap().get(), height: config.default_window_height.unwrap().get(), instance_capacity: config.sprite_manager_capacity, fullscreen: false, vsync: config.vsync, }; Renderer::new(window, config) } fn main() { let args = CommandLineArguments::parse(); let console = if args.debug { let console = Console::new(26009) .block_on() .expect("The console should not fail to start"); log::set_logger(Box::leak(Box::new(console.logger()))) .expect("this should be the only logger"); profile::set_profiler(console.logger()); Some(console) } else { None }; //std::env::set_current_dir(std::env::current_exe().unwrap().parent().unwrap()).unwrap(); let config = match args.config { Some(config) => serde_json::from_str(&config).unwrap(), None => { let config = File::open("game.json").unwrap(); serde_json::from_reader(config).unwrap() } }; let sprites = sprite_manager(&config); let scripts = script_manager(&config); let mut window = window(&config); window.wait_for_resume(); let mut renderer = renderer(&config, &window); window.set_visible(true); window.run(move |window, event| { match event { Some(WindowEvent::RedrawRequest) => { renderer.render_frame(); profile::finish_frame(); } Some(WindowEvent::CloseRequest) => { std::process::exit(0); } Some(_) => (), None => window.request_redraw(), }; if let Some(c) = &console { c.flush().expect("why would this error?"); } }) }