diff --git a/crates/benilla-app/src/ui_script/addons.rs b/crates/benilla-app/src/ui_script/addons.rs
index e5aef85d..cdbf2117 100644
--- a/crates/benilla-app/src/ui_script/addons.rs
+++ b/crates/benilla-app/src/ui_script/addons.rs
@@ -158,11 +158,11 @@ impl Addon {
// order, as `AddOn_Load 0x51f240` hands every listed file to `0x6edb90`.
match script.run_chunk_named(&bytes, &self.chunk_name(file, &path)) {
Ok(()) => info!("ui_script: {}/{file} ran", self.name),
- Err(e) => {
- let e = format!("{}/{file}: {e}", self.name);
+ Err(err) => {
+ let e = format!("{}/{file}: {err}", self.name);
error!("ui_script: {e}");
// A script error: it reaches the player through the Lua error handler.
- script.report_script_error(&e);
+ script.report_script_error(&e, &benilla_ui::script::lua_message(&err));
failures.push(e);
}
}
@@ -210,11 +210,11 @@ impl Addon {
script.report_load_failure(&e);
failures.push(e);
}
- for e in &report.errors {
+ for (e, heard) in report.raised() {
error!("ui_script({}/{file}): {e}", self.name);
// A script error, as in the Lua arm; `_ERRORMESSAGE`'s IsVisible guard shows only
// a burst's first, as in the reference.
- script.report_script_error(&format!("{}/{file}: {e}", self.name));
+ script.report_script_error(&format!("{}/{file}: {e}", self.name), heard);
failures.push(format!("{}/{file}: {e}", self.name));
}
info!(
@@ -2891,4 +2891,74 @@ mod tests {
);
let _ = std::fs::remove_dir_all(home.parent().unwrap());
}
+
+ /// The error handler hears what 1.12's `lua_pcall` error function hands it (`0x703b40`, from
+ /// `FS_DoBuffer 0x704ae0` for a file's top level and its syntax error, `0x704f10` for a
+ /// handler): the bare Lua message, with no frame, `OnLoad: `, addon or file word and no
+ /// traceback. The decorated text stays on the failures list and the host log.
+ #[test]
+ fn the_error_handler_hears_the_bare_message_of_every_load_failure() {
+ let tmp = std::env::temp_dir().join(format!("benilla-addon-bare-{}", std::process::id()));
+ let _ = std::fs::remove_dir_all(&tmp);
+ let addon = dir_addon(
+ &tmp,
+ "Bare",
+ "## Interface: 11200\ntop.lua\nsyntax.lua\nframe.xml\n",
+ );
+ std::fs::write(tmp.join("Bare/top.lua"), "local t = nil\nt.x = 1\n").unwrap();
+ std::fs::write(tmp.join("Bare/syntax.lua"), "x = = 1\n").unwrap();
+ std::fs::write(
+ tmp.join("Bare/frame.xml"),
+ r#"this:NoSuchMethod()
+"#,
+ )
+ .unwrap();
+ let mut script = UiScript::new().unwrap();
+ script.set_screen_size(1024.0, 768.0);
+ script
+ .run("Heard = {} seterrorhandler(function(m) table.insert(Heard, m) end)")
+ .unwrap();
+ let failures = addon.load(&script, &mut Status::default());
+ assert_eq!(failures.len(), 4, "{failures:#?}");
+ script.dispatch_script_errors_to_handler();
+ let heard: Vec = script.eval("return Heard").unwrap();
+ assert_eq!(heard.len(), 4, "{heard:#?}");
+ for m in &heard {
+ assert!(
+ !m.contains("runtime error")
+ && !m.contains("syntax error")
+ && !m.contains("stack traceback")
+ && !m.contains("OnLoad: ")
+ && !m.contains("Bare/")
+ && !m.contains('\n'),
+ "{m:?}"
+ );
+ }
+ assert!(
+ heard[0].ends_with("top.lua:2: attempt to index local `t' (a nil value)"),
+ "{:?}",
+ heard[0]
+ );
+ assert!(
+ heard[0].starts_with("Interface\\AddOns\\Bare\\top.lua"),
+ "{:?}",
+ heard[0]
+ );
+ assert!(
+ heard[1].contains("syntax.lua:1:") && heard[1].contains("unexpected symbol"),
+ "{:?}",
+ heard[1]
+ );
+ assert!(
+ heard[2].ends_with("attempt to call method `NoSuchMethod' (a nil value)"),
+ "{:?}",
+ heard[2]
+ );
+ assert!(
+ heard[3].ends_with("attempt to index local `u' (a nil value)"),
+ "{:?}",
+ heard[3]
+ );
+ let _ = std::fs::remove_dir_all(&tmp);
+ }
}
diff --git a/crates/benilla-ui/src/loader/backdrop.rs b/crates/benilla-ui/src/loader/backdrop.rs
index 9b22381a..b7d18fad 100644
--- a/crates/benilla-ui/src/loader/backdrop.rs
+++ b/crates/benilla-ui/src/loader/backdrop.rs
@@ -15,7 +15,7 @@ impl Loader<'_> {
let table = match self.build_backdrop_table(bd) {
Ok(t) => t,
Err(e) => {
- self.report.errors.push(format!("{dbg}: : {e}"));
+ self.report.fault(format!("{dbg}: : {e}"));
return;
}
};
diff --git a/crates/benilla-ui/src/loader/geometry.rs b/crates/benilla-ui/src/loader/geometry.rs
index 84d3a756..94925791 100644
--- a/crates/benilla-ui/src/loader/geometry.rs
+++ b/crates/benilla-ui/src/loader/geometry.rs
@@ -146,9 +146,7 @@ impl Loader<'_> {
let region: Table = match wrapper.call_method("CreateTitleRegion", ()) {
Ok(r) => r,
Err(e) => {
- self.report
- .errors
- .push(format!("{dbg}: CreateTitleRegion: {e}"));
+ self.report.fault(format!("{dbg}: CreateTitleRegion: {e}"));
return;
}
};
diff --git a/crates/benilla-ui/src/loader/mod.rs b/crates/benilla-ui/src/loader/mod.rs
index 238d4db6..2be04727 100644
--- a/crates/benilla-ui/src/loader/mod.rs
+++ b/crates/benilla-ui/src/loader/mod.rs
@@ -22,6 +22,33 @@ mod regions;
mod scripts;
mod widgets;
+impl LoadReport {
+ /// A load fault that is no script raise: the handler hears the row's own text.
+ pub(super) fn fault(&mut self, text: String) {
+ self.handler_errors.push(text.clone());
+ self.errors.push(text);
+ }
+
+ /// A script raise: the row is `text`, the handler hears the bare Lua message of `e`.
+ pub(super) fn raise(&mut self, text: String, e: &mlua::Error) {
+ self.handler_errors.push(crate::script::lua_message(e));
+ self.errors.push(text);
+ }
+
+ /// Each of [`Self::errors`] with what the Lua error handler is called with for it.
+ pub fn raised(&self) -> impl Iterator- {
+ debug_assert_eq!(
+ self.errors.len(),
+ self.handler_errors.len(),
+ "every error row is written through `fault` or `raise`"
+ );
+ self.errors
+ .iter()
+ .map(String::as_str)
+ .zip(self.handler_errors.iter().map(String::as_str))
+ }
+}
+
/// The outcome of a [`load`]; nothing aborts a load, as the reference logs and continues.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct LoadReport {
@@ -31,6 +58,11 @@ pub struct LoadReport {
/// Failures the reference raises on: a handler that fails to compile, a method call that
/// errors, a malformed included document.
pub errors: Vec,
+ /// What the Lua error handler is called with for each of [`Self::errors`], row for row
+ /// ([`Self::raised`]): the bare Lua message of a raise, as `lua_pcall` leaves it for the
+ /// reference's error function (`0x703b40`), and the row's own text for a fault that is no
+ /// script raise.
+ handler_errors: Vec,
/// `` and `