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<Path>,
script_type: ScriptType,
hash: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
struct ConfigSprite {
texture: String,
x: f32,
y: f32,
z: f32,
}
#[derive(Debug, Clone, Deserialize)]
struct ConfigTexture {
size: usize,
path: Box<Path>,
}
#[derive(Debug, Clone, Deserialize)]
struct Scene {
initial_sprites: HashMap<String, ConfigSprite>,
initial_scripts: Vec<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct Config {
alligator_version: String,
scenes: HashMap<String, Scene>,
textures: HashMap<String, ConfigTexture>,
scripts: HashMap<String, ConfigScript>,
default_scene: String,
sprite_manager_capacity: u32,
default_window_width: NonZeroU32,
default_window_height: NonZeroU32,
default_window_mode: ConfigWindowMode,
window_title: Option<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<Box<str>>,
/// 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,
default_height: config.default_window_height,
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 {
width: config.default_window_width.get(),
height: config.default_window_height.get(),
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: 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?");
}
})
}
|