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:
@@ -137,13 +137,26 @@ impl AddonsPanel {
|
||||
self.list.len().saturating_sub(MAX_ROWS)
|
||||
}
|
||||
|
||||
/// **Why a row will not load, as the reference's `reason` token** — the string the status
|
||||
/// column shows and the colour rule keys on.
|
||||
/// **Why a row will not load** — the string the status column shows and the colour rule keys on.
|
||||
///
|
||||
/// `DEP_DISABLED`/`DEP_MISSING` come from the dependency walk; `DISABLED` from the checkbox;
|
||||
/// `INCOMPATIBLE` from `## Interface`. Reported and never enforced for the last one (1191 §6),
|
||||
/// which is why an out-of-date addon still shows gold-if-enabled: the status column says so
|
||||
/// and the client still loads it, and those two facts must not contradict each other.
|
||||
/// These are the reference's status LABELS, not its tokens, because the glue side has no VM to
|
||||
/// splice `getglobal("ADDON_"..reason)` against (1197 §3). Each is the exact value its token
|
||||
/// carries in the shipped `GlobalStrings.lua`, and
|
||||
/// `the_status_labels_are_the_reference_globalstrings_values` pins all four so they cannot
|
||||
/// drift from the ESC-menu twin, which DOES go through the globals:
|
||||
///
|
||||
/// | here | token | `GlobalStrings.lua` |
|
||||
/// |---|---|---|
|
||||
/// | `"Disabled"` | `ADDON_DISABLED` | `"Disabled"` |
|
||||
/// | `"Dependency missing"` | `ADDON_DEP_MISSING` | `"Dependency missing"` |
|
||||
/// | `"Dependency disabled"` | `ADDON_DEP_DISABLED` | `"Dependency disabled"` |
|
||||
/// | `"Out of date"` | `ADDON_INTERFACE_VERSION` | `"Out of date"` |
|
||||
///
|
||||
/// The last row is why this comment was rewritten: it used to name the token `INCOMPATIBLE`,
|
||||
/// **which does not exist** — the reference has no `ADDON_INCOMPATIBLE`, and a reader who
|
||||
/// trusted the name would have spliced a nil global. Out-of-date is reported and never enforced
|
||||
/// (1191 §6), which is why such a row still shows gold-if-enabled: the status column says so and
|
||||
/// the client still loads it, and those two facts must not contradict each other.
|
||||
fn reason(&self, i: usize) -> Option<&'static str> {
|
||||
let addon = self.list.get(i)?;
|
||||
if !self.staged.get(i).copied().unwrap_or(addon.enabled) {
|
||||
@@ -646,6 +659,46 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// **Every label this panel shows is the value its reference token carries**, so the glue
|
||||
/// screen and the ESC-menu screen cannot say different things about the same addon.
|
||||
///
|
||||
/// The glue side has no VM to splice `getglobal("ADDON_"..reason)` against (1197 §3), so it
|
||||
/// hardcodes the English. That is fine and it is also exactly how the two screens drift: the
|
||||
/// twin resolves through `GlobalStrings.lua` and this one does not, so nothing but a test
|
||||
/// connects them. The values below are quoted from the shipped 1.12 `GlobalStrings.lua`
|
||||
/// (l.16-31, the `ADDON_*` block) — which the client itself loads at boot.
|
||||
///
|
||||
/// This test exists because the comment on [`AddonsPanel::reason`] used to name the token
|
||||
/// `INCOMPATIBLE`, and **there is no `ADDON_INCOMPATIBLE`**; the real one is
|
||||
/// `ADDON_INTERFACE_VERSION`. A wrong token name is invisible until somebody splices it.
|
||||
#[test]
|
||||
fn the_status_labels_are_the_reference_globalstrings_values() {
|
||||
let mut p = panel(vec![
|
||||
addon("Off", &[], &[11200]),
|
||||
addon("Lib", &[], &[11200]),
|
||||
addon("Needs", &["Lib"], &[11200]),
|
||||
addon("Orphan", &["Nowhere"], &[11200]),
|
||||
addon("Old", &[], &[11100]),
|
||||
]);
|
||||
p.staged[0] = false;
|
||||
p.staged[1] = false;
|
||||
|
||||
// (label, the token it must equal, the value GlobalStrings.lua gives that token)
|
||||
for (got, token, want) in [
|
||||
(p.reason(0), "ADDON_DISABLED", "Disabled"),
|
||||
(p.reason(2), "ADDON_DEP_DISABLED", "Dependency disabled"),
|
||||
(p.reason(3), "ADDON_DEP_MISSING", "Dependency missing"),
|
||||
(p.reason(4), "ADDON_INTERFACE_VERSION", "Out of date"),
|
||||
] {
|
||||
assert_eq!(
|
||||
got,
|
||||
Some(want),
|
||||
"this label must be {token}'s value verbatim, or the ESC-menu twin — which \
|
||||
resolves the token through GlobalStrings — will show a different string"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The status column's rule, which is also the title-colour rule — the reference's own
|
||||
/// `reason` token set, in its own precedence.
|
||||
///
|
||||
|
||||
@@ -160,6 +160,20 @@ pub(crate) struct Roster {
|
||||
}
|
||||
|
||||
impl Roster {
|
||||
/// A roster with a pick already in flight — the state world entry runs in.
|
||||
///
|
||||
/// `#[cfg(test)]` and `pub(crate)` because the fields are `pub(super)`: a test outside this
|
||||
/// module cannot build one, and `ui_script`'s does exactly that to drive
|
||||
/// `seat_from_roster` over a real row rather than a copy of its logic.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn with_pending_pick(chars: Vec<Character>, guid: u64) -> Self {
|
||||
Self {
|
||||
chars,
|
||||
pending_pick: Some(guid),
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Note a character the create screen just made, and select its row.
|
||||
///
|
||||
/// **The consume happens here, not on the next roster message** (B119): the IO thread
|
||||
@@ -219,7 +233,13 @@ impl Roster {
|
||||
}
|
||||
|
||||
/// The roster row for the pick in flight.
|
||||
fn pending_row(&self) -> Option<&Character> {
|
||||
///
|
||||
/// `pub(crate)` because this row is the ONLY description of the character that exists during
|
||||
/// world entry: `Connected` flips us `InWorld` a whole server round-trip before the self
|
||||
/// descriptor streams in, and the in-game UI — plus every addon's file scope — materializes
|
||||
/// inside that window. `ui_script::load_ingame_ui_on_world_entry` seats
|
||||
/// `UnitName("player")` from here for exactly that reason.
|
||||
pub(crate) fn pending_row(&self) -> Option<&Character> {
|
||||
self.pending_pick
|
||||
.and_then(|g| self.chars.iter().find(|c| c.guid == g))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user