Commentator player statistics for the spectated arena, wire byte-exact per the client
deserializer sub_7FF7290A19D0 / the 152-byte per-player record reader sub_7FF72906EFA0.
- CommentatorPlayerInfo (SMSG) writer: LeadingId, tracked-spell triple, PackedId, count, a
trailing flag byte, then per player: PackedGuid, faction, specialization, two extra wire
bytes, kills/deaths (u16), damage done/taken + healing done/taken (u32), solo-shuffle round
win/loss (u8), and the four tracked-spell/cooldown array counts.
- CommentatorGetPlayerInfo / CommentatorGetPlayerCooldowns (CMSG) readers (the cooldowns
request carries the player GUID + a {spellID, category} list).
- Handlers (commentator-gated) answer for the arena the session is spectating (the request's
context fields are opaque offline, but the spectated instance identifies the match
unambiguously). Scalar stats are filled from the live BattlegroundScore
(KillingBlows/Deaths/DamageDone/HealingDone) + spec/faction from the player.
The four per-player tracked-spell/cooldown arrays are sent empty (count = 0): their structure
is known but the 44-byte cooldown record's optional-field layout is not confirmed, so we do
not fabricate cooldown timings (empty arrays are byte-exact regardless of array ordering).
This is documented in the dossier as the sole residual. Builds clean.
The observable core: a commentator can now enter a live arena as an invisible observer,
follow a player, and leave. Wire from the client serializers (see dossier).
Battleground spectator support:
- Adds a spectator set (Add/Remove/HasSpectator/GetSpectators). Spectators are not
participants (never in m_Players), so EndBattleground now explicitly ejects them - restoring
GM state, clearing SpectateTarget + BattlegroundId, and teleporting them to their entry point.
Handlers (RBAC/commentator-gated):
- ENTER_INSTANCE { MapID, InstanceIDLow, InstanceIDHigh, bit }: resolves the arena from
BattlegroundMgr::GetActiveArenas by instance+map, saves the entry point, sets the
commentator's BattlegroundId so BattlegroundMap::CannotEnter admits them, becomes an inert
game-master observer, and teleports into the arena start location.
- EXIT_INSTANCE {}: only acts on a tracked spectator - removes it, clears SpectateTarget,
drops GM, teleports back to the entry point.
- SPECTATE { string TargetName }: sets the native PlayerData::SpectateTarget UF to the named
player, but only if they are in the arena being spectated (else clears it).
- New Player::SetSpectateTarget writes the PlayerData::SpectateTarget update field.
Now that the wire is fully recovered offline (see dossier), this builds the first real
data path: a commentator queries the active-match catalogue and the server answers from
live arena state.
- CommentatorMapInfo (SMSG) writer, byte-exact per the client deserializer sub_7FF7290A1700:
uint64 DirectoryId, uint32 MapCount, Maps[]{ TeamSize, MinLevel, MaxLevel, uint16 Field3,
Instances[]{ MapID, {u32,u32,u8}, uint64 InstanceID, Status, Team[2]{ PackedGuid,
Players[]{ PackedGuid, {u32,u32,u8} } } } }. All fixed-width LE; guids PackedGuid.
- CommentatorGetMapInfo (CMSG) reader: { string TargetPlayer } (6-bit length prefix, matching
the client serializer - the extractor had mislabelled this as u8).
- BattlegroundMgr::GetActiveArenas(): new accessor enumerating in-progress arena instances
from bgDataStore.
- HandleCommentatorGetMapInfo (RBAC/commentator-gated): groups active arenas by map into the
client's map->instances catalogue, filling TeamSize (arena type), min/max level, instanceID,
status, and each of the 2 teams' players by GUID (+ best-guess spec/faction in the per-player
triple). Routed STATUS_LOGGEDIN/THREADUNSAFE; SMSG registered STATUS_NEVER (sendable).
The handful of fields with no C_Commentator getter (the opaque u64 blob id, the uint16 map
field, and the {u32,u32,u8} triples) are named FieldN and sent as honest best-guesses/0 - not
fabricated - and documented in the dossier as the only residual sniff items. Builds clean.