From e32643486dd402055e2f12f906eb28d7a407f82c Mon Sep 17 00:00:00 2001 From: John Doty Date: Sat, 19 Aug 2023 18:39:42 -0700 Subject: [PATCH] [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. --- src/lib.rs | 13 ++++----- src/script.rs | 78 +++++++++++++-------------------------------------- 2 files changed, 26 insertions(+), 65 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c0188976..4d96e084 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -768,8 +768,8 @@ fn main_thread(event_loop: EventLoopProxy, state: State, reciever: Re let (script_reload_send, script_reload_recv) = channel(); - let mut script = script::ScriptContext::new(script_reload_send.clone()); - script.init(); + let mut script = script::ScriptContext::new(None, script_reload_send.clone()) + .expect("Unable to create initial script context"); const SPF: f64 = 1.0 / 60.0; loop { @@ -823,11 +823,10 @@ fn main_thread(event_loop: EventLoopProxy, state: State, reciever: Re if reload_script { eprintln!("RELOADING SCRIPT"); let suspend_state = script.suspend(); - script = script::ScriptContext::new(script_reload_send.clone()); - script.init(); - if let Some(s) = suspend_state { - script.resume(&s); - } + match script::ScriptContext::new(suspend_state, script_reload_send.clone()) { + Ok(new_script) => script = new_script, + Err(e) => eprintln!("WARNING: Script reload aborted, load failure: {e:?}"), + }; } } diff --git a/src/script.rs b/src/script.rs index fe837177..be3abd74 100644 --- a/src/script.rs +++ b/src/script.rs @@ -55,11 +55,9 @@ impl ModuleLoader for Loader { pub struct ScriptContext { context: Context, - init: Value, update: Value, draw: Value, suspend: Value, - resume: Value, state: Value, @@ -71,7 +69,7 @@ pub struct ScriptContext { } impl ScriptContext { - pub fn new(reload_trigger: Sender<()>) -> Self { + pub fn new(suspend_state: Option>, reload_trigger: Sender<()>) -> Result { let mut runtime = Runtime::new(); runtime.set_module_loader(Loader::new(reload_trigger)); @@ -82,41 +80,31 @@ impl ScriptContext { 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 input = - input::InputAPI::define(&context).expect("Input module should load without error"); + let gfx = graphics::GraphicsAPI::define(&context, gfx_send.clone())?; + let _io = io::IoAPI::define(&context)?; + let time = time::TimeAPI::define(&context)?; + let input = input::InputAPI::define(&context)?; - let module = context - .import_module("./main.ts", "") - .expect("Unable to load main"); + let module = context.import_module("./main.ts", "")?; - let init = module - .get_export(&context, "init") - .expect("Unable to fetch init"); - let suspend = module - .get_export(&context, "suspend") - .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 init = module.get_export(&context, "init")?; + let suspend = module.get_export(&context, "suspend")?; + let resume = module.get_export(&context, "resume")?; + let update = module.get_export(&context, "update")?; + let draw = module.get_export(&context, "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, - init, suspend, - resume, update, draw, @@ -127,7 +115,7 @@ impl ScriptContext { time, input, - } + }) } /// 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. // 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) { - let _span = span!("script init"); - - self.state = self - .init - .call(&self.context, &[]) - .expect("Exception in init"); - } - pub fn input(&mut self, event: &WindowEvent) -> bool { match event { WindowEvent::KeyboardInput { input, .. } => self.input.handle_keyboard_input(input),