[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 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<OdenEvent>, 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:?}"),
};
}
}

View file

@ -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<Vec<u8>>, reload_trigger: Sender<()>) -> Result<Self> {
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),