summaryrefslogtreecommitdiff
path: root/src/lib.rs
blob: 1418ebd1a2d380affde4f77be4005df1874fa716 (plain)
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<Duration> = 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();
}