addons come alive: Bagnon draws, hovers and clicks

- Lua 5.0's generic-for and nested long strings are restored in a forked VM — 60 corpus addons stopped dying on one deleted opcode
- a day of demand-driven carving takes the 218-addon corpus from 24 addons surviving a session start to 114, with 144 loading without a single error
- Bagnon draws a real bag and answers real gestures — hover tooltip, pickup, use, drag — because our frames wear the reference's names, and the reference's own ContainerFrame.lua runs off the player's install instead of being transcribed
- the physics broad phase leaves: nothing in the client reads a contact, and it was measured at up to 41% of main-thread samples in real sessions

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Sam
2026-08-11 18:19:08 +07:00
co-authored by Claude Fable 5
parent b5bc569156
commit 251daa14c5
104 changed files with 14356 additions and 2306 deletions
+33 -1
View File
@@ -64,6 +64,36 @@ impl Loader<'_> {
/// Compile a handler element into `function(self, ...) <body> end`, or resolve its
/// `function="Global"` reference. A syntax error in the body is recorded as an error (and the
/// handler dropped), never a panic.
///
/// ## `self` falls back to `this`, and that one clause is a whole class of addon bug
///
/// The 1.12 contract for an XML script body is that it takes **no arguments**: the frame
/// arrives as the `this` global, which [`crate::script::event::invoke_with_globals`] sets
/// around every dispatch. This engine also passes the modern `(self, event, …)` positionals —
/// convenient, and what Era-era addons expect — and our own FrameXML is written against
/// *that* spelling (`<OnEnter>BenillaBagToggle_OnEnter(self)</OnEnter>`).
///
/// The two spellings agree right up until **an addon captures a script and calls it back**,
/// which is the standard 1.12 hook idiom and the reason `GetScript` exists:
///
/// ```lua
/// bMainBag_OnEnter = MainMenuBarBackpackButton:GetScript("OnEnter") -- Bagnon.lua:61
/// MainMenuBarBackpackButton:SetScript("OnEnter", BagnonBlizMainBag_OnEnter)
/// function BagnonBlizMainBag_OnEnter()
/// …
/// bMainBag_OnEnter() -- l.87: no arguments. The reference's contract.
/// end
/// ```
///
/// The re-entry is perfectly legal, `this` is correctly set (Bagnon's own body reads
/// `this:GetID()` one line above), and our compiled body still took `self` from argument 1 —
/// which was nil. The director saw it as `bad argument #2: error converting Lua nil to table`
/// out of `GameTooltip:SetOwner`, from a handler that works perfectly when the engine calls it.
///
/// So the fallback lives **here**, once, rather than as `(self or this)` sprinkled through
/// ~20 handler bodies: any XML body in any file — ours or an addon's — now works under both
/// callers. It is written on line 1 of the wrapper on purpose, so `{body}` still starts at
/// line 2 and every traceback line number keeps pointing at the source the author wrote.
pub(super) fn compile_handler(
&mut self,
handler: &Element,
@@ -72,7 +102,9 @@ impl Loader<'_> {
) -> Option<Function> {
let body = handler.body.trim();
if !body.is_empty() {
let src = format!("return function(self, ...)\n{body}\nend");
let src = format!(
"return function(self, ...) if self == nil then self = this end\n{body}\nend"
);
match self
.lua()
.load(&src)