From 55b40b525787a68e28f8546a33d3acdcbaeeff6e Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 29 Aug 2026 17:23:05 +0700 Subject: [PATCH] names persist, the GM notice is a toast, and the target reads Dead MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - the name cache persists to `benilla-config/cache/.tsv`, because the reference's `namecache.wdb` / `petnamecache.wdb` do and that is why it never sees the races we lost every time — unstabling a pet raised `Usage: SetText("text")` off an unresolved `UnitName("pet")`; the header carries magic, format, build, locale and realm and any mismatch discards the file (no checksum, no TTL: eviction is explicit only, via `SMSG_INVALIDATE_PLAYER`, decoded now and never sent by vmangos), negatives persist too, written on a 10 s debounce and at exit; a stable list also seeds the pet-name cache from the names it already carries - `SMSG_NOTIFICATION` is the red UIErrorsFrame toast, not a chat line — `.gm on` printed twice because vmangos sends both a system message and a notification and both were rendered as chat; the stand-in dated from before the errors frame existed; `SMSG_AREA_TRIGGER_MESSAGE` moves with it on the yellow arm (its handler ends `xor edx,edx` — flag 0 — where the notification's is flag 1) - the target frame's dead text is the GlobalString `DEAD` = "Dead" resolved through `text=` like the target-of-target frame, not the key typed into `SetText` in caps, and it takes `UnitIsConnected` so a disconnected player does not read as dead; a shipped-script sweep now refuses any key-shaped literal handed to a text sink; and the Help window is one click deep — Home's button opens the ticket text box, Auto-Unstuck sits beside it, and a ticket files as category 0, which a GM's queue names "Unknown" (a guard added days earlier refused 0 on reasoning from the enum rather than from the function that reads it) Co-Authored-By: Claude Fable 5 --- crates/benilla-app/assets/ui/HelpFrame.xml | 1169 ++++------------- crates/benilla-app/assets/ui/UnitFrames.xml | 29 +- crates/benilla-app/src/lib.rs | 3 + crates/benilla-app/src/local_state.rs | 29 +- crates/benilla-app/src/name_persist.rs | 160 +++ crates/benilla-app/src/names.rs | 345 +++++ crates/benilla-app/src/net/apply.rs | 14 +- crates/benilla-app/src/net/apply/chat.rs | 108 +- crates/benilla-app/src/net/apply/death.rs | 8 +- crates/benilla-app/src/net/apply/names.rs | 13 + crates/benilla-app/src/net/apply/npc.rs | 23 + crates/benilla-app/src/ui_action/errors.rs | 32 +- crates/benilla-app/src/ui_action/feed.rs | 21 +- .../benilla-app/src/ui_action/feed_tests.rs | 80 +- crates/benilla-app/src/ui_action/mod.rs | 3 +- crates/benilla-app/src/ui_gm_ticket.rs | 78 +- crates/benilla-app/src/ui_items/feed.rs | 7 +- crates/benilla-app/src/ui_items/mod.rs | 7 - .../src/ui_script/help_frame_tests.rs | 201 +-- .../src/ui_script/shipped_xml_tests.rs | 108 ++ .../src/ui_script/unit_frame_tests.rs | 23 +- .../benilla-formats/src/gm_ticket_category.rs | 30 +- crates/benilla-protocol/src/events.rs | 3 + crates/benilla-protocol/src/events/decode.rs | 1 + .../src/messages/gm_ticket.rs | 7 +- .../benilla-protocol/src/messages/opcode.rs | 7 + .../benilla-protocol/src/messages/packet.rs | 7 + crates/benilla-protocol/src/messages/parse.rs | 3 + 28 files changed, 1399 insertions(+), 1120 deletions(-) create mode 100644 crates/benilla-app/src/name_persist.rs diff --git a/crates/benilla-app/assets/ui/HelpFrame.xml b/crates/benilla-app/assets/ui/HelpFrame.xml index 9850d9e9..e0a49792 100644 --- a/crates/benilla-app/assets/ui/HelpFrame.xml +++ b/crates/benilla-app/assets/ui/HelpFrame.xml @@ -17,17 +17,35 @@ MainMenuBarMicroButtons and StaticPopup entries reach by, and MicroMenu.xml is a straight transcription that now calls the first of them. Same posture as MicroMenu/FriendsFrame. + ── THE FLOW (decision 1687 — this is where we depart from the reference) ──────────────── + **Home → the ticket box, one click.** The reference is four pages deep — Home, then a + scrolling list of the ten `GMTicketCategory.dbc` rows, then a per-category blurb page + re-dressed nine ways, then finally the text box. The director's call (2026-08-29) is that + the middle two are a tax on somebody who already knows they want a GM, so they are **gone**, + not hidden: Home's own button goes straight to `HelpFrameOpenTicket`, and the client-side + Auto-Unstuck the category list used to bury three levels down sits beside it. + + Two pages remain, and `HELPFRAME_FRAMES` has exactly the two keys to match. + + The cost, stated where it is paid: with no picker there is no category to pick, so a ticket + this window CREATES carries **0** and a GM's queue reads "Unknown" — see + `TICKET_CATEGORY_UNCATEGORIZED` in the script block for why 0 and not one of the ten real + ids. An EDIT is the exception, and deliberately so: it sends back the category the server + last reported, because the update opcode overwrites the field unconditionally and sending 0 + there would undo a GM's re-filing every time the player fixes a typo. + ── THE ENGINE SEAM ───────────────────────────────────────────────────────────────────── Six bindings, all of them the reference's own declarations: `GetGMTicket()`, `NewGMTicket(category, text)`, `UpdateGMTicket(category, text)`, `DeleteGMTicket()`, - `GetGMStatus()` and `Stuck()`, plus `GetGMTicketCategories()` — a flat vararg list - `id1, name1, id2, name2, …` sourced from `GMTicketCategory.dbc`. That DBC ships exactly ten - rows and its ids ARE the wire category values (1 Stuck, 2 Behavior/Harassment, 3 Guild, - 4 Item, 5 Environmental, 6 Non-Quest/Creep, 7 Quest/Quest NPC, 8 Technical, - 9 Account/Billing, 10 Character) — which is WHY `HELPFRAME_FRAMES` and `GENERAL_HELPFRAME` - below are keyed 1..10: the keys are the DBC ids, so `HelpFrameGM_UpdateCategories`'s - `button.key = ` transcribes unchanged and a click carries the wire value straight into - `NewGMTicket`. + `GetGMStatus()` and `Stuck()`. + + The reference's seventh, `GetGMTicketCategories()` — a flat vararg list + `id1, name1, id2, name2, …` sourced from `GMTicketCategory.dbc`, ten rows whose ids ARE the + wire category values (1 Stuck, 2 Behavior/Harassment, 3 Guild, 4 Item, 5 Environmental, + 6 Non-Quest/Creep, 7 Quest/Quest NPC, 8 Technical, 9 Account/Billing, 10 Character) — is + **no longer called from this window**: the list it fed was the page 1687 removed. The + binding, the DBC reader and their tests all still ship, because they are the engine's half + and an addon may still ask; what went is this window's only caller. ── THE STRING TABLE ───────────────────────────────────────────────────────────────────── We ship none of Blizzard's text (AuctionFrame.xml's header carries the law): @@ -59,8 +77,10 @@ `UIDropDownMenu_Initialize(this, …)` in `_OnLoad` could never have run and `UIDropDownMenu_SetSelectedID(HelpFrameOpenTicketDropDown, …)` in `_OnClick` would index a nil global. `TICKET_TYPE1..4` (GlobalStrings.lua l.4239-4242) feed only - `_Initialize` and are therefore dead with it. The ticket's category comes from the GM - category button that was clicked instead (`HelpFrameOpenTicket.ticketType`). + `_Initialize` and are therefore dead with it. Still worth recording after 1687, because + it answers the obvious question in the other direction: there is no picker on the ticket + page for us to have kept — 1.12 never shipped one, and the category used to come from + the list page 1687 removed. 3 · **The `ScrollingEdit_OnUpdate()` on the ticket editor** (ref-xml l.700-702). `ScrollingEdit_OnTextChanged` and `_OnCursorChanged` both ship (ScrollTemplates.xml) and are wired below; `_OnUpdate` deliberately does not — it needs @@ -77,72 +97,103 @@ is the reference's own second way out. Its two labels ("Edit My Ticket" / "Abandon My Ticket") also sit in the engine's fixed 120px buttons — UiPanels.xml's own named narrowing. - - ── FRAMES NOTHING REACHES TODAY (transcribed anyway, so nobody "fixes" a non-bug) ─────── - `HelpFrameStuck` (ref-xml l.1301-1378) and `HelpFrameBug` (ref-xml l.1380-1392) are - unreachable in 1.12. Every caller of `HelpFrame_ShowFrame` in the whole extraction passes - "Home", "GMHome", "OpenTicket", or a GM category id 1..10 (grepped: ref-lua l.177/504/540, - ref-xml l.378/593/975/1105/1255/1285/1373, StaticPopup.lua l.351) — never "Stuck" and never - "Bug". `HELPFRAME_FRAMES["Stuck"]` therefore never fires, because ticket category 1 - ("Stuck") routes through `HELPFRAME_FRAMES[1]` to `HelpFrameGeneral`, which - `GENERAL_HELPFRAME[1]` dresses with the unstick button. There is no `HELPFRAME_FRAMES` - "Bug" key at all — `HelpFrameBug` is a bare divider frame with no route to it. Both are - transcribed because they are the reference's own layout and a later - `HelpFrame_ShowFrame("Stuck")` would want them intact. + 5 · **Five of the seven pages, REMOVED — not stubbed, not hidden, gone** (1687). Deleted + with the flow that reached them, each named here with the reference lines it was + transcribed from so a later reader can put one back from the reference rather than from + memory: `HelpFrameGM` and its ten `HelpFrameButton1..10` rows over + `HelpFrameGMScrollFrame` (ref-xml l.399-603) · `HelpFrameGeneral`, its ten bullets, two + bullet headings, end paragraph and two action buttons (ref-xml l.755-980) · + `HelpFrameHarassment` and its eight tips (ref-xml l.983-1299) · `HelpFrameStuck` + (ref-xml l.1301-1378) · `HelpFrameBug` (ref-xml l.1380-1392). With them went the Lua + that existed only to drive them — `GENERAL_HELPFRAME` (the nine dressings, ref-lua + l.29-158), `HelpFrame_SetupGeneralFrame` (l.231-349), `HelpFrameButton_OnClick` + (l.503-505), `HelpFrameGM_Update` (l.507-509), `HelpFrameGM_UpdateCategories` + (l.511-534), `HelpFrameGeneralButton_OnClick` (l.536-542), our own + `HelpFrameHarassment_OnLoad` (the reference fills those eight strings from eight XML + OnLoad bodies instead), and the four constants none of them left a reader for + (`HELPFRAME_BULLET_SPACING`, `HELPFRAME_SECTION_SPACING`, `MAX_GENERAL_BULLETS`, + `NUM_GM_CATEGORIES_TO_DISPLAY`) — and three of the four templates, + `HelpFrameBulletPointTemplate`, `HelpFrameHomeButtonTemplate` and + `GMCategoryButtonTemplate`. Those three are + **inheritable surface** (1254's mirror: a name the reference owns must not be hidden), + so they were checked before they went rather than after: nothing else in the whole 1.12 + extraction inherits any of the three, and nothing in the installed addon corpus names + one. `HelpFrameDividerTemplate` has a live customer — the ticket page's own divider — + and stays. + The last two, `HelpFrameStuck` and `HelpFrameBug`, were already unreachable *in the + reference* and were transcribed anyway so nobody would "fix" a non-bug; that reason + expired with the routes around them. Nothing in the whole extraction passes "Stuck" or + "Bug" to `HelpFrame_ShowFrame` (grepped: ref-lua l.177/504/540, ref-xml + l.378/593/975/1105/1255/1285/1373, StaticPopup.lua l.351), so the pages could only ever + have been reached by an addon calling a key the reference itself never used. ── DELIBERATE DIVERGENCES (each stated where it stands, listed here so they are countable) ─ - 1 · The GM category list is the house FAUX scroll kit (ScrollTemplates.xml, decisions - 0247/0250/0251): a plain ``, not the - reference's ``, so its `` body (ref-xml l.564-566) has - nothing to fire and is replaced by an `updateFunc`. The three loose trough textures the - reference hangs beside each bar (ref-xml l.517-560 and l.642-685) become the kit's - shared `BenillaScrollTroughTemplate`, whose seat law was derived from exactly these - offsets. ReputationFrame.xml's precedent. - 2 · The ticket editor's scroll frame IS a real `UIPanelScrollFrameTemplate`, so its bar is + Renumbered by 1687: three entries went with the pages they described (the GM list's faux + scroll kit, the Lua-5.0 vararg spelling — this file has no varargs left — and the dropped + `message("Not enough bullets! Tell Derek")` developer assert). Numbers below are the ones + the inline `(header, divergence N)` cites use. + 1 · The ticket editor's scroll frame IS a real `UIPanelScrollFrameTemplate`, so its bar is always up and `BenillaScrollBar_SetShown` (the only thing that seats a trough) never runs for it. Its trough is seated explicitly in the frame's OnLoad through the kit's own - public `BenillaScrollTrough_Seat`. GuildInfoFrameScrollFrame (FriendsFrame.xml) simply - has no trough; that is an omission, not a law, and the reference draws the channel here. - 3 · "Fit the button to its label" reads the Button's own `GetTextWidth()` (which forwards to + public `BenillaScrollTrough_Seat` (the house faux/real scroll kit, ScrollTemplates.xml, + decisions 0247/0250/0251; it replaces the three loose trough textures the reference + hangs beside the bar at ref-xml l.642-685). GuildInfoFrameScrollFrame (FriendsFrame.xml) + simply has no trough; that is an omission, not a law, and the reference draws the + channel here. + 2 · "Fit the button to its label" reads the Button's own `GetTextWidth()` (which forwards to the label's unwrapped `GetStringWidth`, script/button.rs) rather than the reference's `…Text:GetWidth()`. Same number for a non-wrapping label; the laid-out width is the one that feeds itself its own previous output (decision 0997's measured feedback loop). The fit also moves from the button's `` to its page's ``, because a measure taken before the atlas has seen the string answers 0 and nothing would re-run it. - 4 · `TicketStatusFrame_OnEvent`'s "no ticket" leg re-anchors `TemporaryEnchantFrame` back to + 3 · `TicketStatusFrame_OnEvent`'s "no ticket" leg re-anchors `TemporaryEnchantFrame` back to TOPRIGHT **(-175, -13)**, not the reference's (-180, -13) (ref-lua l.498). -175 is what BuffFrame.xml (and ref-BuffFrame.xml l.390-394) actually declares, so the reference nudges the frame 5px left after every ticket closes and never puts it back. A typo, not a mechanism — MicroMenu.xml's precedent for not reproducing one. - 5 · `HELP_TICKET`'s OnCancel drops the reference's `local currentFrame = this:GetParent()` / + 4 · `HELP_TICKET`'s OnCancel drops the reference's `local currentFrame = this:GetParent()` / `dialogFrame.data = currentFrame` pair (StaticPopup.lua l.357-361). Our engine calls `OnCancel(data, reason)` as a plain function with no `this`, and nothing ever reads that data — `HELP_TICKET_ABANDON_CONFIRM`'s OnAccept calls `DeleteGMTicket()` with no argument. Keeping it would be two lines that can only throw. - 6 · The reference's Lua-5.0 `arg`/`arg.n` vararg table is written the 5.1-native way, - `select("#", ...)` / `select(i, ...)` (this VM is lua51 — TalentFrame.xml's precedent), - and its `for k, v in do` generic-for becomes `ipairs`. - 7 · `elapsedTime = 0` (ref-lua l.164) is dropped: a global assigned once and read nowhere in + 5 · `elapsedTime = 0` (ref-lua l.164) is dropped: a global assigned once and read nowhere in the whole extraction. `refreshTime` (ref-lua l.495/548) becomes a file-local rather than a leaked global — it is read by exactly one function in this file. - 8 · `message("Not enough bullets! Tell Derek")` (ref-lua l.260/284) is dropped. It is a - Blizzard developer assert naming a Blizzard engineer, not UI text, and the branch is - unreachable: `MAX_GENERAL_BULLETS` is 10 and the longest configured run is 7. - 9 · `HelpFrameOpenTicket_OnEvent`'s `ticketFrameHeight`/`twoLineHeight` locals are dropped: + 6 · `HelpFrameOpenTicket_OnEvent`'s `ticketFrameHeight`/`twoLineHeight` locals are dropped: assigned on three legs and read on none, even in the reference — the toast sizes itself from its two measured FontStrings. ReputationFrame.xml's precedent. + 7 · **The Home page's button row is OURS** (1687), and it is the one place in this window + where the geometry is not a measurement off the reference. `HelpFrameHomeIssues` keeps + its reference size (250x22) and its reference vertical hang (-40 below the last text + line), but its anchor point moves TOP → TOPRIGHT at x -6 so a second button can sit + beside it: the pair now straddles the text column's centre line instead of one button + sitting on it. The -6/+13 arithmetic is not invented — it is the reference's own answer + to "two buttons, centred as a pair, under a block of text", lifted from + StaticPopupTemplate (ref StaticPopup.xml, our UiPanels.xml transcription of it). Both + buttons are fitted to their own labels from the page's OnShow, so each grows *away* + from the centre and the gutter between them is fixed however long the labels get. + The consequence for the geometry oracle is stated rather than hidden: `HelpFrameHomeIssues` + no longer matches the reference's anchor offset, so it is a listed deviation in + `help_frame_tests`, not a silent one. + 8 · **The second Home button is a name we invented, so it wears the prefix** — + `BenillaHelpFrameHomeUnstick` (1254's rule, and OptionsFrameTemplates.xml's phrasing of + it: a name the reference owns must not be hidden, a name we invent must not collide). + 1.12 has no such frame, so the bare `HelpFrameHomeUnstick` would have claimed a + reference provenance it does not have and added a global to the 1.12 surface under a + reference-shaped name. Its LABEL is the reference's own `STUCK_BUTTON_TEXT` + (GlobalStrings.lua l.4206, "Auto-Unstuck") and its click is `HelpFrameUnstick_OnClick`, + both unchanged — only the seat is new. - ── TWO FIDELITY NOTES A LATER READER WOULD OTHERWISE MISREAD AS BUGS ──────────────────── + ── A FIDELITY NOTE A LATER READER WOULD OTHERWISE MISREAD AS A BUG ────────────────────── · `HelpFrame_OnEvent`'s `UPDATE_GM_STATUS` arm branches on arg1 == 1 (queue up) and arg1 == -1 (queue down AND pop HELP_TICKET_QUEUE_DISABLED). Against the vmangos server benilla talks to, `SMSG_GMTICKETSYSTEMSTATUS` only ever carries 0 or 1 — so the -1 leg is transcribed faithfully and is simply unreachable here today. - · `CATEGORY_TO_NOT_DISPLAY` (ref-lua l.305) suppresses one category's action button. Its - ONLY setter in the whole extraction is `Localization.lua`'s `LocalizeFrames()` (l.23, - `= 9`) — an enGB-only adjustment that hides the Billing & Account button because EU - billing went somewhere else. benilla ships no Localization.lua (GameTime.xml's header - makes the same note about `TwentyFourHourTime`), so the global is nil, the guard never - fires, and category 9 keeps its button. That is the enUS behaviour. + + (The second note this section used to carry went with 1687: `CATEGORY_TO_NOT_DISPLAY` + (ref-lua l.305) suppressed one category page's action button, and there are no category + pages. Its only setter in the whole extraction was `Localization.lua`'s enGB + `LocalizeFrames()`, which benilla does not ship, so it never fired here either.) --> - - - - - - - - - - - - - - - - + + The horizontal rule: one 256x64 sheet cut into three bands stacked in v (0..0.3125 left + cap, 0.3125..0.625 middle, 0.625..0.9375 right cap) and laid end to end. The middle's +1 + y-offset is the reference's own. --> @@ -925,46 +652,11 @@ - - - - - - + @@ -1094,15 +787,43 @@ - - + + - HelpFrameButton_FitToText(HelpFrameHomeIssues) + + HelpFrameButton_FitToText(HelpFrameHomeIssues) + HelpFrameButton_FitToText(BenillaHelpFrameHomeUnstick) + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ScrollFrame_OnLoad() - self.updateFunc = HelpFrameGM_Update - - - - - - - - - HelpFrameGM_Update() - - - - @@ -1242,7 +880,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - HelpFrameHarassment_OnLoad() - - - - - - - - - - - - - - - - - - - - - - - - - - HelpFrameButton_FitToText(HelpFrameStuckStuck) - - - - - - - - - - - HelpFrame_OnLoad(self) diff --git a/crates/benilla-app/assets/ui/UnitFrames.xml b/crates/benilla-app/assets/ui/UnitFrames.xml index 9d62e750..92261ae5 100644 --- a/crates/benilla-app/assets/ui/UnitFrames.xml +++ b/crates/benilla-app/assets/ui/UnitFrames.xml @@ -805,14 +805,22 @@ TargetFrame_CheckClassification() end - -- DEAD text (target only): shown at zero health (ref TargetFrame_CheckDead). Blanked - -- via SetText when alive — a site that predates region Show/Hide (region.rs has the - -- real pair now, the level slot above uses it); equivalent either way. + -- DEAD text (target only), ref TargetFrame_CheckDead (l.220-228) whole: Show/Hide a + -- region whose word the XML already carries. This site used to SetText("DEAD") a + -- LITERAL and blank it when alive, which put the KEY on screen in caps — `DEAD` is a + -- GlobalStrings key (l.898 = "Dead"), not the string, and typing a key as display text + -- is the "CREATE_MACROS" failure of 0983/0991 again. Nothing here names the word now: + -- it comes from the FontString's `text="DEAD"` attribute, which the loader resolves + -- through the global (loader/mod.rs resolve_text), locale and all. + -- + -- `UnitIsConnected` is the reference's own second term and is what tells dead from + -- LINK-dead: a disconnected player reads 0 health here and is not dead. The ToT twin + -- (TargetofTarget_CheckDead) has carried it since it was written; this leg had not. if frame.deadText then - if UnitHealth(unit) <= 0 then - frame.deadText:SetText("DEAD") + if UnitHealth(unit) <= 0 and UnitIsConnected(unit) then + frame.deadText:Show() else - frame.deadText:SetText("") + frame.deadText:Hide() end end @@ -1599,8 +1607,13 @@ - - + +