From 4dc33b4afd60a4d801e8b446ef777b47f335ba49 Mon Sep 17 00:00:00 2001 From: Sam Sung <51686767+samwhosung@users.noreply.github.com> Date: Sat, 26 Sep 2026 01:29:00 +0700 Subject: [PATCH] Comments in benilla-protocol: the second read, citations on the pinned revision and restored facts (#375) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The second read of `benilla-protocol`'s comments, the last step #41 asked for: that crate's comments were rewritten to the rule before second readers existed. Four readers compared every file's comments against their state before the rewrite and against the code as it is now. Fixes #41. - **Citations.** vmangos line citations are re-pointed to the pinned revision: off-by-a-few lines (`Pet.cpp`, `Guild.cpp`, `Petition.cpp`, `LootHandler.cpp`, `ChatHandler.cpp`), and paths the rewrite dropped where a bare file name is ambiguous (`Server/Packets/Spell.cpp` against `Spells/Spell.cpp`, the two `Item.cpp`, `Group.cpp` and `Chat.cpp`). The gossip-hello citation is `NPCHandler.cpp:347`. - **Facts restored.** The move-time-skipped chain (`0x603b40` โ†’ `0x601560` โ†’ `0x61ab90`), the addon-message TAB surviving vmangos's chat sanitizer, the zone-under-attack sender, `SMSG_ADDON_INFO` answering the auth session's addon block, the officer-note permission gate, and `SMSG_ITEM_COOLDOWN` on equipping an item with an on-use spell (the old text said a proc). - **Deviation labels.** A label now carries its reason, or goes when the behaviour is only unbuilt (the survey window, the group XP bonus, the NPC-text language garble). - **Two examples.** `park_probe` and `login_refusal_probe` took a hard-coded local account; they now read `WOW_USER` and `WOW_PASS`. - **Verification.** Every file strips to identical code apart from the two examples; `cargo fmt --check` is clean. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 5.5 (1M context) --- .../examples/cooldown_probe.rs | 4 +-- .../examples/login_refusal_probe.rs | 6 ++-- .../benilla-protocol/examples/park_probe.rs | 6 ++-- .../examples/srp_encoding_probe.rs | 4 +-- crates/benilla-protocol/src/auth.rs | 3 +- .../src/bin/benilla-world/probes/mod.rs | 2 +- crates/benilla-protocol/src/events.rs | 3 +- crates/benilla-protocol/src/messages/bank.rs | 2 +- .../src/messages/battlefield.rs | 3 +- .../src/messages/broadcast.rs | 5 ++-- crates/benilla-protocol/src/messages/chat.rs | 5 ++-- crates/benilla-protocol/src/messages/death.rs | 4 +-- .../benilla-protocol/src/messages/gossip.rs | 6 ++-- crates/benilla-protocol/src/messages/group.rs | 4 +-- crates/benilla-protocol/src/messages/guild.rs | 8 +++-- .../benilla-protocol/src/messages/instance.rs | 2 +- crates/benilla-protocol/src/messages/loot.rs | 2 +- crates/benilla-protocol/src/messages/mod.rs | 2 +- .../benilla-protocol/src/messages/movement.rs | 2 +- .../benilla-protocol/src/messages/opcode.rs | 21 ++++++++----- .../benilla-protocol/src/messages/packet.rs | 7 +++-- crates/benilla-protocol/src/messages/parse.rs | 3 +- crates/benilla-protocol/src/messages/pet.rs | 6 ++-- .../benilla-protocol/src/messages/petition.rs | 16 +++++----- .../src/messages/progression.rs | 4 +-- .../src/messages/quest/giver.rs | 2 +- .../src/messages/spellbook.rs | 30 +++++++++++-------- .../benilla-protocol/src/messages/spells.rs | 4 +-- .../src/messages/update_object/fields/unit.rs | 3 +- .../benilla-protocol/src/world/writer/chat.rs | 7 +++-- .../src/world/writer/gossip.rs | 3 +- crates/benilla-protocol/tests/client.rs | 3 +- crates/benilla-protocol/tests/guild.rs | 2 +- .../benilla-protocol/tests/simple_packets.rs | 2 +- crates/benilla-protocol/tests/spells.rs | 2 +- crates/benilla-protocol/tests/trainer.rs | 4 +-- docs/MAP.md | 4 +-- 37 files changed, 111 insertions(+), 85 deletions(-) diff --git a/crates/benilla-protocol/examples/cooldown_probe.rs b/crates/benilla-protocol/examples/cooldown_probe.rs index 39361af4..083afb95 100644 --- a/crates/benilla-protocol/examples/cooldown_probe.rs +++ b/crates/benilla-protocol/examples/cooldown_probe.rs @@ -1,13 +1,13 @@ //! Diagnostic probe: the server's cooldown window for Charge (spell 100: `recoveryTime 0`, //! category 44, `categoryRecoveryTime 15000`) against the client's 15 s sweep. It charges a //! Northshire Kobold Vermin (entry 6), then recasts every 200 ms. vmangos checks the cooldown -//! first in `CheckCast` (`Spell.cpp:5369`), so the first result other than +//! near the top of `CheckCast` (`Spell.cpp:5369`), so the first result other than //! `SPELL_FAILED_NOT_READY` (60), or a second `SMSG_SPELL_GO`, marks the server's cooldown end //! relative to the first GO. //! //! Run: `cargo run -p benilla-protocol --example cooldown_probe -- probeN [host]` on a //! probe account with a warrior (a login kicks whoever is on the account); `.learn` needs -//! gmlevel 5 (`SEC_DEVELOPER`). +//! gmlevel 5 (`SEC_DEVELOPER`). It is a combat probe, so it never runs unattended. use std::time::{Duration, Instant}; diff --git a/crates/benilla-protocol/examples/login_refusal_probe.rs b/crates/benilla-protocol/examples/login_refusal_probe.rs index 9ca7a0af..2bb2f039 100644 --- a/crates/benilla-protocol/examples/login_refusal_probe.rs +++ b/crates/benilla-protocol/examples/login_refusal_probe.rs @@ -5,7 +5,7 @@ //! and that a valid pick then works on the same socket. //! //! A creature guid trips the stateless `!packet.guid.IsPlayer()` guard. Needs the local vmangos -//! and account `two`/`ptwo`. +//! and an account in `WOW_USER`/`WOW_PASS`. use std::time::Duration; @@ -25,7 +25,9 @@ fn connect(user: &str, pass: &str) -> anyhow::Result { } fn main() -> anyhow::Result<()> { - let (user, pass) = ("two", "ptwo"); + let user = std::env::var("WOW_USER").map_err(|_| anyhow::anyhow!("set WOW_USER"))?; + let pass = std::env::var("WOW_PASS").map_err(|_| anyhow::anyhow!("set WOW_PASS"))?; + let (user, pass) = (user.as_str(), pass.as_str()); let mut s = connect(user, pass)?; let chars = s.char_enum()?; println!( diff --git a/crates/benilla-protocol/examples/park_probe.rs b/crates/benilla-protocol/examples/park_probe.rs index 3badbb46..f29be9ee 100644 --- a/crates/benilla-protocol/examples/park_probe.rs +++ b/crates/benilla-protocol/examples/park_probe.rs @@ -2,7 +2,7 @@ //! `SMSG_LOGOUT_COMPLETE` and a reconnect re-serves the roster; a socket parked at character //! select for 130 s still logs in, since vmangos does not kick a quiet authenticated socket. //! -//! Needs the local vmangos and account `two`/`ptwo`. +//! Needs the local vmangos and an account in `WOW_USER`/`WOW_PASS`. use std::time::Duration; @@ -19,7 +19,9 @@ fn connect(user: &str, pass: &str) -> anyhow::Result { } fn main() -> anyhow::Result<()> { - let (user, pass) = ("two", "ptwo"); + let user = std::env::var("WOW_USER").map_err(|_| anyhow::anyhow!("set WOW_USER"))?; + let pass = std::env::var("WOW_PASS").map_err(|_| anyhow::anyhow!("set WOW_PASS"))?; + let (user, pass) = (user.as_str(), pass.as_str()); // โ”€โ”€ 1 ยท logout round-trip โ”€โ”€ let mut s = connect(user, pass)?; diff --git a/crates/benilla-protocol/examples/srp_encoding_probe.rs b/crates/benilla-protocol/examples/srp_encoding_probe.rs index 43006735..2a47dad1 100644 --- a/crates/benilla-protocol/examples/srp_encoding_probe.rs +++ b/crates/benilla-protocol/examples/srp_encoding_probe.rs @@ -9,8 +9,8 @@ //! ``` //! //! `--stress N` runs N ordinary fixed-width handshakes and reports the failure rate, waiting out -//! realmd's lockout: 60 s after `WrongPass.MaxAttempts` (default 10) failures, during which it -//! answers `0x08 WOW_FAIL_DB_BUSY`. +//! realmd's lockout: `WrongPass.MaxAttempts` (default 10) failures within 60 s lock the IP out +//! for 60 s, answered with `0x08 WOW_FAIL_DB_BUSY`. use std::net::TcpStream; use std::time::Duration; diff --git a/crates/benilla-protocol/src/auth.rs b/crates/benilla-protocol/src/auth.rs index 29d5c108..552ad544 100644 --- a/crates/benilla-protocol/src/auth.rs +++ b/crates/benilla-protocol/src/auth.rs @@ -165,7 +165,8 @@ const INTEGRITY_HASH_5875_MACOS: [u8; 20] = [ ]; /// The integrity digest `H` for `crc_salt`, known only for [`MANGOS_VERSION_CHALLENGE`]. -/// Deviation: a stored constant, not a hash of the install, which may lack the five binaries. +/// Deviation: a stored per-OS constant, not the reference's HMAC over its own executables +/// (`0x5b1170`), because every mangos-family realmd issues this one salt. fn integrity_hash(crc_salt: &[u8; 16]) -> Option<[u8; 20]> { if *crc_salt != MANGOS_VERSION_CHALLENGE { return None; diff --git a/crates/benilla-protocol/src/bin/benilla-world/probes/mod.rs b/crates/benilla-protocol/src/bin/benilla-world/probes/mod.rs index a3200306..59151c89 100644 --- a/crates/benilla-protocol/src/bin/benilla-world/probes/mod.rs +++ b/crates/benilla-protocol/src/bin/benilla-world/probes/mod.rs @@ -38,7 +38,7 @@ pub(crate) trait Probe { // Quest constants shared by several probes. /// Onto Marshal McBride, the `--quest` turn-in NPC and quest 7's giver and ender. -pub(crate) const QUEST_TURNIN_TP: &str = ".go xyz -8902.59 -162.606 82.0223"; // onto Marshal McBride +pub(crate) const QUEST_TURNIN_TP: &str = ".go xyz -8902.59 -162.606 82.0223"; pub(crate) const QUEST_TURNIN_ENTRY: u32 = 197; // Marshal McBride, who takes 783 /// `PLAYER_QUEST_LOG_1_1`: `UNIT_END` (188) + 0xA, 3 fields per slot for 20 slots /// (`UpdateFields_1_12_1.h:128`). diff --git a/crates/benilla-protocol/src/events.rs b/crates/benilla-protocol/src/events.rs index 30a058ca..6a1a0d2f 100644 --- a/crates/benilla-protocol/src/events.rs +++ b/crates/benilla-protocol/src/events.rs @@ -229,7 +229,8 @@ pub enum SessionEvent { /// cinematic camera (`Player::UpdateCinematic`), despawning the world around the body. CinematicTriggered { cinematic_id: u32 }, /// `MSG_MOVE_TIME_SKIPPED`: not a pose. The mover's relay stamp advances by `lag_ms`, as the - /// reference's `[CMovement+0xac] += lag` (`0x603b40`), or its next packet is scheduled late. + /// reference's `[CMovement+0xac] += lag` (`0x603b40` โ†’ `0x601560` โ†’ `0x61ab90`), or its next + /// packet is scheduled late. MoveTimeSkipped { guid: u64, lag_ms: u32 }, /// A server path: `path` is the travel-order polyline from `start`, walked at constant speed /// over `duration_ms`; a stop, a zero duration or fewer than two points leaves it empty. diff --git a/crates/benilla-protocol/src/messages/bank.rs b/crates/benilla-protocol/src/messages/bank.rs index e88a1a80..ada779c0 100644 --- a/crates/benilla-protocol/src/messages/bank.rs +++ b/crates/benilla-protocol/src/messages/bank.rs @@ -5,7 +5,7 @@ use std::io; use crate::wire::{read_u32_le, read_u64_le}; -/// `SMSG_BUY_BANK_SLOT_RESULT`'s reason (`BANK_SLOT_*`, vmangos `Player.h:91-94`). +/// `SMSG_BUY_BANK_SLOT_RESULT`'s reason (`ERR_BANKSLOT_*`, vmangos `Player.h:91-94`). pub mod bank_slot_result { pub const FAILED_TOO_MANY: u32 = 0; pub const INSUFFICIENT_FUNDS: u32 = 1; diff --git a/crates/benilla-protocol/src/messages/battlefield.rs b/crates/benilla-protocol/src/messages/battlefield.rs index d4270c03..b4bc954a 100644 --- a/crates/benilla-protocol/src/messages/battlefield.rs +++ b/crates/benilla-protocol/src/messages/battlefield.rs @@ -95,7 +95,8 @@ pub struct PvpLogData { pub rows: Vec, } -/// Deviation: every row is kept; the reference has 80 row blocks and does not clamp the count. +/// Deviation: every row is kept, because the reference takes the count unclamped into its 80 +/// row blocks and a larger one overruns them. pub(super) fn read_pvp_log_data(r: &mut impl Read) -> io::Result { let ended = read_u8(r)? != 0; let winner = if ended { Some(read_u8(r)?) } else { None }; diff --git a/crates/benilla-protocol/src/messages/broadcast.rs b/crates/benilla-protocol/src/messages/broadcast.rs index 05813d6c..d50926a5 100644 --- a/crates/benilla-protocol/src/messages/broadcast.rs +++ b/crates/benilla-protocol/src/messages/broadcast.rs @@ -17,7 +17,8 @@ pub(super) fn read_server_message(r: &mut &[u8]) -> io::Result<(u32, String)> { /// `SMSG_ZONE_UNDER_ATTACK` (`Server/Packets/Misc.cpp:451-454`, handler `0x49dcc0`): the attacked /// area's `AreaTable.dbc` id, whose own name fills the text. vmangos sends it map-wide to the -/// enemy team when a player kills a guard or PvP-enabling creature, once per area per 10 s. +/// enemy team when a player kills a guard or PvP-enabling creature, once per area per 10 s +/// (`Creature.cpp:2889`). pub(super) fn read_zone_under_attack(r: &mut &[u8]) -> io::Result { read_u32_le(r) } @@ -25,7 +26,7 @@ pub(super) fn read_zone_under_attack(r: &mut &[u8]) -> io::Result { /// `SMSG_DEFENSE_MESSAGE` (`Maps/Map.cpp:1868-1884`, handler `0x49de30`): `u32` zone id, `u32` /// length counting the NUL, then the text. The reference skips exactly `length` bytes /// (`0x419ac0`) and drops the line when that overruns the packet, so an overrun errors here. -/// Deviation: text without a NUL stops at `length`, where the reference reads out of bounds. +/// Deviation: text without a NUL stops at `length`, because the reference reads on out of bounds. pub(super) fn read_defense_message(r: &mut &[u8]) -> io::Result<(u32, String)> { let zone_id = read_u32_le(r)?; let length = read_u32_le(r)? as usize; diff --git a/crates/benilla-protocol/src/messages/chat.rs b/crates/benilla-protocol/src/messages/chat.rs index 023fb784..f5c56342 100644 --- a/crates/benilla-protocol/src/messages/chat.rs +++ b/crates/benilla-protocol/src/messages/chat.rs @@ -33,8 +33,9 @@ pub const CHAT_MSG_RAID_LEADER: u8 = 0x57; pub const CHAT_MSG_RAID_WARNING: u8 = 0x58; pub const CHAT_MSG_RAID_BOSS_WHISPER: u8 = 0x59; pub const CHAT_MSG_RAID_BOSS_EMOTE: u8 = 0x5A; -/// The server's "your message was filtered" notice, which the chat frame formats as -/// `CHAT_FILTERED`; the client's spam and profanity filters exempt it (`0x49aacc`). +/// The server's "your message was filtered" notice: its text is the addressee's name, which the +/// chat frame formats into `CHAT_FILTERED`; the client's spam and profanity filters exempt it +/// (`0x49aacc`). pub const CHAT_MSG_FILTERED: u8 = 0x5B; pub const CHAT_MSG_BATTLEGROUND: u8 = 0x5C; pub const CHAT_MSG_BATTLEGROUND_LEADER: u8 = 0x5D; diff --git a/crates/benilla-protocol/src/messages/death.rs b/crates/benilla-protocol/src/messages/death.rs index 169edfe4..1a8df397 100644 --- a/crates/benilla-protocol/src/messages/death.rs +++ b/crates/benilla-protocol/src/messages/death.rs @@ -5,7 +5,7 @@ use std::io; use crate::wire::{read_cstring, read_f32_le, read_i32_le, read_u32_le, read_u64_le, read_u8}; /// `MSG_CORPSE_QUERY`'s answer (`QueryHandler.cpp:258-304`), a lone `u8(0)` when not found; the -/// request is the same opcode, empty. The server also sends an unprompted not-found when the +/// request is the same opcode, empty. The server also sends an unprompted not-found when a looted /// corpse turns to bones (`Map.cpp:3624-3629`), which means "drop the marker". #[derive(Debug, Clone, PartialEq)] pub struct CorpseLocation { @@ -48,7 +48,7 @@ pub(super) fn read_corpse_reclaim_delay(r: &mut &[u8]) -> io::Result { /// `SMSG_RESURRECT_REQUEST`, a resurrection offer (`Spell.cpp:5024-5044`). `sickness` warns the /// accept brings resurrection sickness, `has_timer` keeps the reclaim-delay gate, and the stock -/// popup picks its variant by the two. +/// popup picks its variant by the two. `name` is empty for a player caster, named by guid instead. #[derive(Debug, Clone, PartialEq)] pub struct ResurrectRequestBody { pub caster: u64, diff --git a/crates/benilla-protocol/src/messages/gossip.rs b/crates/benilla-protocol/src/messages/gossip.rs index 5d5e93ab..b7b78a59 100644 --- a/crates/benilla-protocol/src/messages/gossip.rs +++ b/crates/benilla-protocol/src/messages/gossip.rs @@ -24,7 +24,7 @@ pub struct QuestOption { } /// `CMSG_GOSSIP_HELLO` body: the NPC's full guid (`Npc.cpp:3`); it works on any interactable -/// creature, gossip flag or not (`Player.cpp:347`). +/// creature, gossip flag or not (`NPCHandler.cpp:347`). pub fn gossip_hello(npc_guid: u64) -> Vec { npc_guid.to_le_bytes().to_vec() } @@ -108,8 +108,8 @@ pub const NPC_TEXT_BLOCKS: usize = 8; /// Read `SMSG_NPC_TEXT_UPDATE` (vmangos `GossipDef.cpp:298-369`), all eight blocks undecided; the /// greeting is picked when the frame opens, by [`select_greeting`]. /// -/// Deviation: the 1.12 client garbles a block in a non-zero language (`0x49b560`); we drop the -/// language because no 1.12 record sets one. +/// The language is parsed and dropped: the 1.12 client garbles a block in a non-zero language +/// (`0x49b560`), which is not built, and no 1.12 record is known to set one. pub(super) fn read_npc_text_update(r: &mut &[u8]) -> io::Result<(u32, Vec)> { let text_id = read_u32_le(r)?; let mut blocks = Vec::with_capacity(NPC_TEXT_BLOCKS); diff --git a/crates/benilla-protocol/src/messages/group.rs b/crates/benilla-protocol/src/messages/group.rs index e8e5c6af..e0ef1756 100644 --- a/crates/benilla-protocol/src/messages/group.rs +++ b/crates/benilla-protocol/src/messages/group.rs @@ -11,7 +11,7 @@ use crate::wire::{ }; /// The raid-assistant bit of a member's flags byte; bits 0-2 are the subgroup -/// (`Group.cpp:158,166`). +/// (`Group/Group.cpp:1382,1394`). pub const GROUP_MEMBER_ASSISTANT: u8 = 0x80; /// vmangos `GroupMemberStatus` (`Group/Group.h:102-111`); bit `0x20` is never set. @@ -51,7 +51,7 @@ pub mod party_result { pub struct GroupMemberEntry { pub name: String, pub guid: u64, - /// [`member_status`] bits (`GetGroupMemberStatus`, `Group.cpp:45-63`). + /// [`member_status`] bits (`GetGroupMemberStatus`, `Group/Group.cpp:45-63`). pub status: u8, /// Subgroup in bits 0-2, plus [`GROUP_MEMBER_ASSISTANT`]. pub flags: u8, diff --git a/crates/benilla-protocol/src/messages/guild.rs b/crates/benilla-protocol/src/messages/guild.rs index 0bbc5183..b432ce2f 100644 --- a/crates/benilla-protocol/src/messages/guild.rs +++ b/crates/benilla-protocol/src/messages/guild.rs @@ -3,8 +3,9 @@ //! //! Deviation: string reads are unbounded. The reference `0x4191b0` caps each field, counting the //! NUL (roster name `0x30`, notes `0x80`, MOTD `0x200`, info `0x7d0`, guild name `0x60`, rank name -//! `0x40`), and on overflow empties it and no-ops every later read in the packet; every cap is far -//! above the server's own length limits, so a legitimate packet reads the same. +//! `0x40`), and on overflow empties it and no-ops every later read in the packet. Unbounded reads +//! cannot poison a packet, and every cap is far above the server's own length limits, so a +//! legitimate packet reads the same. use std::io::{self, Read}; @@ -550,7 +551,8 @@ pub fn guild_set_public_note(name: &str, note: &str) -> Vec { two_cstring_body(name, note) } -/// Body of `CMSG_GUILD_SET_OFFICER_NOTE` (`Server/Packets/Guild.cpp:67-71`): name, then note. +/// Body of `CMSG_GUILD_SET_OFFICER_NOTE` (`Server/Packets/Guild.cpp:67-71`): name, then note; +/// the server requires [`guild_rank_right::EDIT_OFFICER_NOTE`]. pub fn guild_set_officer_note(name: &str, note: &str) -> Vec { two_cstring_body(name, note) } diff --git a/crates/benilla-protocol/src/messages/instance.rs b/crates/benilla-protocol/src/messages/instance.rs index 640d36ff..840e1ede 100644 --- a/crates/benilla-protocol/src/messages/instance.rs +++ b/crates/benilla-protocol/src/messages/instance.rs @@ -68,7 +68,7 @@ pub enum InstanceResetFailure { impl InstanceResetFailure { /// Deviation: a reason of 3 or more is silent, where the reference prints an uninitialized - /// stack buffer; 3 is the server's `INSTANCERESET_FAIL_SILENTLY`. + /// stack buffer, because 3 is the server's `INSTANCERESET_FAIL_SILENTLY`. pub fn from_wire(reason: u32) -> Option { match reason { 0 => Some(Self::General), diff --git a/crates/benilla-protocol/src/messages/loot.rs b/crates/benilla-protocol/src/messages/loot.rs index feea98be..e01fec45 100644 --- a/crates/benilla-protocol/src/messages/loot.rs +++ b/crates/benilla-protocol/src/messages/loot.rs @@ -35,7 +35,7 @@ pub mod slot_type { /// `LootType` (vmangos `LootMgr.h:49-61`). Only these four reach the wire: `SendLoot` remaps /// skinning and insignia to `PICKPOCKETING`, fishing holes and fails to `FISHING` -/// (`Player.cpp:8117-8131`); `CMSG_LOOT` always gets `CORPSE` (`LootHandler.cpp:340-354`). +/// (`Player.cpp:8120-8136`); `CMSG_LOOT` always gets `CORPSE` (`LootHandler.cpp:380`). pub mod loot_type { pub const CORPSE: u8 = 1; pub const PICKPOCKETING: u8 = 2; diff --git a/crates/benilla-protocol/src/messages/mod.rs b/crates/benilla-protocol/src/messages/mod.rs index a22a00d1..5cffe85e 100644 --- a/crates/benilla-protocol/src/messages/mod.rs +++ b/crates/benilla-protocol/src/messages/mod.rs @@ -297,7 +297,7 @@ pub const LANGUAGE_ORCISH: u32 = 0x1; /// `LANG_ADDON` (vmangos `SharedDefines.h:270`): marks a chat line as addon data, not speech; /// 1.12 has no addon opcode, and the client routes such a line to `CHAT_MSG_ADDON`. The server /// skips language, flood and sanitize checks for it, never rewrites it, and allows it only on the -/// group, guild and channel lanes (`ChatHandler.cpp:49,84,176-218`). The 1.12 `SendAddonMessage` +/// group, guild and channel lanes (`ChatHandler.cpp:49,84,172-219`). The 1.12 `SendAddonMessage` /// (`0x49f920`) sends only PARTY, RAID, GUILD and BATTLEGROUND, and the receive side names any /// other lane "UNKNOWN" (`0x49aff4`). pub const LANGUAGE_ADDON: u32 = 0xFFFF_FFFF; diff --git a/crates/benilla-protocol/src/messages/movement.rs b/crates/benilla-protocol/src/messages/movement.rs index 869c4e67..32fd4eaa 100644 --- a/crates/benilla-protocol/src/messages/movement.rs +++ b/crates/benilla-protocol/src/messages/movement.rs @@ -100,7 +100,7 @@ impl SpeedKind { } /// A mode the server grants the controlling client, one `MOVEMENTFLAGS` bit each (vmangos -/// `MovementInfo.h:25-62`, reference setters `0x7c7280`-`0x7c7370`). Root stops translation and +/// `MovementInfo.h:28-62`, reference setters `0x7c7280`-`0x7c7370`). Root stops translation and /// falling but not turning (`0x618054`); water walk makes liquid walkable (aura 104); feather fall /// caps the fall at 7 yd/s instead of 60.148 (aura 105, `0x7c5d20`); hover lifts ground contact by /// 1.0 yd (aura 106, `0x6367b0`). Levitate (spell 1706) grants the last three at once. diff --git a/crates/benilla-protocol/src/messages/opcode.rs b/crates/benilla-protocol/src/messages/opcode.rs index 1271830b..70e42f78 100644 --- a/crates/benilla-protocol/src/messages/opcode.rs +++ b/crates/benilla-protocol/src/messages/opcode.rs @@ -60,9 +60,11 @@ pub const CMSG_SET_WATCHED_FACTION: u16 = 0x0318; // 792 pub const SMSG_AUTH_CHALLENGE: u16 = 0x01EC; pub const SMSG_AUTH_RESPONSE: u16 = 0x01EE; /// The Warden anticheat challenge; vmangos kicks a client that leaves it unanswered for 30 s -/// (`Warden::Update`). Deviation: no Warden ([`crate::WardenRequired`]), so the connect refuses. +/// (`Warden::Update`). Deviation: benilla has no Warden, so the connect refuses such a server +/// ([`crate::WardenRequired`]) because a session there would only be kicked. pub const SMSG_WARDEN_DATA: u16 = 0x02E6; -/// One record per `## Secure:` addon in `CMSG_AUTH_SESSION`, in order, with no count or names. +/// The answer to `CMSG_AUTH_SESSION`'s addon block: one record per `## Secure:` addon, in order, +/// with no count or names. pub const SMSG_ADDON_INFO: u16 = 0x02EF; pub const SMSG_COMPRESSED_UPDATE_OBJECT: u16 = 0x01F6; /// A zlib envelope of whole movement packets. Routine: vmangos switches a session to it after 300 @@ -222,7 +224,8 @@ pub const SMSG_CLEAR_COOLDOWN: u16 = 0x01DE; // 478 pub const SMSG_COOLDOWN_CHEAT: u16 = 0x01E1; // 481 /// A timed item's remaining life in seconds; the display reads this, not `ITEM_FIELD_DURATION` -/// (`Item.cpp:1094`). It shares the reference handler (`0x5e4f69`) with the enchant update below. +/// (`Item.cpp:1094`). Its reference handler is the enchant update's, forking on the opcode +/// (`0x5e4f69`). pub const SMSG_ITEM_TIME_UPDATE: u16 = 0x01EA; // 490 /// The only source of a temporary enchant's remaining time: the reference tooltip reads a @@ -607,8 +610,8 @@ pub const CMSG_GMTICKET_SYSTEMSTATUS: u16 = 0x021A; // 538 pub const SMSG_GMTICKET_SYSTEMSTATUS: u16 = 0x021B; // 539 /// A ticket-state push: `u32` 1 updated, 2 closed, 3 survey offered; vmangos never sends it. The -/// reference re-asks for the ticket on 1 (`0x5e7932`). Deviation: 3 opens no survey window, -/// since vmangos can never offer one. +/// reference re-asks for the ticket on 1 (`0x5e7932`); 2 and 3 are recorded, not acted on, and +/// the survey window 3 offers is not built. pub const SMSG_GM_TICKET_STATUS_UPDATE: u16 = 0x0328; // 808 // The bank: `CMSG_BANKER_ACTIVATE` opens a pure banker; `SMSG_SHOW_BANK` also arrives unasked @@ -891,9 +894,10 @@ pub const CMSG_GROUP_SWAP_SUB_GROUP: u16 = 0x0280; // 640 pub const CMSG_GROUP_RAID_CONVERT: u16 = 0x028E; // 654 pub const CMSG_GROUP_ASSISTANT_LEADER: u16 = 0x028F; // 655 pub const SMSG_PARTY_MEMBER_STATS_FULL: u16 = 0x02F2; // 754 -/// Both ways; the server's side is mode-prefixed (`Group.cpp:77-82` read, `:132-147` write). +/// Both ways; the server's side is mode-prefixed (`Packets/Group.cpp:77-82` read, `:132-147` +/// write). pub const MSG_RAID_TARGET_UPDATE: u16 = 0x0321; // 801 -/// Both ways: an empty body starts a check, a non-empty one answers (`Group.cpp:84-96`). +/// Both ways: an empty body starts a check, a non-empty one answers (`Packets/Group.cpp:84-96`). pub const MSG_RAID_READY_CHECK: u16 = 0x0322; // 802 /// Ask for our raid lockouts: empty body, sent by `RequestRaidInfo()` on every RaidFrame show. pub const CMSG_REQUEST_RAID_INFO: u16 = 0x02CD; // 717 @@ -975,7 +979,8 @@ pub const SMSG_UPDATE_WORLD_STATE: u16 = 0x02C3; // 707 // โ”€โ”€ Instance and raid lockouts โ”€โ”€ /// "You are now saved to this instance": a `u32` flag, 0 from vmangos (`0x4e7e60`; 1 adds a debug -/// prefix). Deviation: 2 or more prints nothing; the reference prints an uninitialized buffer. +/// prefix). Deviation: 2 or more prints nothing, because the reference prints an uninitialized +/// buffer. pub const SMSG_INSTANCE_SAVE_CREATED: u16 = 0x02CB; // 715 /// A raid-lockout warning: `u32 type`, `u32 mapId`, `u32 secondsUntilReset` (`0x49e1c0`). pub const SMSG_RAID_INSTANCE_MESSAGE: u16 = 0x02FA; // 762 diff --git a/crates/benilla-protocol/src/messages/packet.rs b/crates/benilla-protocol/src/messages/packet.rs index 81ba5be3..d0aae26e 100644 --- a/crates/benilla-protocol/src/messages/packet.rs +++ b/crates/benilla-protocol/src/messages/packet.rs @@ -409,7 +409,8 @@ pub enum ServerPacket { flags: u8, members: Vec<(u64, u8)>, }, - /// `SMSG_CHAT_PLAYER_NOT_FOUND`: the whisper target is not online (`Chat.cpp:26-29`). + /// `SMSG_CHAT_PLAYER_NOT_FOUND`: the whisper target is not online + /// (`Server/Packets/Chat.cpp:26-29`). ChatPlayerNotFound { name: String, }, @@ -420,7 +421,7 @@ pub enum ServerPacket { text: String, }, /// `SMSG_AREA_TRIGGER_MESSAGE`: why a trigger refused us, as `u32 length` then a cstring - /// (`WorldSession.cpp:882-898`). + /// (`WorldSession.cpp:883-898`). AreaTriggerMessage { text: String, }, @@ -751,7 +752,7 @@ pub enum ServerPacket { vendor: u64, items: Vec, }, - /// `SMSG_BUY_ITEM`: the vendor's stock after a purchase (`Item.cpp:190-196`). + /// `SMSG_BUY_ITEM`: the vendor's stock after a purchase (`Server/Packets/Item.cpp:190-196`). BuyItem { vendor: u64, slot: u32, diff --git a/crates/benilla-protocol/src/messages/parse.rs b/crates/benilla-protocol/src/messages/parse.rs index 8e589874..4419af05 100644 --- a/crates/benilla-protocol/src/messages/parse.rs +++ b/crates/benilla-protocol/src/messages/parse.rs @@ -230,7 +230,8 @@ fn parse_server_body( // bytes left, `u32 billingTimeRemaining, u8 billingPlanFlags, u32 billingTimeRested`; // then, for WAIT_QUEUE, `u32 position`. The 5 guarding a 9-byte group is the client's // and mis-parses a 6..=9 byte body; matched on purpose. Deviation: a body too short - // for the position gives `None`, because the client redisplays a stale position. + // for the position gives `None` where the client redisplays a stale global, because + // showing a stale queue position as current is a client bug. let result = read_u8(&mut r)?; let queued = result == super::AUTH_WAIT_QUEUE; let mut billing_time_rested = None; diff --git a/crates/benilla-protocol/src/messages/pet.rs b/crates/benilla-protocol/src/messages/pet.rs index 4e123b55..98e27b89 100644 --- a/crates/benilla-protocol/src/messages/pet.rs +++ b/crates/benilla-protocol/src/messages/pet.rs @@ -130,7 +130,7 @@ pub struct PetSpells { pub state: u32, /// The ten bar slots, in bar order. pub bar: [PetActionEntry; PET_ACTION_SLOTS], - /// The pet spellbook, packed like the bar; only a permanent pet has one (`Player.cpp:17549`). + /// The pet spellbook, packed like the bar; only a permanent pet has one (`Player.cpp:17547`). pub spells: Vec, pub cooldowns: Vec, } @@ -231,7 +231,7 @@ pub struct PetMode { pub state: u32, } -/// Read `SMSG_PET_MODE`: `u64 petGuid, u32 state` (`0x4bdb10`; vmangos `Packets/Pet.cpp:101-108`). +/// Read `SMSG_PET_MODE`: `u64 petGuid, u32 state` (`0x4bdb10`; vmangos `Packets/Pet.cpp:104-111`). pub(super) fn read_pet_mode(r: &mut impl Read) -> io::Result { Ok(PetMode { pet_guid: read_u64_le(r)?, @@ -239,7 +239,7 @@ pub(super) fn read_pet_mode(r: &mut impl Read) -> io::Result { }) } -/// Read `SMSG_PET_ACTION_FEEDBACK` (vmangos `Packets/Pet.cpp:87-90`): one reason byte. +/// Read `SMSG_PET_ACTION_FEEDBACK` (vmangos `Packets/Pet.cpp:90-93`): one reason byte. pub(super) fn read_pet_action_feedback(r: &mut impl Read) -> io::Result { read_u8(r) } diff --git a/crates/benilla-protocol/src/messages/petition.rs b/crates/benilla-protocol/src/messages/petition.rs index c46f24ef..2c47b3d0 100644 --- a/crates/benilla-protocol/src/messages/petition.rs +++ b/crates/benilla-protocol/src/messages/petition.rs @@ -13,7 +13,7 @@ use crate::wire::{ /// The guild charter's item entry, vmangos `GUILD_CHARTER` (`PetitionsHandler.cpp:37`). pub const CHARTER_ITEM_ENTRY: u32 = 5863; -/// The charter's display id, vmangos `CHARTER_DISPLAY_ID` (`PetitionsHandler.cpp:38`). +/// The charter's display id, vmangos `CHARTER_DISPLAY_ID` (`PetitionsHandler.cpp:39`). pub const CHARTER_DISPLAY_ID: u32 = 16161; /// The template-flag bit of a signable petition (vmangos `ItemPrototype.h:77`); the reference @@ -29,7 +29,7 @@ pub const MAX_PETITION_SIGNATURES: usize = 9; pub const CHARTER_NAME_MAX_LENGTH: usize = 24; /// The result code shared by `SMSG_PETITION_SIGN_RESULTS` and `SMSG_TURN_IN_PETITION_RESULTS` -/// (vmangos `PetitionSigns`, `Guild.h:147-154`); each packet uses its own subset. +/// (vmangos `PetitionSigns`, `Guild/Guild.h:147-154`); each packet uses its own subset. pub mod petition_result { /// Signed, or turned in; both packets. pub const OK: u32 = 0; @@ -52,13 +52,13 @@ pub mod petition_result { /// (`Petition.h:176`: "only 1 element is supported in the client"). #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct PetitionShowListEntry { - /// Row index, 1-based (`PetitionsHandler.cpp:501`). + /// Row index, 1-based (`PetitionsHandler.cpp:496`). pub index: u32, /// The item entry sold, [`CHARTER_ITEM_ENTRY`] in practice. pub charter_entry: u32, /// Its display id, [`CHARTER_DISPLAY_ID`] in practice. pub charter_display_id: u32, - /// Price in copper, an `int32` on the wire (vmangos charges 1000, `PetitionsHandler.cpp:39`). + /// Price in copper, an `int32` on the wire (vmangos charges 1000, `PetitionsHandler.cpp:38`). pub charter_cost: i32, /// vmangos sends 1; its header says a row "must be `&1` to show it in the UI" /// (`Petition.h:169`). @@ -79,7 +79,7 @@ pub struct PetitionShowList { pub(super) fn read_petition_show_list(r: &mut impl Read) -> io::Result { let npc = read_u64_le(r)?; let count = read_u8(r)?; - // vmangos sends exactly one charter (`PetitionsHandler.cpp:503`); 8 is generous. + // vmangos sends exactly one charter (`PetitionsHandler.cpp:504`); 8 is generous. let mut entries = Vec::with_capacity(capacity_hint(count, 8)); for _ in 0..count { entries.push(PetitionShowListEntry { @@ -170,9 +170,9 @@ pub struct PetitionQueryResponse { pub owner: u64, /// The proposed guild's name, carried by no other packet. pub name: String, - /// Free text on the charter; empty on vmangos (`PetitionsHandler.cpp:178`). + /// Free text on the charter; empty, as vmangos never sets it (`PetitionsHandler.cpp:177-183`). pub body_text: String, - /// vmangos sends 1 (`PetitionsHandler.cpp:179`); meaning unknown. + /// vmangos sends 1 (`PetitionsHandler.cpp:181`); meaning unknown. pub flags: u32, /// Signatures required, which the reference's Request-Signature button tests; vmangos sends 9 /// even when its `MinPetitionSigns` is lower (`PetitionsHandler.cpp:182`). @@ -189,7 +189,7 @@ pub struct PetitionQueryResponse { pub allowed_classes: u32, /// Race mask restriction, 0 on vmangos. pub allowed_races: u32, - /// Gender restriction: a `u16`, the packet's one odd width (`Packets/Petition.cpp:94`). + /// Gender restriction: a `u16`, the packet's one odd width (`Packets/Petition.cpp:95`). pub allowed_gender: u16, /// Minimum level, 0 on vmangos. pub allowed_min_level: u32, diff --git a/crates/benilla-protocol/src/messages/progression.rs b/crates/benilla-protocol/src/messages/progression.rs index 962afb77..8ba7843c 100644 --- a/crates/benilla-protocol/src/messages/progression.rs +++ b/crates/benilla-protocol/src/messages/progression.rs @@ -16,8 +16,8 @@ pub struct XpGain { } /// Read `SMSG_LOG_XPGAIN`: `u64 victim, u32 total, u8 type` (0 kill, 1 other), then for a kill -/// `u32 base, f32 groupBonus`. Deviation: the group bonus is read and dropped; vmangos always -/// sends 1.0, meaning none (`Player.cpp:3039`), so 1.12 shows no group-bonus line either. +/// `u32 base, f32 groupBonus`. The group bonus is read and dropped, its chat line not built; +/// vmangos always sends 1.0, meaning none (`Player.cpp:3039`). pub(super) fn read_xp_gain(r: &mut impl Read) -> io::Result { let victim = read_u64_le(r)?; let total = read_u32_le(r)?; diff --git a/crates/benilla-protocol/src/messages/quest/giver.rs b/crates/benilla-protocol/src/messages/quest/giver.rs index 56edfa86..473a8aaf 100644 --- a/crates/benilla-protocol/src/messages/quest/giver.rs +++ b/crates/benilla-protocol/src/messages/quest/giver.rs @@ -116,7 +116,7 @@ pub fn questgiver_status_query(npc: u64) -> Vec { } /// Body of `CMSG_QUESTGIVER_HELLO` (`Quest.cpp:16-19`): the NPC guid. The server answers it like -/// `CMSG_GOSSIP_HELLO`, through `SendPreparedGossip` (`QuestHandler.cpp:103`). +/// `CMSG_GOSSIP_HELLO`, through `SendPreparedGossip` (`QuestHandler.cpp:104`). pub fn questgiver_hello(npc: u64) -> Vec { npc.to_le_bytes().to_vec() } diff --git a/crates/benilla-protocol/src/messages/spellbook.rs b/crates/benilla-protocol/src/messages/spellbook.rs index dd946de7..81456ee7 100644 --- a/crates/benilla-protocol/src/messages/spellbook.rs +++ b/crates/benilla-protocol/src/messages/spellbook.rs @@ -41,28 +41,29 @@ pub(super) fn read_initial_spells(r: &mut impl Read) -> io::Result<(Vec, Ve Ok((spells, cooldowns)) } -/// Read `SMSG_LEARNED_SPELL` (vmangos `Spell.cpp:175-179`): `u16 spellId`, then an action-bar slot -/// the client does not use. The one message that adds to the book after login. +/// Read `SMSG_LEARNED_SPELL` (vmangos `Server/Packets/Spell.cpp:175-179`): `u16 spellId`, then +/// an action-bar slot the client does not use. The one message that adds to the book after login. pub(super) fn read_learned_spell(r: &mut impl Read) -> io::Result { let spell_id = read_u16_le(r)?; let _action_bar_slot = read_u16_le(r)?; Ok(spell_id) } -/// Read `SMSG_REMOVED_SPELL` (vmangos `Spell.cpp:181`): a bare `u16 spellId`, no action-bar slot. +/// Read `SMSG_REMOVED_SPELL` (vmangos `Server/Packets/Spell.cpp:181`): a bare `u16 spellId`, no +/// action-bar slot. pub(super) fn read_removed_spell(r: &mut impl Read) -> io::Result { read_u16_le(r) } -/// Read `SMSG_SUPERCEDED_SPELL` (vmangos `Spell.cpp:169-173`): `(oldSpellId, newSpellId)`; a -/// rank-up replaces the old spell in both the book and the action bar. +/// Read `SMSG_SUPERCEDED_SPELL` (vmangos `Server/Packets/Spell.cpp:169-173`): `(oldSpellId, +/// newSpellId)`; a rank-up replaces the old spell in both the book and the action bar. pub(super) fn read_superceded_spell(r: &mut impl Read) -> io::Result<(u16, u16)> { Ok((read_u16_le(r)?, read_u16_le(r)?)) } -/// Read `SMSG_SPELL_COOLDOWN` (vmangos `Spell.cpp:142-151`, client `0x6e9460`): a `u64` caster, -/// then `(spell, ms)` pairs to the end, with no flags byte in 1.12. `ms == 0` means the spell's own -/// `Spell.dbc` recovery times. Sent for school lockouts and pet cooldowns. +/// Read `SMSG_SPELL_COOLDOWN` (vmangos `Server/Packets/Spell.cpp:142-150`, client `0x6e9460`): a +/// `u64` caster, then `(spell, ms)` pairs to the end, with no flags byte in 1.12. `ms == 0` means +/// the spell's own `Spell.dbc` recovery times. Sent for school lockouts and pet cooldowns. pub(super) fn read_spell_cooldown(r: &mut &[u8]) -> io::Result<(u64, Vec<(u32, u32)>)> { let caster = read_u64_le(r)?; let mut cooldowns = Vec::new(); @@ -74,15 +75,18 @@ pub(super) fn read_spell_cooldown(r: &mut &[u8]) -> io::Result<(u64, Vec<(u32, u Ok((caster, cooldowns)) } -/// Read `SMSG_ITEM_COOLDOWN` (vmangos `Item.cpp:229-233`): `(item_guid, spell_id)`. The client -/// (`0x6e95d0`) gives the item a hardcoded 30 s cooldown; no duration is on the wire. +/// Read `SMSG_ITEM_COOLDOWN` (vmangos `Server/Packets/Item.cpp:229-233`): `(item_guid, spell_id)`, +/// sent when an item with an on-use spell is equipped (`Player::ApplyEquipCooldown`, +/// `Player.cpp:19358-19384`). The client (`0x6e95d0`) gives the item a hardcoded 30 s cooldown; no +/// duration is on the wire. pub(super) fn read_item_cooldown(r: &mut impl Read) -> io::Result<(u64, u32)> { Ok((read_u64_le(r)?, read_u32_le(r)?)) } -/// Read `SMSG_COOLDOWN_EVENT` or `SMSG_CLEAR_COOLDOWN` (vmangos `Spell.cpp:152-167`, client -/// `0x6e9670`): the `u32` spell id first, then the `u64` caster. EVENT starts the timers of an -/// on-hold (`SPELL_ATTR_COOLDOWN_ON_EVENT`) cooldown; CLEAR removes the cooldown. +/// Read `SMSG_COOLDOWN_EVENT` or `SMSG_CLEAR_COOLDOWN` (vmangos +/// `Server/Packets/Spell.cpp:152-167`, client `0x6e9670`): the `u32` spell id first, then the +/// `u64` caster. EVENT starts the timers of an on-hold (`SPELL_ATTR_COOLDOWN_ON_EVENT`) cooldown; +/// CLEAR removes the cooldown. pub(super) fn read_cooldown_event(r: &mut impl Read) -> io::Result<(u32, u64)> { Ok((read_u32_le(r)?, read_u64_le(r)?)) } diff --git a/crates/benilla-protocol/src/messages/spells.rs b/crates/benilla-protocol/src/messages/spells.rs index 1e70c096..9db976ab 100644 --- a/crates/benilla-protocol/src/messages/spells.rs +++ b/crates/benilla-protocol/src/messages/spells.rs @@ -268,8 +268,8 @@ pub(super) fn read_update_aura_duration(r: &mut impl Read) -> io::Result<(u8, u3 Ok((slot, remaining_ms)) } -/// Read `SMSG_PLAY_SPELL_VISUAL` (vmangos `Spell.cpp:54-58`): raw `u64` unit, `u32` kit id. The -/// client (`0x6e98d0`) checks the kit against `SpellVisualKit.dbc` and plays it at stage 0. +/// Read `SMSG_PLAY_SPELL_VISUAL` (vmangos `Packets/Spell.cpp:54-58`): raw `u64` unit, `u32` kit +/// id. The client (`0x6e98d0`) checks the kit against `SpellVisualKit.dbc` and plays it at stage 0. pub(super) fn read_play_spell_visual(r: &mut impl Read) -> io::Result<(u64, u32)> { let unit = read_u64_le(r)?; let kit_id = read_u32_le(r)?; diff --git a/crates/benilla-protocol/src/messages/update_object/fields/unit.rs b/crates/benilla-protocol/src/messages/update_object/fields/unit.rs index f52b8940..1ce69315 100644 --- a/crates/benilla-protocol/src/messages/update_object/fields/unit.rs +++ b/crates/benilla-protocol/src/messages/update_object/fields/unit.rs @@ -99,7 +99,8 @@ impl ObjectFields { self.unit_pet_number() != 0 } /// `UNIT_FIELD_PETNUMBER`: `GetUnitName` (`0x609210`) keys the pet-name cache on it when - /// nonzero; a `HIGHGUID_PET` guid's number is not a substitute, no name query answers it. + /// nonzero. A `HIGHGUID_PET` guid's number is no substitute: a companion's is never filed in + /// its charm info, so no pet-name query answers it. pub fn unit_pet_number(&self) -> u32 { self.get_u32(FIELD_UNIT_PETNUMBER).unwrap_or(0) } diff --git a/crates/benilla-protocol/src/world/writer/chat.rs b/crates/benilla-protocol/src/world/writer/chat.rs index f0cfcde6..d059e147 100644 --- a/crates/benilla-protocol/src/world/writer/chat.rs +++ b/crates/benilla-protocol/src/world/writer/chat.rs @@ -132,9 +132,10 @@ impl WorldWriter { ) } - /// Send an addon message (`SendAddonMessage`). Its language, [`messages::LANGUAGE_ADDON`], is - /// the only mark of addon data (`0x49f920`); vmangos gates it on `AddonChannel` and skips the - /// language gate, flood control and sanitizing. + /// Send an addon message (`SendAddonMessage`): `text` is the caller's composed `prefix` TAB + /// `message`, on the party, raid, guild or battleground lane. Its language, + /// [`messages::LANGUAGE_ADDON`], is the only mark of addon data (`0x49f920`); vmangos gates it + /// on `AddonChannel` and skips the language gate, flood control and sanitizing. pub fn send_addon_message(&mut self, chat_type: u32, text: &str) -> Result<()> { self.send( opcode::CMSG_MESSAGECHAT, diff --git a/crates/benilla-protocol/src/world/writer/gossip.rs b/crates/benilla-protocol/src/world/writer/gossip.rs index 4a9ca716..297a9512 100644 --- a/crates/benilla-protocol/src/world/writer/gossip.rs +++ b/crates/benilla-protocol/src/world/writer/gossip.rs @@ -1,5 +1,6 @@ //! The gossip sends. `CMSG_GOSSIP_HELLO` opens every NPC service window: vmangos accepts it for any -//! interactable creature (`CanInteractWithNPC` with `UNIT_NPC_FLAG_NONE`, `Player.cpp:347`). +//! interactable creature (`GetNPCIfCanInteractWith` with `UNIT_NPC_FLAG_NONE`, +//! `NPCHandler.cpp:347`). use anyhow::Result; diff --git a/crates/benilla-protocol/tests/client.rs b/crates/benilla-protocol/tests/client.rs index e49efe7f..749e6c30 100644 --- a/crates/benilla-protocol/tests/client.rs +++ b/crates/benilla-protocol/tests/client.rs @@ -310,7 +310,8 @@ fn messagechat_sendable_types_golden() { /// `SendAddonMessage` (reference `0x49f920`) sends `CMSG_MESSAGECHAT` on one of four lanes: `u32` /// type, `u32` language `LANG_ADDON`, then one cstring of prefix, TAB and message (`0x49f9b3`); -/// there is no prefix or target field. The receiver splits on the first TAB (`0x49a8d0`). +/// there is no prefix or target field. The receiver splits on the first TAB (`0x49a8d0`); vmangos +/// relays the TAB intact, as it skips `SanitizeChatMessage` for addon chat (`ChatHandler.cpp:49`). #[test] fn addon_message_bodies_golden() { assert_eq!( diff --git a/crates/benilla-protocol/tests/guild.rs b/crates/benilla-protocol/tests/guild.rs index 1cb3bd71..40d54d78 100644 --- a/crates/benilla-protocol/tests/guild.rs +++ b/crates/benilla-protocol/tests/guild.rs @@ -383,7 +383,7 @@ fn guild_roster_with_no_members_is_just_the_head() { } /// A viewer without `GR_RIGHT_VIEWOFFNOTE` gets `""` for every officer note (vmangos -/// `Guild/Guild.cpp:821`, `:844`): the cstring's NUL is still on the wire. +/// `Guild/Guild.cpp:819`, `:846`): the cstring's NUL is still on the wire. #[test] fn guild_roster_reads_the_empty_officer_notes_of_an_unprivileged_viewer() { let mut body = roster_head(2, "motd", "info", &[guild_rank_right::ALL, 0x0000_0003]); diff --git a/crates/benilla-protocol/tests/simple_packets.rs b/crates/benilla-protocol/tests/simple_packets.rs index 5ac6e1af..36bf42a9 100644 --- a/crates/benilla-protocol/tests/simple_packets.rs +++ b/crates/benilla-protocol/tests/simple_packets.rs @@ -309,7 +309,7 @@ fn death_arc_family_parses_and_decodes() { } } - // The movement-flag bits (vmangos `Objects/MovementInfo.h:25-62`). + // The movement-flag bits (vmangos `Objects/MovementInfo.h:28-62`). assert_eq!(MoveMode::Root.flag(), 0x0000_1000); assert_eq!(MoveMode::WaterWalk.flag(), 0x1000_0000); assert_eq!(MoveMode::FeatherFall.flag(), 0x2000_0000); diff --git a/crates/benilla-protocol/tests/spells.rs b/crates/benilla-protocol/tests/spells.rs index 7314dacc..232d4a1f 100644 --- a/crates/benilla-protocol/tests/spells.rs +++ b/crates/benilla-protocol/tests/spells.rs @@ -1031,7 +1031,7 @@ fn combat_log_wire_golden() { } } -/// Raw caster guid, u32 spellId, u32 count, raw guids (vmangos `Spell.cpp:4970-4997`); in the +/// Raw caster guid, u32 spellId, u32 count, raw guids (vmangos `Spell.cpp:4974-4997`); in the /// reference (`0x6e9820`) the only writer of the beams' chain-target array (`unit+0xd44`). #[test] fn spell_chain_targets_wire() { diff --git a/crates/benilla-protocol/tests/trainer.rs b/crates/benilla-protocol/tests/trainer.rs index b8319686..3e4c365e 100644 --- a/crates/benilla-protocol/tests/trainer.rs +++ b/crates/benilla-protocol/tests/trainer.rs @@ -189,7 +189,7 @@ fn trainer_buy_result_wire() { #[test] fn learned_and_superceded_spell_wire() { // SMSG_LEARNED_SPELL: u16 spellId + u16 actionBarSlot, the slot unused by the client and - // dropped (vmangos `Spell.cpp:175-179`). + // dropped (vmangos `Packets/Spell.cpp:175-179`). match messages::parse_server(messages::opcode::SMSG_LEARNED_SPELL, &hx("cb19aaaa")).unwrap() { ServerPacket::LearnedSpell { spell_id } => assert_eq!(spell_id, 6603), other => panic!("learned spell, got {}", other.name()), @@ -201,7 +201,7 @@ fn learned_and_superceded_spell_wire() { other => panic!("spell learned event, got {other:?}"), } - // SMSG_SUPERCEDED_SPELL: u16 oldSpellId + u16 newSpellId (vmangos `Spell.cpp:169-173`). + // SMSG_SUPERCEDED_SPELL: u16 oldSpellId + u16 newSpellId (vmangos `Packets/Spell.cpp:169-173`). match messages::parse_server(messages::opcode::SMSG_SUPERCEDED_SPELL, &hx("cb19cc19")).unwrap() { ServerPacket::SupercededSpell { diff --git a/docs/MAP.md b/docs/MAP.md index e94ece58..a06f1408 100644 --- a/docs/MAP.md +++ b/docs/MAP.md @@ -364,7 +364,7 @@ - `WOW_PARTICLE_DEPTHDUMP_BONES` โ€” benilla-world/src/particles/depthdump.rs - `WOW_PARTICLE_FLAT` โ€” benilla-world/src/particles/render.rs - `WOW_PARTICLE_NODEPTH` โ€” benilla-world/src/particles/render.rs -- `WOW_PASS` โ€” benilla-app/src/login/mod.rs, benilla-app/src/net.rs, benilla-app/src/run_mode.rs +- `WOW_PASS` โ€” benilla-app/src/login/mod.rs, benilla-app/src/net.rs, benilla-app/src/run_mode.rs, benilla-protocol/examples/login_refusal_probe.rs, benilla-protocol/examples/park_probe.rs - `WOW_PERF_HUD` โ€” benilla-app/src/perf/hud.rs - `WOW_PHASE` โ€” benilla-app/src/capture/phase_probe.rs, benilla-app/src/dev.rs, benilla-world/src/bgwin.rs - `WOW_PHASE_AT` โ€” benilla-app/src/capture/phase_probe.rs @@ -493,7 +493,7 @@ - `WOW_UNIT_VISUALS` โ€” benilla-app/src/capture/probes/visual_census.rs, benilla-app/src/dev.rs - `WOW_UNIT_VISUALS_RADIUS` โ€” benilla-app/src/capture/probes/visual_census.rs - `WOW_UPLOAD_BUDGET` โ€” benilla-world/src/world_plugins.rs -- `WOW_USER` โ€” benilla-app/src/login/mod.rs, benilla-app/src/net.rs, benilla-app/src/run_mode.rs +- `WOW_USER` โ€” benilla-app/src/login/mod.rs, benilla-app/src/net.rs, benilla-app/src/run_mode.rs, benilla-protocol/examples/login_refusal_probe.rs, benilla-protocol/examples/park_probe.rs - `WOW_VIS_DUMP` โ€” benilla-app/src/capture/probes/live_fps.rs - `WOW_VIS_TRACE` โ€” benilla-world/src/model_render/visibility.rs - `WOW_VISTA_AT` โ€” benilla-app/src/capture/mod.rs