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();
}
|