From 6306fed1cafebf2a73f152006b7fec9c1dbe0bac Mon Sep 17 00:00:00 2001 From: grom Date: Thu, 5 Nov 2026 10:50:43 -0700 Subject: [PATCH] A capture seats a player record, because the interface it loads is a player's MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A capture never logs in, so `record_from_roster` answers `None` and the record stayed empty — and the interface a capture loads is the one a player sees, where there is always a player. That is the harness diverging from the client, not the other way round: `Turtle_TransmogUI.lua:17` is `strlower(class)` over `local _, class = UnitClass("player")` at main-chunk scope, so the nil killed the file and raised an error popup over the middle of **every UI capture** — visible in the Interface options shot, and in nothing a player has ever seen (his own session logs nine `Turtle_TransmogUI` lines and no such error). Minimal and presence-only, the same record `manifest.rs` already seats for the offline loader and for the same stated reason. Where `$WOW_CAPTURE_SAVED` names a character it is that name, since the saved variables the capture just loaded are keyed on it. Verified: `WOW_CAPTURE=ui-options` goes from one raised chain file to 0 runtime errors, and the shot is a clean Interface page. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn --- crates/benilla-app/src/ui_script/lifecycle.rs | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/crates/benilla-app/src/ui_script/lifecycle.rs b/crates/benilla-app/src/ui_script/lifecycle.rs index 76649f59..6152014f 100644 --- a/crates/benilla-app/src/ui_script/lifecycle.rs +++ b/crates/benilla-app/src/ui_script/lifecycle.rs @@ -817,19 +817,29 @@ pub(crate) fn load_ingame_ui_on_world_entry(world: &mut World) { .get_resource::() .and_then(record_from_roster) .or_else(|| { - // The other half of the realm above, and for the same reason: `$WOW_CAPTURE_SAVED` - // names the **character** whose saved variables this capture has just loaded, so - // `UnitName("player")` has to answer that name or an addon's `[realm][player]` key - // looks up rows it was not handed. Race, class and sex are presence-only here, as - // `manifest.rs`'s own offline record documents; a fixture that seats a real one - // later overwrites this, and the addon walk has already run by then. - crate::local_state::capture_saved_identity().map(|(_, name)| { - benilla_ui::script::PlayerRecord { - name, - race: Some(("Human".into(), "Human".into())), - class: Some(("Warrior".into(), "WARRIOR".into())), - sex: 2, - } + // A capture never logs in, so it has no roster and no record — and the interface it + // loads is the one a player sees, where there is always a player. Leaving the record + // empty is the harness diverging from the client, not the other way round: a chain + // file reading `UnitClass("player")` at main-chunk scope then takes a nil into + // `strlower` and the whole file dies, which is a popup on every UI capture and + // nothing a player ever meets (`Turtle_TransmogUI.lua:17`). `manifest.rs` makes the + // same argument for its offline loader and seats the same minimal record. + // + // `$WOW_CAPTURE_SAVED` names the **character** whose saved variables this capture has + // just loaded, so where it is given it is the name: an addon's `[realm][player]` key + // has to find the rows it was handed (the realm above is the other half). Race, class + // and sex are presence-only, and a fixture that seats a real one later overwrites + // this — the addon walk has already run by then. + let name = match crate::local_state::capture_saved_identity() { + Some((_, character)) => character, + None if std::env::var_os("WOW_CAPTURE").is_some() => "Benilla".to_owned(), + None => return None, + }; + Some(benilla_ui::script::PlayerRecord { + name, + race: Some(("Human".into(), "Human".into())), + class: Some(("Warrior".into(), "WARRIOR".into())), + sex: 2, }) }) {