capture: WOW_CAPTURE_ADDONS=1, to put an addon's own drawing in front of the renderer
A map report could be answered from neither harness: `addon_harness` runs the VM with no renderer, so it can say what a region is and never what it looks like, and the capture harness renders for real but is hermetic — `local_state::home` answers None under $WOW_CAPTURE, so no config, no saved variables and no addons, which is what makes a baseline comparable between machines. An addon that draws its own art was therefore unseeable without a server, and the only evidence was a player's screen recording. `WOW_CAPTURE_ADDONS=1` opts the player's addon folder back in for one run — `ui_script::addons::root`, the single narrow exception, with saved variables still out, so an addon runs on its shipped defaults. The world-map fixture will also ask a stitched-map addon for a view through its published API, retrying until the map is stitched (`WOW_CAPTURE_MAP_ZOOM`, `WOW_CAPTURE_MAP_AT`). `scripts/visual.sh` never sets any of it, so no baseline sees an addon. The first shot taken this way answered the open question: with OpenMapClassic loaded this client draws the stitched Dun Morogh map correctly, art, neighbours, labels, lore panel and player dot — so the map report is not the art pipeline. Also corrects a stale comment: the shot has never been Elwynn; continent 2, zone 10 is Dun Morogh. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
This commit is contained in:
@@ -719,10 +719,47 @@ pub(super) fn seed_ui_fixture(
|
||||
player.pos = benilla_assets::coords::wow_to_bevy(scenario.eye);
|
||||
// Alternating explore bits, so about half of each zone's overlays reveal.
|
||||
script.set_world_map_explored(vec![0x5555_5555; 64]);
|
||||
// The Elwynn zone map: continent 2, zone 10 in the alphabetical zone list.
|
||||
// Continent 2, zone 10 of the alphabetical zone list, which is Dun Morogh — the
|
||||
// comment here said Elwynn and the shot has never been Elwynn.
|
||||
if let Err(e) = script.run("ToggleWorldMap(); SetMapZoom(2, 10)") {
|
||||
warn!("capture: ui-worldmap seed failed to open the map: {e}");
|
||||
}
|
||||
// With `$WOW_CAPTURE_ADDONS=1` a map addon has taken the window over and draws its own
|
||||
// art, and the defects worth shooting are the ones that only appear zoomed in — a
|
||||
// stitched map at rest looks right and comes apart at a zone's own scale. So drive the
|
||||
// addon's published view: `WOW_CAPTURE_MAP_ZOOM=<zoom>` (1 is the whole world) and
|
||||
// `WOW_CAPTURE_MAP_AT=<u>,<v>` (normalized on the stitched world; the default is Dun
|
||||
// Morogh's centre, which is where grom's report came from). Nothing happens without
|
||||
// an addon publishing `OpenMapAPI`, so a stock capture is untouched.
|
||||
if let Ok(zoom) = std::env::var("WOW_CAPTURE_MAP_ZOOM") {
|
||||
let at = std::env::var("WOW_CAPTURE_MAP_AT").unwrap_or_default();
|
||||
let (u, v) = at
|
||||
.split_once(',')
|
||||
.and_then(|(u, v)| {
|
||||
Some((u.trim().parse::<f32>().ok()?, v.trim().parse().ok()?))
|
||||
})
|
||||
.unwrap_or((0.7655, 0.4942));
|
||||
let zoom: f32 = zoom.parse().unwrap_or(8.0);
|
||||
// Asked for on a retry rather than once: the fixture runs the moment the scene
|
||||
// settles, and a stitched map is not built until the addon has seen the window
|
||||
// open, so a single call lands before there is anything to drive and answers
|
||||
// false. The retry stops at the first success, because `Glide` animates and a
|
||||
// second call would restart the travel every frame. `$WOW_CAPTURE_AGE` is what
|
||||
// gives the art time to arrive after it.
|
||||
let chunk = format!(
|
||||
"local f = CreateFrame(\"Frame\") local n = 0 \
|
||||
f:SetScript(\"OnUpdate\", function() \
|
||||
n = n + 1 \
|
||||
if OpenMapAPI and OpenMapAPI.ShowWorld and OpenMapAPI.ShowWorld({u}, {v}, {zoom}) then \
|
||||
OpenMapCaptureView = n f:SetScript(\"OnUpdate\", nil) \
|
||||
elseif n > 600 then OpenMapCaptureView = -1 f:SetScript(\"OnUpdate\", nil) end \
|
||||
end)"
|
||||
);
|
||||
match script.run(&chunk) {
|
||||
Ok(()) => info!("capture: map addon view armed for ({u}, {v}) zoom {zoom}"),
|
||||
Err(e) => warn!("capture: driving the map addon's view: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
UiFixture::PartyInvite => {
|
||||
let Some(script) = script else {
|
||||
|
||||
@@ -25,6 +25,13 @@ pub(crate) fn home() -> Option<PathBuf> {
|
||||
if std::env::var_os("WOW_CAPTURE").is_some() {
|
||||
return None; // hermetic: captures neither read nor write player state
|
||||
}
|
||||
home_ignoring_capture()
|
||||
}
|
||||
|
||||
/// [`home`] without the capture gate, for the one caller that may opt back in:
|
||||
/// `ui_script::addons::root` under `$WOW_CAPTURE_ADDONS`. Nothing else should use it — the
|
||||
/// hermetic rule is what makes a baseline comparable between machines.
|
||||
pub(crate) fn home_ignoring_capture() -> Option<PathBuf> {
|
||||
if let Some(over) = std::env::var_os("BENILLA_HOME") {
|
||||
return Some(PathBuf::from(over));
|
||||
}
|
||||
|
||||
@@ -322,6 +322,19 @@ const ADDONS_PREFIX: &str = "Interface/AddOns/";
|
||||
/// `Interface\AddOns\`; benilla reads an install and does not live in one, so its addons live in
|
||||
/// its own state folder, and only there.
|
||||
pub(crate) fn root() -> Option<PathBuf> {
|
||||
// `WOW_CAPTURE_ADDONS=1`: put the player's own addons into a capture. A capture is otherwise
|
||||
// hermetic — `local_state::home` answers `None` under `$WOW_CAPTURE`, so a baseline is the
|
||||
// shipped interface and nothing else, and `scripts/visual.sh` never sets this. It exists
|
||||
// because an addon that draws its own art is otherwise unseeable without a server: the addon
|
||||
// harness has no renderer, and a screen recording from a player has no knobs. With it,
|
||||
// `WOW_CAPTURE=ui-worldmap WOW_CAPTURE_ADDONS=1` puts a map addon's own drawing in front of
|
||||
// the real renderer at a chosen zoom (`capture::fixtures`' `WOW_CAPTURE_MAP_ZOOM`).
|
||||
//
|
||||
// Saved variables still do not load (that is `local_state::home` too), so an addon runs on
|
||||
// its shipped defaults, not the player's settings.
|
||||
if std::env::var("WOW_CAPTURE_ADDONS").as_deref() == Ok("1") {
|
||||
return root_from(crate::local_state::home_ignoring_capture());
|
||||
}
|
||||
root_from(crate::local_state::home())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user