- the Character window's third tab is in: faction rows under their headers, standing bars, the detail popup and the watch bar, at the reference's own anchors - all four reputation opcodes parse byte-exact, the flag byte reads the way the client reads it, and a standing gain reveals the faction unless it is hidden - an end-to-end golden drives live wire bytes through the game data to the exact visible row list - the reference-code overlap is measured by an instrument instead of asserted — the start of a debt paid in full later in the day Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
81 lines
4.1 KiB
Rust
81 lines
4.1 KiB
Rust
//! The reputation pane's wire: the pane's three client verbs, byte-exact against the vmangos
|
|
//! reader side, and the one server push (`SMSG_SET_FACTION_VISIBLE`) that carries no standing.
|
|
//!
|
|
//! The standing-bearing pair (`SMSG_INITIALIZE_FACTIONS` / `SMSG_SET_FACTION_STANDING`) already
|
|
//! has its goldens in `tests/simple_packets.rs`, where it landed with the unit-reaction decode.
|
|
|
|
mod common;
|
|
|
|
use benilla_protocol::events::{decode, SessionEvent};
|
|
use benilla_protocol::messages::{self, opcode};
|
|
use benilla_protocol::ServerPacket;
|
|
use common::hx;
|
|
|
|
/// `CMSG_SET_FACTION_ATWAR` (293): `u32 repListId`, `u8 flag` — the pane's crossed-swords box.
|
|
///
|
|
/// Byte-exact against vmangos's reader (`WorldPackets::Misc::SetFactionAtWar::ReadFromWorldPacket`,
|
|
/// `Server/Packets/Misc.cpp`, over the `uint32 repListId` / `uint8 flag` fields in `Misc.h`), which
|
|
/// is the side that has to parse what we send.
|
|
#[test]
|
|
fn set_faction_at_war_body_is_slot_then_flag() {
|
|
assert_eq!(opcode::CMSG_SET_FACTION_ATWAR, 293);
|
|
// Slot 21 (Booty Bay is reputationIndex 1; 21 is Darnassus's), war ON.
|
|
assert_eq!(messages::set_faction_at_war(21, true), hx("1500000001"));
|
|
// …and OFF. The flag is a whole byte, not a bit — `0`, never a cleared bit in a mask.
|
|
assert_eq!(messages::set_faction_at_war(21, false), hx("1500000000"));
|
|
// Slot 0 is a REAL faction (the Bloodsail Buccaneers hold reputationIndex 0), so a zero slot
|
|
// is an ordinary request here — the "nothing" sentinel problem is the watched verb's alone.
|
|
assert_eq!(messages::set_faction_at_war(0, true), hx("0000000001"));
|
|
}
|
|
|
|
/// `CMSG_SET_FACTION_INACTIVE` (791): `u32 repListId`, `u8 inactive` — the "move to inactive" box.
|
|
///
|
|
/// Byte-exact against `WorldPackets::Misc::SetFactionInactive::ReadFromWorldPacket`.
|
|
#[test]
|
|
fn set_faction_inactive_body_is_slot_then_flag() {
|
|
assert_eq!(opcode::CMSG_SET_FACTION_INACTIVE, 791);
|
|
assert_eq!(messages::set_faction_inactive(54, true), hx("3600000001"));
|
|
assert_eq!(messages::set_faction_inactive(54, false), hx("3600000000"));
|
|
}
|
|
|
|
/// `CMSG_SET_WATCHED_FACTION` (792): one **signed** `i32` slot, `-1` for "watch nothing".
|
|
///
|
|
/// The signedness is the whole point and is why this has its own assert: vmangos writes the value
|
|
/// straight into `PLAYER_FIELD_WATCHED_FACTION_INDEX` with `SetInt32Value`
|
|
/// (`HandleSetWatchedFactionOpcode`), and slot `0` is a real faction — so a `0` here would watch the
|
|
/// Bloodsail Buccaneers rather than clear the bar. FrameXML's `SetWatchedFactionIndex(0)` means "no
|
|
/// display row"; translating that to [`messages::WATCHED_FACTION_NONE`] is the binding's job, above
|
|
/// this layer, and this test pins the two values apart so the translation cannot be skipped silently.
|
|
#[test]
|
|
fn set_watched_faction_body_is_a_signed_slot_and_none_is_minus_one() {
|
|
assert_eq!(opcode::CMSG_SET_WATCHED_FACTION, 792);
|
|
assert_eq!(messages::WATCHED_FACTION_NONE, -1);
|
|
assert_eq!(messages::set_watched_faction(11), hx("0b000000"));
|
|
assert_eq!(
|
|
messages::set_watched_faction(messages::WATCHED_FACTION_NONE),
|
|
hx("ffffffff")
|
|
);
|
|
// The trap, asserted: watching slot 0 and watching nothing are different bytes.
|
|
assert_ne!(
|
|
messages::set_watched_faction(0),
|
|
messages::set_watched_faction(messages::WATCHED_FACTION_NONE)
|
|
);
|
|
}
|
|
|
|
/// `SMSG_SET_FACTION_VISIBLE` (291): one `u32` reputation-list slot, no standing
|
|
/// (vmangos `ReputationMgr::SendVisible` / `SetFactionVisible::AppendBodyTo`).
|
|
///
|
|
/// The server pushes it the first time the player meets a faction. A client that drops it keeps a
|
|
/// correct standing for a row the pane will not list — which is exactly the failure this parse arm
|
|
/// exists to prevent, so the decode leg is asserted too.
|
|
#[test]
|
|
fn set_faction_visible_parses_and_decodes() {
|
|
assert_eq!(opcode::SMSG_SET_FACTION_VISIBLE, 291);
|
|
let p = messages::parse_server(opcode::SMSG_SET_FACTION_VISIBLE, &hx("0d000000")).unwrap();
|
|
assert!(matches!(p, ServerPacket::SetFactionVisible { list_id: 13 }));
|
|
assert!(matches!(
|
|
decode(p)[..],
|
|
[SessionEvent::ReputationVisible { list_id: 13 }]
|
|
));
|
|
}
|