1,039 comment lines become 472 across 19 files; the code is unchanged. The movement test's claim that write() never serializes a transport tail is gone: it does. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
100 lines
3.7 KiB
Rust
100 lines
3.7 KiB
Rust
//! The party quest-share wire: the Share Quest push, the escort confirm, and the verdict relay.
|
|
//! `MSG_QUEST_PUSH_RESULT`'s guid is the sharer going up and the member concerned coming down.
|
|
|
|
mod common;
|
|
|
|
use benilla_protocol::events::{decode, SessionEvent};
|
|
use benilla_protocol::messages::{self, opcode, quest_flags, QuestShareMsg};
|
|
use benilla_protocol::ServerPacket;
|
|
use common::hx;
|
|
|
|
/// The three CMSG bodies (vmangos `Server/Packets/Quest.cpp:63-77`).
|
|
#[test]
|
|
fn cmsg_bodies_golden() {
|
|
// CMSG_PUSHQUESTTOPARTY / CMSG_QUEST_CONFIRM_ACCEPT: one u32 quest id and no member guid; the
|
|
// server addresses the group itself.
|
|
assert_eq!(
|
|
messages::push_quest_to_party(0x1234),
|
|
hx("34120000"),
|
|
"CMSG_PUSHQUESTTOPARTY body"
|
|
);
|
|
assert_eq!(
|
|
messages::quest_confirm_accept(0x1234),
|
|
hx("34120000"),
|
|
"CMSG_QUEST_CONFIRM_ACCEPT body"
|
|
);
|
|
|
|
// MSG_QUEST_PUSH_RESULT going up: the u64 guid of the sharer we answer, then the u8 verdict
|
|
// (`Quest.cpp:73-77`).
|
|
assert_eq!(
|
|
messages::quest_push_result(0x1234_5678_9abc_def0, QuestShareMsg::DECLINE_QUEST),
|
|
hx("f0debc9a7856341203"),
|
|
"MSG_QUEST_PUSH_RESULT (client) body"
|
|
);
|
|
}
|
|
|
|
/// `MSG_QUEST_PUSH_RESULT` coming down (`Quest.cpp:81-85`): the same two fields, the guid now the
|
|
/// party member the verdict concerns.
|
|
#[test]
|
|
fn push_result_decodes_member_and_verdict() {
|
|
let body = hx("f0debc9a7856341200");
|
|
let p = messages::parse_server(opcode::MSG_QUEST_PUSH_RESULT, &body).unwrap();
|
|
match &p {
|
|
ServerPacket::QuestPushResult(r) => {
|
|
assert_eq!(r.member, 0x1234_5678_9abc_def0);
|
|
assert_eq!(r.msg, QuestShareMsg::SHARING_QUEST);
|
|
}
|
|
other => panic!("expected QuestPushResult, got {}", other.name()),
|
|
}
|
|
match decode(p).as_slice() {
|
|
[SessionEvent::QuestPushResult { member, msg }] => {
|
|
assert_eq!(*member, 0x1234_5678_9abc_def0);
|
|
assert_eq!(*msg, QuestShareMsg::SHARING_QUEST);
|
|
}
|
|
other => panic!("push result decoded to {} events", other.len()),
|
|
}
|
|
}
|
|
|
|
/// An unmapped verdict byte reaches the app as data rather than failing the parse.
|
|
#[test]
|
|
fn every_verdict_byte_round_trips() {
|
|
for raw in [0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xFF] {
|
|
let mut body = 1u64.to_le_bytes().to_vec();
|
|
body.push(raw);
|
|
match messages::parse_server(opcode::MSG_QUEST_PUSH_RESULT, &body).unwrap() {
|
|
ServerPacket::QuestPushResult(r) => {
|
|
assert_eq!(r.msg, QuestShareMsg(raw), "verdict {raw}");
|
|
assert_eq!(r.member, 1);
|
|
}
|
|
other => panic!("verdict {raw} parsed as {}", other.name()),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// `SMSG_QUEST_CONFIRM_ACCEPT` (`Quest.cpp:131-136`): `u32` questId, cstring title, `u64`
|
|
/// senderGuid; the title sits between the two numbers.
|
|
#[test]
|
|
fn confirm_accept_decodes_id_title_sender() {
|
|
let mut body = 1234u32.to_le_bytes().to_vec();
|
|
body.extend_from_slice(b"Escort Duty\0");
|
|
body.extend_from_slice(&0x0000_0000_0000_002Au64.to_le_bytes());
|
|
|
|
let p = messages::parse_server(opcode::SMSG_QUEST_CONFIRM_ACCEPT, &body).unwrap();
|
|
match decode(p).as_slice() {
|
|
[SessionEvent::QuestConfirmAccept(c)] => {
|
|
assert_eq!(c.quest_id, 1234);
|
|
assert_eq!(c.title, "Escort Duty");
|
|
assert_eq!(c.sender, 0x2A);
|
|
}
|
|
other => panic!("confirm accept decoded to {} events", other.len()),
|
|
}
|
|
}
|
|
|
|
/// The two quest flags the share flow reads (vmangos `QuestDef.h:145-160`), the client's only
|
|
/// sign that a quest is shareable or an escort.
|
|
#[test]
|
|
fn share_quest_flags_are_the_documented_bits() {
|
|
assert_eq!(quest_flags::PARTY_ACCEPT, 0x2);
|
|
assert_eq!(quest_flags::SHARABLE, 0x8);
|
|
}
|