pub mod buffer; pub mod file; pub mod input; pub mod manifest; pub mod scene; pub mod script; pub mod storage; pub mod time; use crate::{ buffer::{Buffer, Event}, file::FileManager, input::{Direction, InputManager, Position}, manifest::GameManifest, scene::switch_scenes, script::{ScriptContext, ScriptManager, WasmRunner}, storage::StorageManager, time::TimeContext, }; use crossterm::event::MouseEventKind; use etcetera::AppStrategy; use prologger::{ColorMode, ProLogger, RotationConfig}; use std::{ fs::File, io::{BufRead, BufWriter}, path::PathBuf, time::{Duration, Instant}, }; pub fn apply_patches( manifest: &mut GameManifest, files: &mut FileManager, wasm_compiler: &WasmRunner, ) -> std::io::Result<()> { let path = etcetera::choose_app_strategy(manifest.app_strategy_args()) .unwrap() .in_config_dir("patches"); let patchfile = File::open(path)?; let mut patches = Vec::new(); for line in std::io::BufReader::new(patchfile).lines() { let Ok(line) = line else { continue; }; patches.push(line); } for path in patches { let path = PathBuf::from(path).into_boxed_path(); let patch = files.load_file(path.clone(), wasm_compiler); let patch = patch.as_ref().as_ref().unwrap(); let patch = wasm_compiler.patch(patch)?; manifest.apply_wasm_patch(path, patch); } Ok(()) } pub fn init_logger(manifest: &GameManifest) { let path = etcetera::choose_app_strategy(manifest.app_strategy_args()) .unwrap() .in_state_dir("log") .unwrap(); let path = path.to_string_lossy(); ProLogger::builder() .with_rotating_file(&path, RotationConfig::new(100_000, 2)) .with_color(ColorMode::Never) .init() .unwrap(); } pub fn run_game(mut files: FileManager, wasm_runner: WasmRunner, manifest: GameManifest) { let start = Instant::now(); let stdout = Box::new(BufWriter::new(std::io::stdout().lock())); let mut framerate_limit: Option = None; let mut buffer = Buffer::new(stdout).unwrap(); let mut input = InputManager::new(); let mut time = TimeContext::new(); let mut storage = StorageManager::load(manifest.app_strategy_args()).unwrap(); let mut scripts = ScriptManager::new(&manifest.scripts); let mut context = ScriptContext { framerate_limit: &mut framerate_limit, manifest: &manifest, buffer: &mut buffer, storage: &mut storage, input: &input, time: &time, operation_queue: Vec::new(), }; let scene_start = Instant::now(); switch_scenes( &mut scripts, &mut context, &mut files, &wasm_runner, manifest.first_scene, ); log::info!( "Switch scenes time: {} ms", scene_start.elapsed().as_millis() ); let server_addr = format!("127.0.0.1:{}", puffin_http::DEFAULT_PORT); let _puffin_server = puffin_http::Server::new(&server_addr).unwrap(); puffin::set_scopes_on(true); log::info!("Total startup time: {} ms", start.elapsed().as_millis()); buffer .render_loop(|buffer, event| match event { Event::Key(event) => { if event.is_press() { input.press_key(event.code); } } Event::Mouse(event) => { match event.kind { MouseEventKind::Down(_) => input.click_mouse(), MouseEventKind::ScrollDown => input.scroll(Direction::Down), MouseEventKind::ScrollUp => input.scroll(Direction::Up), MouseEventKind::ScrollLeft => input.scroll(Direction::Left), MouseEventKind::ScrollRight => input.scroll(Direction::Right), _ => (), } input.move_mouse(Position { x: event.column, y: event.row, }); } Event::FocusChanged(is_focused) => { if is_focused { input.focus(); } else { input.unfocus(); } } Event::Render => { input.read_gil_events(); buffer.cells.clear(); let mut context = ScriptContext { framerate_limit: &mut framerate_limit, manifest: &manifest, buffer, storage: &mut storage, input: &input, time: &time, operation_queue: Vec::new(), }; scripts.run_scripts(&mut context, &wasm_runner, &mut files); files.reload_files(&wasm_runner); input.reset_next_frame(); if let Some(framerate_limit) = framerate_limit && let Ok(elapsed) = time.system_now().elapsed() { std::thread::sleep(framerate_limit - elapsed); } time.next_frame(); } }) .unwrap(); }