app: WOW_MAP_TRACE, because no instrument here can see the map

A player's report about how the map *looks* is the one question this side cannot answer. The
offline addon harness seats no renderer, the capture harness seats no UI or addons, and a scripted
login would kick the player off their own account — so a full audit (art present and correct,
catalog right, canvas geometry right, overlay tiling arithmetic right, addon setup complete) still
could not say why grom's Dun Morogh map came up magnified, clipped and grey.

Every number that decides the picture is readable from the VM. `WOW_MAP_TRACE=1` prints them once
per map change: the detail frame's rect and effective scale, then per tile its rect, the texture
path it holds, and the decoded size of that file. The file size is measured through a scratch
texture with no size of its own, because the tile declares 256x256 in `WorldMapFrame.xml` and would
answer that whatever the file holds — and this install's map art is a 512 upscale pack, which is
exactly the disagreement worth seeing.

Off unless the variable is set: it runs a Lua chunk per map change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
This commit is contained in:
2026-11-02 15:54:53 -07:00
co-authored by Claude Opus 5
parent 08689e7d14
commit e62327424f
+66
View File
@@ -64,6 +64,50 @@ struct DirectAreaEntry {
view: WorldMapZoneView,
}
/// `WOW_MAP_TRACE=1`, read once: the map-art trace below is off unless a player is being asked
/// to reproduce something, since it runs a Lua chunk per map change.
fn map_trace_on() -> bool {
static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
*ON.get_or_init(|| std::env::var_os("WOW_MAP_TRACE").is_some())
}
/// The chunk [`map_trace_on`] runs. Pure reads; `GetTexture` answers the path the stock repaint
/// set, and a texture created with no size of its own answers the file's own extent through the
/// host's size probe, which is why a scratch region is used to measure the file rather than the
/// tile (the tile declares 256x256 in `WorldMapFrame.xml` and would answer that whatever the file
/// holds).
const MAP_ART_TRACE: &str = r#"
local d = WorldMapDetailFrame
if not d then return "no WorldMapDetailFrame" end
local function num(v) return v and string.format("%.0f", v) or "?" end
local out = { string.format("detail %sx%s @(%s,%s) scale %.2f shown=%s",
num(d:GetWidth()), num(d:GetHeight()), num(d:GetLeft()), num(d:GetTop()),
d:GetEffectiveScale() or 1, tostring(d:IsShown())) }
-- One scratch texture, re-pointed per tile: with no size of its own it answers the file's extent.
if not BenillaMapTraceProbe then
BenillaMapTraceProbe = UIParent:CreateTexture(nil, "BACKGROUND")
BenillaMapTraceProbe:Hide()
end
local probe = BenillaMapTraceProbe
for i = 1, 12 do
local t = _G["WorldMapDetailTile" .. i]
if t then
local path = t:GetTexture()
local fw, fh = "?", "?"
if path then
probe:SetWidth(0) probe:SetHeight(0)
probe:SetTexture(path)
fw, fh = num(probe:GetWidth()), num(probe:GetHeight())
end
out[#out + 1] = string.format("%d %sx%s @(%s,%s) file %sx%s %s", i,
num(t:GetWidth()), num(t:GetHeight()), num(t:GetLeft()), num(t:GetTop()),
fw, fh, tostring(path))
end
end
out[#out + 1] = "overlays " .. tostring(GetNumMapOverlays and GetNumMapOverlays() or "?")
return table.concat(out, " | ")
"#;
fn zone_rect(a: &WorldMapArea) -> ZoneRect {
ZoneRect {
left: a.loc_left,
@@ -647,6 +691,9 @@ struct FeedMemos {
/// line prints on change rather than every frame — the selection is in the key because a
/// selection that stops following the player is the thing the line exists to show.
zone_logged: Option<(u32, u32, u32, u32, u32, u32, u32)>,
/// The map selection the `WOW_MAP_TRACE` art line last reported, so it prints once per map
/// rather than once per frame.
art_logged: Option<(u32, u32, Option<u32>)>,
/// The resolved pair last seen, so the watchdog in [`feed_world_map`] can tell the player
/// moving to a new zone from the map being browsed away from the player's.
last_resolved: Option<(u32, u32, Option<u32>)>,
@@ -981,6 +1028,25 @@ fn feed_world_map(
}
}
// `WOW_MAP_TRACE=1`: what the map's base art actually is, once per map change. A player's
// report about how the map *looks* cannot be answered from this side — the offline harness
// seats no renderer and the capture harness seats no UI — and every number that decides the
// picture is readable from the VM, so this prints them rather than guessing from a
// screenshot: the detail frame's own rect and effective scale, then per tile its rect, the
// texture path it holds and the decoded size of that file. A tile whose rect is not 256x256
// at a 256 pitch, or whose file is not the size the layout assumes, names the fault outright.
if map_trace_on() {
let key = script.world_map_selection();
let memo = memo.get(&script);
if memo.art_logged != Some(key) {
memo.art_logged = Some(key);
match script.eval::<String>(MAP_ART_TRACE) {
Ok(line) => info!("world map art: {line}"),
Err(e) => warn!("world map art: trace failed: {e}"),
}
}
}
// First world enter: the zone updater `0x494780` (`crate::area`), finding the cached zone id
// 0, selects the player's zone itself through `0x4a6650` and the setter `0x4a67a0` before any
// Lua asks. Once per VM, each login being a first enter.