[oden] Catch and render script errors, stop crashing
This is kinda nice actually
This commit is contained in:
parent
a08bc07cbb
commit
22732c2b05
2 changed files with 72 additions and 30 deletions
|
|
@ -41,7 +41,7 @@ pub enum Error {
|
||||||
ConversionError(String),
|
ConversionError(String),
|
||||||
#[error("an error occurred calling a rust function: {0}")]
|
#[error("an error occurred calling a rust function: {0}")]
|
||||||
RustFunctionError(String),
|
RustFunctionError(String),
|
||||||
#[error("an exception was thrown during evaluation: {1}\nStack: {2}")]
|
#[error("an exception was thrown during evaluation: {1}\nStack:\n{2}")]
|
||||||
Exception(Value, String, String),
|
Exception(Value, String, String),
|
||||||
#[error("out of memory")]
|
#[error("out of memory")]
|
||||||
OutOfMemory,
|
OutOfMemory,
|
||||||
|
|
|
||||||
100
src/script.rs
100
src/script.rs
|
|
@ -15,7 +15,7 @@ mod input;
|
||||||
mod io;
|
mod io;
|
||||||
mod time;
|
mod time;
|
||||||
|
|
||||||
use graphics::GraphicsCommand;
|
use graphics::{ClearCommand, GraphicsCommand, PrintCommand};
|
||||||
|
|
||||||
mod typescript;
|
mod typescript;
|
||||||
use typescript::transpile_to_javascript;
|
use typescript::transpile_to_javascript;
|
||||||
|
|
@ -66,6 +66,8 @@ pub struct ScriptContext {
|
||||||
|
|
||||||
time: time::TimeAPI,
|
time: time::TimeAPI,
|
||||||
input: input::InputAPI,
|
input: input::InputAPI,
|
||||||
|
|
||||||
|
error_lines: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScriptContext {
|
impl ScriptContext {
|
||||||
|
|
@ -115,6 +117,8 @@ impl ScriptContext {
|
||||||
|
|
||||||
time,
|
time,
|
||||||
input,
|
input,
|
||||||
|
|
||||||
|
error_lines: Vec::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,15 +127,17 @@ impl ScriptContext {
|
||||||
pub fn suspend(&self) -> Option<Vec<u8>> {
|
pub fn suspend(&self) -> Option<Vec<u8>> {
|
||||||
let _span = span!("script suspend");
|
let _span = span!("script suspend");
|
||||||
if !self.suspend.is_undefined() {
|
if !self.suspend.is_undefined() {
|
||||||
let suspend_state = self
|
match self.suspend.call(&self.context, &[&self.state]) {
|
||||||
.suspend
|
Ok(suspend_state) => Some(
|
||||||
.call(&self.context, &[&self.state])
|
suspend_state
|
||||||
.expect("Exception in suspend");
|
.serialize(&self.context)
|
||||||
Some(
|
.expect("Unable to serialize state"),
|
||||||
suspend_state
|
),
|
||||||
.serialize(&self.context)
|
Err(e) => {
|
||||||
.expect("Unable to serialize state"),
|
eprintln!("WARNING: Error during suspend, state will not be saved: {e}");
|
||||||
)
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
@ -151,6 +157,10 @@ impl ScriptContext {
|
||||||
pub fn update(&mut self) {
|
pub fn update(&mut self) {
|
||||||
let _span = span!("script update");
|
let _span = span!("script update");
|
||||||
|
|
||||||
|
if self.error_lines.len() > 0 {
|
||||||
|
return; // Don't bother, nothing.
|
||||||
|
}
|
||||||
|
|
||||||
// Do we update the frame time before of after async completion?
|
// Do we update the frame time before of after async completion?
|
||||||
// Hmmmmm.
|
// Hmmmmm.
|
||||||
self.time.set_frame_time(Instant::now());
|
self.time.set_frame_time(Instant::now());
|
||||||
|
|
@ -159,37 +169,69 @@ impl ScriptContext {
|
||||||
// promise completions.
|
// promise completions.
|
||||||
{
|
{
|
||||||
let _span = span!("process jobs");
|
let _span = span!("process jobs");
|
||||||
self.context
|
self.handle_result(self.context.process_all_jobs());
|
||||||
.process_all_jobs()
|
|
||||||
.expect("Error processing async jobs");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now run the update function.
|
// Now run the update function.
|
||||||
{
|
if self.error_lines.len() == 0 {
|
||||||
let _span = span!("javascript update");
|
let _span = span!("javascript update");
|
||||||
let old_state = &self.state;
|
let old_state = &self.state;
|
||||||
self.state = self
|
self.state = match self.update.call(&self.context, &[old_state]) {
|
||||||
.update
|
Ok(v) => v,
|
||||||
.call(&self.context, &[old_state])
|
Err(e) => {
|
||||||
.expect("Exception in update");
|
self.handle_error(e);
|
||||||
|
self.context.null()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&mut self) -> Vec<graphics::GraphicsCommand> {
|
pub fn render(&mut self) -> Vec<graphics::GraphicsCommand> {
|
||||||
let _span = span!("script render");
|
let _span = span!("script render");
|
||||||
|
if self.error_lines.len() > 0 {
|
||||||
|
// TODO: Use font 0 for a fallback.
|
||||||
|
let mut commands = vec![
|
||||||
|
GraphicsCommand::Clear(ClearCommand {
|
||||||
|
color: [0.0, 0.0, 1.0, 1.0],
|
||||||
|
}),
|
||||||
|
GraphicsCommand::Print(PrintCommand {
|
||||||
|
text: "FATAL SCRIPT ERROR".to_owned(),
|
||||||
|
pos: [6.0, 6.0],
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
self.draw
|
let mut y = 20.0;
|
||||||
.call(&self.context, &[&self.state])
|
for line in &self.error_lines {
|
||||||
.expect("Exception in draw");
|
commands.push(GraphicsCommand::Print(PrintCommand {
|
||||||
self.gfx.end_frame();
|
text: line.clone(),
|
||||||
|
pos: [6.0, y],
|
||||||
let mut commands = Vec::new();
|
}));
|
||||||
loop {
|
y += 8.0;
|
||||||
match self.gfx_receive.recv().unwrap() {
|
|
||||||
GraphicsCommand::EndFrame => break,
|
|
||||||
other => commands.push(other),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
commands
|
||||||
|
} else {
|
||||||
|
self.handle_result(self.draw.call(&self.context, &[&self.state]));
|
||||||
|
self.gfx.end_frame();
|
||||||
|
|
||||||
|
let mut commands = Vec::new();
|
||||||
|
loop {
|
||||||
|
match self.gfx_receive.recv().unwrap() {
|
||||||
|
GraphicsCommand::EndFrame => break,
|
||||||
|
other => commands.push(other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commands
|
||||||
}
|
}
|
||||||
commands
|
}
|
||||||
|
|
||||||
|
fn handle_result<T>(&mut self, result: Result<T>) {
|
||||||
|
if let Err(e) = result {
|
||||||
|
self.handle_error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_error(&mut self, error: oden_js::Error) {
|
||||||
|
self.error_lines = error.to_string().lines().map(|l| l.to_owned()).collect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue