summaryrefslogtreecommitdiff
path: root/src/main.rs
blob: 100cfea9eadab77e12cd0a98a75e746413752ee0 (plain)
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: Option<NonZeroU32>,
	default_window_height: Option<NonZeroU32>,
	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<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.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?");
		}
	})
}