139 lines
3.7 KiB
Rust
139 lines
3.7 KiB
Rust
use oden_js::{
|
|
module::loader::{ModuleLoader, ModuleSource},
|
|
Context, ContextRef, Result, Runtime, Value,
|
|
};
|
|
use std::ffi::OsStr;
|
|
use std::sync::mpsc::{channel, Receiver};
|
|
use std::time::Instant;
|
|
|
|
pub mod graphics;
|
|
use graphics::GraphicsCommand;
|
|
|
|
mod typescript;
|
|
use typescript::transpile_to_javascript;
|
|
|
|
mod io;
|
|
mod time;
|
|
|
|
struct Loader {}
|
|
|
|
impl Loader {
|
|
pub fn new() -> Loader {
|
|
Loader {}
|
|
}
|
|
}
|
|
|
|
impl ModuleLoader for Loader {
|
|
fn load(&self, _context: &ContextRef, name: &str) -> Result<ModuleSource> {
|
|
eprintln!("Loading {name}...");
|
|
let path = io::resolve_path(name, &["ts"])?;
|
|
let contents = std::fs::read_to_string(&path)?;
|
|
let contents = if path.extension().and_then(OsStr::to_str) == Some("ts") {
|
|
transpile_to_javascript(name, contents)?
|
|
} else {
|
|
contents
|
|
};
|
|
|
|
Ok(ModuleSource::JavaScript(contents))
|
|
}
|
|
}
|
|
|
|
pub struct ScriptContext {
|
|
context: Context,
|
|
init: Value,
|
|
update: Value,
|
|
draw: Value,
|
|
|
|
gfx: graphics::GraphicsAPI,
|
|
gfx_receive: Receiver<graphics::GraphicsCommand>,
|
|
|
|
time: time::TimeAPI,
|
|
}
|
|
|
|
impl ScriptContext {
|
|
pub fn new() -> Self {
|
|
let runtime = Runtime::with_loader(Loader::new());
|
|
|
|
let mut context = Context::new(runtime);
|
|
context.add_intrinsic_bigfloat();
|
|
context.add_intrinsic_bigdecimal();
|
|
context.add_intrinsic_operators();
|
|
|
|
let (gfx_send, gfx_receive) = channel();
|
|
|
|
let gfx = graphics::GraphicsAPI::define(&context, gfx_send.clone())
|
|
.expect("Graphics module should load without error");
|
|
let _io = io::IoAPI::define(&context).expect("IO module should load without error");
|
|
let time = time::TimeAPI::define(&context).expect("Time module should load without error");
|
|
|
|
let module = context
|
|
.import_module("./main.ts", "")
|
|
.expect("Unable to load main");
|
|
|
|
let init = module
|
|
.get_export(&context, "init")
|
|
.expect("Unable to fetch init");
|
|
let update = module
|
|
.get_export(&context, "update")
|
|
.expect("Unable to fetch update");
|
|
let draw = module
|
|
.get_export(&context, "draw")
|
|
.expect("Unable to fetch draw");
|
|
|
|
ScriptContext {
|
|
context,
|
|
|
|
init,
|
|
update,
|
|
draw,
|
|
|
|
gfx,
|
|
gfx_receive,
|
|
|
|
time,
|
|
}
|
|
}
|
|
|
|
// TODO: The script could really be on a background thread you know.
|
|
// We would want a bi-directional gate for frames to not let the
|
|
// game thread go to fast probably? And to discard whole frames &c.
|
|
|
|
pub fn init(&mut self) {
|
|
self.init
|
|
.call(&self.context, &[])
|
|
.expect("Exception in init");
|
|
}
|
|
|
|
pub fn update(&mut self) {
|
|
// Do we update the frame time before of after async completion?
|
|
// Hmmmmm.
|
|
self.time.set_frame_time(Instant::now());
|
|
|
|
// Tell the runtime to process all pending "jobs". This includes
|
|
// promise completions.
|
|
self.context
|
|
.process_all_jobs()
|
|
.expect("Error processing async jobs");
|
|
|
|
// Now run the update function.
|
|
self.update
|
|
.call(&self.context, &[])
|
|
.expect("Exception in update");
|
|
}
|
|
|
|
pub fn render(&mut self) -> Vec<graphics::GraphicsCommand> {
|
|
self.draw
|
|
.call(&self.context, &[])
|
|
.expect("Exception in draw");
|
|
self.gfx.end_frame();
|
|
|
|
let mut commands = Vec::new();
|
|
loop {
|
|
match self.gfx_receive.recv().unwrap() {
|
|
GraphicsCommand::EndFrame => break,
|
|
other => commands.push(other),
|
|
}
|
|
}
|
|
commands
|
|
}
|
|
}
|