superwow: RAW_COMBATLOG and CombatLogAdd, the only two names his addons call
Of SuperWoW's whole surface these were the two names grom's corpus calls
that this client lacked (`scripts/dll-surface.py --wanted`), and the first
is load-bearing out of proportion to its size: `SuperCleveRoidMacros`
funnels FIVE subsystems through one `RAW_COMBATLOG` frame — immunity and
resist learning, hidden-CC detection, debuff-duration learning, and the
reactive-proc window that lights Overpower and Revenge. Its own comment
says why the last of those can come from nowhere else: "SPELL_GO only
provides hit/miss COUNTS, not miss TYPES".
`route` is the fire site: the one funnel every combat line already passes
on its way to a window and to WoWCombatLog.txt. `arg1` is the printed
sentence; `arg2` is the same sentence with each unit name spelled as its
GUID, composed a second time through the same `compose_line` so the two
can only differ in the name slots. Fired before the `CHAT_MSG_*`, which is
what "raw" means upstream. Both the fire and the `arg2` composition are
gated on `has_listener` — a raid's combat log is tens of lines a second.
`CombatLogAdd` is also BigWigs' SuperWoW detection (`if CombatLogAdd
then`), so defining it turns that module's path on.
Three corrections came out of reading the DLL's own usage strings, which
it publishes beside the names — the arity and argument meaning of every
remaining SuperWoW function is now measured rather than inferred, and
`docs/ADDON_COMPAT.md` carries the manifest:
- `CombatLogAdd("text" [, addToRawLog])` has a SECOND argument. Nothing
in the corpus passes it; it would have been missed.
- `SUPERWOW_COMBAT_LOG_EVENT` does not exist. The DLL registers exactly
three events, and that name is in no part of its string pool.
- `IsIndoors` is capital-I beside `IsSwimming`/`IsMounted`, not the
lowercase `isIndoors` two files guessed at — all three are already live
off one feed, so that clause of the "blocked" row was already answered.
- The detection pair is `SUPERWOW_STRING="SuperWoW 2.2 by Balake"` and
`SUPERWOW_VERSION="2.2"`, both quoted. Ours had the string without its
author clause and the version as a number.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
This commit is contained in:
+51
-2
@@ -33,10 +33,12 @@ nothing behind it.
|
||||
| Nameplate `frame:GetName(1)` → GUID | done | `script/object/frame_state.rs`'s shared `GetName`, `NamePlates::guid_for_frame` (`script/nameplate.rs`) |
|
||||
| `GetWeaponEnchantInfo([unit])` + `GetWeaponEnchantID` | todo, blocked | needs per-unit weapon-enchant tracking (today only `"player"`'s is fed at all) and no item-template data distinguishing an on-use charge slot from an equip/proc slot — would need new app-side state, not a binding tweak |
|
||||
| `GetContainerItemInfo` charges (negative stack count) | todo, blocked | the raw signed field exists (`ObjectFields::item_spell_charges`, benilla-protocol), but nothing in `benilla-formats`' item template says *which* of an item's five spell-trigger slots (if any) is a consumable charge rather than an equip/proc effect — exposing it without that would be a guess, not a port |
|
||||
| New functions (`SpellInfo`, `TrackUnit`/`UntrackUnit`, `SetMouseoverUnit`, `Clickthrough`, `SetAutoloot`, `UnitNameplate`, `CanLootUnit`, `CombatLogAdd`, Import/Export/AppendText, map/world coord conversions, `IsSwimming`/`IsMounted`/`isIndoors`/`GetSpeed`, mail/quest-link getters) | todo, blocked | each needs new app-side state, not a binding tweak: `IsSwimming`/`IsMounted`/`isIndoors`/`GetSpeed` need a self-movement snapshot (`move_flags() & SWIMMING`, `unit_mount_display_id()`, `area.rs`'s `indoor` bool, and a `MoveSpeeds` component — none currently reach a `feed_*` system that can push them, and `ui_unit.rs`'s `feed_units` is already at Bevy's 16-param ceiling); **`SpellInfo` is now done** — `compat/superwow.rs`, over `script/spellbook.rs`'s `spell_display`: the player's book first, then the host's `Spell.dbc` probe ([`SpellProbe`], installed by `ui_script::lifecycle::install_spell_probe` on every VM build), so it reaches every shipped spell as the real mod does. The range pair stays nil — `SpellRange.dbc` is not on the probe; the rest (`TrackUnit`, `SetMouseoverUnit`, `Clickthrough`, `SetAutoloot`, `CanLootUnit`, `UnitNameplate`, file I/O, map/world coords, quest links) are each plausible but unstarted. **The two mail link getters are now done**, built for ClassicAPI's own `Mail` namespace (`compat/classic_api/merchant_frame.rs`, see its row below) — SuperWoW specifies the same `GetSendMailItemLink`/`GetInboxItemLink`, so that half of this row is closed |
|
||||
| New functions (`SpellInfo`, `TrackUnit`/`UntrackUnit`, `SetMouseoverUnit`, `Clickthrough`, `SetAutoloot`, `UnitNameplate`, `CanLootUnit`, Import/Export/AppendText, map/world coord conversions, `IsSwimming`/`IsMounted`/`isIndoors`/`GetSpeed`, mail/quest-link getters) | todo, blocked | each needs new app-side state, not a binding tweak: `IsSwimming`/`IsMounted`/`isIndoors`/`GetSpeed` need a self-movement snapshot (`move_flags() & SWIMMING`, `unit_mount_display_id()`, `area.rs`'s `indoor` bool, and a `MoveSpeeds` component — none currently reach a `feed_*` system that can push them, and `ui_unit.rs`'s `feed_units` is already at Bevy's 16-param ceiling); **`SpellInfo` is now done** — `compat/superwow.rs`, over `script/spellbook.rs`'s `spell_display`: the player's book first, then the host's `Spell.dbc` probe ([`SpellProbe`], installed by `ui_script::lifecycle::install_spell_probe` on every VM build), so it reaches every shipped spell as the real mod does. The range pair stays nil — `SpellRange.dbc` is not on the probe; the rest (`TrackUnit`, `SetMouseoverUnit`, `Clickthrough`, `SetAutoloot`, `CanLootUnit`, `UnitNameplate`, file I/O, map/world coords, quest links) are each plausible but unstarted. **`CombatLogAdd` is done** and has its own row below. **The two mail link getters are now done**, built for ClassicAPI's own `Mail` namespace (`compat/classic_api/merchant_frame.rs`, see its row below) — SuperWoW specifies the same `GetSendMailItemLink`/`GetInboxItemLink`, so that half of this row is closed |
|
||||
| `UnitPosition(unitid)`, `CursorPosition()` | done | `UnitState.position` (`script/unit/mod.rs`), fed by `Transform` from `ui_unit.rs`'s `feed_units` (player/target/targettarget/npc) and `ui_tooltip.rs`'s `drive_mouseover_tooltip` (mouseover); functions in `compat/superwow.rs`. Party/raid/pet aren't wired (their own `snapshot()` call sites in `ui_party/feed.rs`/`ui_pet/unit.rs` pass `None` — each needs its own `Entity`→`Transform` lookup threaded through, not attempted this pass). Reading `Transform` broadly past the usual one-frame-stale tolerance raised `game_plugins.rs`'s `UPDATE_ACTIONABLE_CEILING` by 30 (cited there) — `Transform` is written by enough movement/animation/physics systems that `With<Guid>` alone doesn't shrink the ambiguity count |
|
||||
| `UNIT_CASTEVENT` | done | `crates/benilla-app/src/superwow_events.rs`: a `CastEventFeed` queue the cast handlers push into, drained once per tick by one VM-holding system that fires the event — the pattern `ui_mirror.rs` already uses, and the only way across, since `spell/net.rs`'s `Lifecycle`/`Scene` bundles deliberately hold no `UiScript` (`game_plugins.rs`'s `every_vm_holder_in_update_declares_its_side_of_the_tick` gates that). Edges wired: `START` from `SMSG_SPELL_START` with its wire cast time as `arg5` (an instant raises none — its only edge is `CAST`, which is what the wiki's own START/CAST split means), `CAST` from `SMSG_SPELL_GO`, `FAIL` from `SMSG_SPELL_FAILED_OTHER`, `CHANNEL` from `MSG_CHANNEL_START` with its duration. GUIDs go out in the same 16-hex-digit spelling every other `compat/` surface uses, so `arg1`/`arg2` compare directly against a `UnitExists`/`UnitGUID` return. Not wired: `MAINHAND`/`OFFHAND` — benilla's melee-swing path raises no cast edge at all, so there is nothing to forward. `MSG_CHANNEL_START` names no caster (1.12 sends it for the local player's own channel only), so `CHANNEL`'s `arg1` is self and its `arg2` is 0 |
|
||||
| `CREATE_CHATBUBBLE`, `RAW_COMBATLOG`, `SUPERWOW_COMBAT_LOG_EVENT` | todo | `CREATE_CHATBUBBLE` needs the chat-bubble creation site in `chat_bubble.rs` (its `push`/`drive_bubbles`); now that `superwow_events.rs` exists with the queue-plus-drain pattern established, this is a small addition rather than new architecture. `RAW_COMBATLOG`'s funnel is `ui_chat/feed.rs`'s `ChatLog::push_combat`, where (almost) every combat line lands as a `combat::PendingCombat` from two call sites in `combat_log/chat.rs` — but that's a *queued* value, not rendered text, and the sentence `arg2` wants is built later at drain time, which this pass didn't trace into. Firing at `push_combat` with the structured fields in place of `arg2` would be a different (arguably more useful, but non-conforming) shape than the wiki's "text with GUID". `SUPERWOW_COMBAT_LOG_EVENT`'s full prefix/suffix param schema is its own sub-project |
|
||||
| `RAW_COMBATLOG` | **done** | `ui_chat/frames.rs`'s `route` — the one funnel every combat line already passes on its way to a window and to `WoWCombatLog.txt`. Two arguments, as its one consumer in grom's corpus destructures them: the printed sentence, and the same sentence with each unit name spelled as its GUID (`PendingCombat::guid_fills`, composed a second time through the same `compose_line`, so the two can only ever differ in the name slots). Fired **before** the `CHAT_MSG_*`, which is what "raw" means upstream — it hooks the combat-log parse, ahead of the client's own formatting. Gated on `has_listener`, the gate nampower's combat-log family already uses, and the `arg2` composition is skipped under the same test: a raid's log is tens of lines a second. A variant whose template says "you" keeps saying it, because that word is in the *template* and not in a fill — and its consumer depends on exactly that, reading the word and then asking `UnitExists("player")` for its own guid. **Why this row came first:** of SuperWoW's whole surface, it and `CombatLogAdd` are the only two names grom's addons call that this client lacked (`scripts/dll-surface.py --wanted`), and `SuperCleveRoidMacros/Utility.lua:10206` funnels **five** subsystems through this single event — immunity and resist learning, hidden-CC ("afflicted by") detection, debuff-duration learning off the GUID half, and the reactive-proc window that lights **Overpower** and **Revenge**. Its own comment there is why the last of those can come from nowhere else: "SPELL_GO only provides hit/miss COUNTS, not miss TYPES, so it cannot distinguish dodge (Overpower proc) from parry/block/resist". This does **not** by itself fix the Overpower complaint: that needs the dodged-*ability* sentence to be emitted at all, which is its own open item (`benilla-overpower-aura-spelldodged`) |
|
||||
| `CombatLogAdd(text)` | **done** | `compat/superwow.rs` queues the line; `ui_chat/logging.rs`'s `sync_chat_logging` appends it to `WoWCombatLog.txt` through the same `record` every routed line uses, so it is stamped identically and one writer owns the file. Dropped while the log is closed, which is upstream's behaviour too (it appends to an open file). A non-string argument is ignored rather than raising: its one real caller is a debug-trace helper (`BigWigs/Plugins/MageTools.lua:761`, under a `fileDebug` profile flag) and a raise there would take the addon down for something the player cannot act on. **It is also a detection name**: `BigWigs/Plugins/SpellRequests.lua:6` is `if CombatLogAdd then hasSuperWow = true`, which gates that whole module's SuperWoW path |
|
||||
| `CREATE_CHATBUBBLE` | todo | needs the chat-bubble creation site in `chat_bubble.rs` (its `push`/`drive_bubbles`); now that `superwow_events.rs` exists with the queue-plus-drain pattern established, this is a small addition rather than new architecture. It is the **last** of SuperWoW's three events — `SUPERWOW_COMBAT_LOG_EVENT`, which this row used to carry, is a ghost: see the manifest below |
|
||||
| `NameplateRange` | done | one CVar, two readers: `nameplates.rs`'s `NameConfig::range_sq` (the overhead name — introduces a cull where the reference has none at all) and `vplates.rs`'s `VPlateMode::range_sq` (replaces the reference's own hardcoded 20 yd V-key-plate range). Default `80`, the top of the mod's documented 10-80 range — no shipped default is published, so this is the least-restrictive honest choice, not a confirmed number |
|
||||
| `ChatBubbleRange` | done | `chat_bubble.rs`'s `BubbleConfig::range_sq`, replacing the reference's hardcoded 20 yd. Default `20` (today's exact value) — no shipped default published for this one either, within the mod's documented 10-200 yd range |
|
||||
| `NameplateMotion`, `NameplateVerticalSpacing` | todo | needs locating whatever (if anything) already staggers overlapping nameplates, not investigated |
|
||||
@@ -49,6 +51,53 @@ nothing behind it.
|
||||
| Friendly-player item-visibility range limit removed | todo | not investigated this session — need to find where benilla currently gates item-row/tooltip visibility by range for a friendly player, if it does at all |
|
||||
| `ShowVKeyCastbar` | **registered, stored only** | pfUI writes `0` at `modules/nameplates.lua:3` ("disable original castbars") under `pcall`, and that write was one of the session log's six `unknown CVar '…' (not host-registered) — ignored` warnings. **Provenance, measured not assumed**: the name appears *nowhere* in this OctoWoW install except that one pfUI line — `grep -rli ShowVKeyCastbar` over the whole tree hits one file, and neither `WoW.exe` nor any of its eighteen DLLs (SuperWoW's included) carries it. So no mod here registers it and **there is no published default to match**; pfUI writes it defensively for whichever client does. Registered in `cvars.rs` as `ours("ShowVKeyCastbar", "0", …)` and **stored, not wired**: benilla's V-key plates have no cast bar to suppress (`benilla-ui`'s `script/nameplate.rs` header says so outright, and `vplates.rs` draws name, level, health bar and raid mark and nothing else), so `0` *is* this client's behaviour rather than a chosen value, shipping `1` would advertise a bar that does not exist, and an observer would give a knob two positions with one picture. If a plate cast bar is ever built this row becomes its switch |
|
||||
|
||||
### SuperWoW's own manifest, read off the DLL — the remaining work is now a closed list
|
||||
|
||||
`scripts/dll-surface.py` finds the names a DLL points at; `SuperWoWhook.dll` goes further and
|
||||
**publishes its own usage strings**, in one contiguous block beside the names. So the arity and the
|
||||
argument *meaning* of every remaining function are measured here, not inferred from the wiki — the
|
||||
same trick that read `VanillaHelpers`' two blips. Three corrections came straight out of it:
|
||||
|
||||
- **`SUPERWOW_COMBAT_LOG_EVENT` does not exist.** The DLL registers exactly three events:
|
||||
`UNIT_CASTEVENT`, `RAW_COMBATLOG`, `CREATE_CHATBUBBLE`. That name appears nowhere in the pool, so
|
||||
the row above that called its parameter schema "its own sub-project" was a sub-project for a
|
||||
ghost. Deleted rather than carried.
|
||||
- **`IsIndoors` is capital-I**, beside `IsSwimming` and `IsMounted` — not the lowercase `isIndoors`
|
||||
this file and `self_movement.rs` both guessed at. All three are already live off one feed
|
||||
(`SelfMovement`, built for ClassicAPI's `State` namespace), so that whole clause of the "needs a
|
||||
self-movement snapshot" row was answered before it was written. `GetSpeed` is the only posture
|
||||
fact still missing, and `current_speed` (`net.rs`, the reference's own `CMovement::GetCurrentSpeed
|
||||
0x7c4c90`) already computes it for every unit.
|
||||
- **The detection pair is `SUPERWOW_STRING="SuperWoW 2.2 by Balake"` and `SUPERWOW_VERSION="2.2"`**,
|
||||
both quoted, assigned by a `superwowInfo.lua` the DLL carries. Ours had the string without its
|
||||
author clause and the version as a *number*. Fixed, and welded by a test.
|
||||
|
||||
The usage strings, verbatim, for what is left:
|
||||
|
||||
| Usage string, as the DLL prints it | Status |
|
||||
|---|---|
|
||||
| `CombatLogAdd("text" [, addToRawLog])` | **done** — the second argument is why this row is here: nothing in the corpus passes it, and it would have been missed |
|
||||
| `TrackUnit("unit")` / `UntrackUnit("unit")` | todo — pfUI calls `TrackUnit` for every party and raid member under `C.unitframes.track_group` (`modules/superwow.lua:96-127`, inside `pcall`), to put the group on the minimap |
|
||||
| `UnitPosition("unit")` | done |
|
||||
| `GetPlayerBuffID(buffIndex)` | done |
|
||||
| `UnitNameplate("unit")` | todo — the plate frame for a unit; `NamePlates::guid_for_frame` already keys the other direction |
|
||||
| `CanlootUnit("unit")` | todo. **Note the lowercase `l`**: the *usage* string says `CanlootUnit` where the registered name is `CanLootUnit`. The registered name is what an addon calls; the typo is in the error message only |
|
||||
| `GetMapPositionWorldLoc(continentIndex, zoneIndex, mapX, mapY)` | todo — map fraction to world XY, which `benilla-world`'s own map maths already does for the world map |
|
||||
| `GetMapBoundaries(continentIndex, zoneIndex)` | todo — the zone's world extent, the same table that conversion reads |
|
||||
| `GetInboxItemLink(messageIndex)` | done (built for ClassicAPI's `Mail`) |
|
||||
| `GetSoundDriverName(driverIndex)` | todo — with `GetSoundDevices`, `GetNumSoundDrivers`, `GetLoadedSoundDriver`: four getters over the audio backend. benilla has one backend and no enumeration, so these answer a one-row list honestly or not at all; no addon in the corpus calls any of them |
|
||||
| `GetQuestIDForLogIndex(questIndex)` / `GetQuestLinkForLogIndex(questIndex)` | done |
|
||||
| `GetQuestLinkForID(questID)` | todo — the same link for a quest the log does not hold, which is a server round trip (`Requesting quest ID %d from server` is the DLL's own next string) |
|
||||
| `ImportFile("filename")` / `ExportFile("filename","text")` | done, under nampower's fence. **Upstream's own folder is `Imports\<name>.txt`** (two more of its literals), where ours is `benilla-config/CustomFiles/` — the deviation is written up in the file-and-credential section below |
|
||||
|
||||
And the CVars it registers, as names: `NameplateRange`, `NameplateMotion`, `ChatBubbleRange`,
|
||||
`ChatBubblesRaid`, `ChatBubblesBattleground`, `ChatBubblesWhisper`, `ChatBubblesCreatures`,
|
||||
`BackgroundSound`, `UncapSounds`, `InterfaceVolume`, `LootSparkle`, `HealingText`,
|
||||
`SelectionCircleStyle`. Three of the bubble four are already live under nampower's `NP_*` spellings
|
||||
on the same fields (`chat_bubble.rs`'s `BubbleConfig`), so those are aliases to add rather than
|
||||
behaviour to build — the same "two mods, one knob" the nameplate reach already is. `HealingText`
|
||||
carries its own description in the pool: *"Toggle display of in-world healing feedback"*.
|
||||
|
||||
## nampower — **two different mods share this name; this bundle ships the second**
|
||||
|
||||
A correction worth stating plainly, because it was wrong here for most of this project's life. The
|
||||
|
||||
Reference in New Issue
Block a user