[oden] Tolerate bad scripts on hot reload

When the script changes from under us it might be bugged for some
reason; just let that be for now, ignore the load, and hopefully the
engineer will fix it, eventually.
This commit is contained in:
John Doty 2023-08-19 18:39:42 -07:00
parent d79b891b7b
commit e32643486d
2 changed files with 26 additions and 65 deletions

View file

@ -768,8 +768,8 @@ fn main_thread(event_loop: EventLoopProxy<OdenEvent>, state: State, reciever: Re
let (script_reload_send, script_reload_recv) = channel(); let (script_reload_send, script_reload_recv) = channel();
let mut script = script::ScriptContext::new(script_reload_send.clone()); let mut script = script::ScriptContext::new(None, script_reload_send.clone())
script.init(); .expect("Unable to create initial script context");
const SPF: f64 = 1.0 / 60.0; const SPF: f64 = 1.0 / 60.0;
loop { loop {
@ -823,11 +823,10 @@ fn main_thread(event_loop: EventLoopProxy<OdenEvent>, state: State, reciever: Re
if reload_script { if reload_script {
eprintln!("RELOADING SCRIPT"); eprintln!("RELOADING SCRIPT");
let suspend_state = script.suspend(); let suspend_state = script.suspend();
script = script::ScriptContext::new(script_reload_send.clone()); match script::ScriptContext::new(suspend_state, script_reload_send.clone()) {
script.init(); Ok(new_script) => script = new_script,
if let Some(s) = suspend_state { Err(e) => eprintln!("WARNING: Script reload aborted, load failure: {e:?}"),
script.resume(&s); };
}
} }
} }

View file

@ -55,11 +55,9 @@ impl ModuleLoader for Loader {
pub struct ScriptContext { pub struct ScriptContext {
context: Context, context: Context,
init: Value,
update: Value, update: Value,
draw: Value, draw: Value,
suspend: Value, suspend: Value,
resume: Value,
state: Value, state: Value,
@ -71,7 +69,7 @@ pub struct ScriptContext {
} }
impl ScriptContext { impl ScriptContext {
pub fn new(reload_trigger: Sender<()>) -> Self { pub fn new(suspend_state: Option<Vec<u8>>, reload_trigger: Sender<()>) -> Result<Self> {
let mut runtime = Runtime::new(); let mut runtime = Runtime::new();
runtime.set_module_loader(Loader::new(reload_trigger)); runtime.set_module_loader(Loader::new(reload_trigger));
@ -82,41 +80,31 @@ impl ScriptContext {
let (gfx_send, gfx_receive) = channel(); let (gfx_send, gfx_receive) = channel();
let gfx = graphics::GraphicsAPI::define(&context, gfx_send.clone()) let gfx = graphics::GraphicsAPI::define(&context, gfx_send.clone())?;
.expect("Graphics module should load without error"); let _io = io::IoAPI::define(&context)?;
let _io = io::IoAPI::define(&context).expect("IO module should load without error"); let time = time::TimeAPI::define(&context)?;
let time = time::TimeAPI::define(&context).expect("Time module should load without error"); let input = input::InputAPI::define(&context)?;
let input =
input::InputAPI::define(&context).expect("Input module should load without error");
let module = context let module = context.import_module("./main.ts", "")?;
.import_module("./main.ts", "")
.expect("Unable to load main");
let init = module let init = module.get_export(&context, "init")?;
.get_export(&context, "init") let suspend = module.get_export(&context, "suspend")?;
.expect("Unable to fetch init"); let resume = module.get_export(&context, "resume")?;
let suspend = module let update = module.get_export(&context, "update")?;
.get_export(&context, "suspend") let draw = module.get_export(&context, "draw")?;
.expect("Unable to fetch suspend");
let resume = module
.get_export(&context, "resume")
.expect("Unable to fetch suspend");
let update = module
.get_export(&context, "update")
.expect("Unable to fetch update");
let draw = module
.get_export(&context, "draw")
.expect("Unable to fetch draw");
let state = context.undefined(); let mut state = init.call(&context, &[])?;
if let Some(buffer) = suspend_state {
if !resume.is_undefined() {
let serialized_state = context.deserialize(&buffer)?;
state = resume.call(&context, &[&serialized_state, &state])?;
}
}
ScriptContext { Ok(ScriptContext {
context, context,
init,
suspend, suspend,
resume,
update, update,
draw, draw,
@ -127,7 +115,7 @@ impl ScriptContext {
time, time,
input, input,
} })
} }
/// Allow the script to save its state before we destroy it and re-create /// Allow the script to save its state before we destroy it and re-create
@ -149,36 +137,10 @@ impl ScriptContext {
} }
} }
/// Allow the script to restore its state after being destroyed and
/// re-created.
pub fn resume(&mut self, state: &[u8]) {
let _span = span!("script resume");
if !self.resume.is_undefined() {
let suspend_state = self
.context
.deserialize(state)
.expect("Unable to deserialize state");
let prev_state = self.state.clone();
self.state = self
.resume
.call(&self.context, &[&suspend_state, &prev_state])
.expect("Exception in resume");
}
}
// TODO: The script could really be on a background thread you know. // 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 // 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. // game thread go to fast probably? And to discard whole frames &c.
pub fn init(&mut self) {
let _span = span!("script init");
self.state = self
.init
.call(&self.context, &[])
.expect("Exception in init");
}
pub fn input(&mut self, event: &WindowEvent) -> bool { pub fn input(&mut self, event: &WindowEvent) -> bool {
match event { match event {
WindowEvent::KeyboardInput { input, .. } => self.input.handle_keyboard_input(input), WindowEvent::KeyboardInput { input, .. } => self.input.handle_keyboard_input(input),