mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
game: implement Classic SMSG_FRIEND_LIST and full SMSG_CONTACT_LIST parsing
Classic 1.12 and TBC use SMSG_FRIEND_LIST (not SMSG_CONTACT_LIST) to send the initial friend list at login. Previously this packet was silently dropped, leaving friendsCache empty and breaking /friend remove and note operations for Classic players. - Add handleFriendList(): parses Classic format (u8 count, then per-entry: u64 guid + u8 status + optional area/level/class if online) - Add handleContactList(): fully parses WotLK SMSG_CONTACT_LIST entries (previously only read mask+count header and dropped all entries) - Both handlers populate friendGuids_ and call queryPlayerName() for unknown GUIDs; handleNameQueryResponse() now backfills friendsCache when a name resolves for a known friend GUID - Clear friendGuids_ on disconnect alongside playerNameCache
This commit is contained in:
parent
ab0828a4ce
commit
23878e530f
2 changed files with 102 additions and 18 deletions
|
|
@ -1537,6 +1537,8 @@ private:
|
|||
void handleWho(network::Packet& packet);
|
||||
|
||||
// ---- Social handlers ----
|
||||
void handleFriendList(network::Packet& packet); // Classic SMSG_FRIEND_LIST
|
||||
void handleContactList(network::Packet& packet); // WotLK SMSG_CONTACT_LIST (full parse)
|
||||
void handleFriendStatus(network::Packet& packet);
|
||||
void handleRandomRoll(network::Packet& packet);
|
||||
|
||||
|
|
@ -1656,6 +1658,7 @@ private:
|
|||
|
||||
// ---- Friend list cache ----
|
||||
std::unordered_map<std::string, uint64_t> friendsCache; // name -> guid
|
||||
std::unordered_set<uint64_t> friendGuids_; // all known friend GUIDs (for name backfill)
|
||||
uint32_t lastContactListMask_ = 0;
|
||||
uint32_t lastContactListCount_ = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -482,6 +482,7 @@ void GameHandler::disconnect() {
|
|||
activeCharacterGuid_ = 0;
|
||||
playerNameCache.clear();
|
||||
pendingNameQueries.clear();
|
||||
friendGuids_.clear();
|
||||
transportAttachments_.clear();
|
||||
serverUpdatedTransportGuids_.clear();
|
||||
requiresWarden_ = false;
|
||||
|
|
@ -1484,27 +1485,15 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
handleFriendStatus(packet);
|
||||
}
|
||||
break;
|
||||
case Opcode::SMSG_CONTACT_LIST: {
|
||||
// Known variants:
|
||||
// - Full form: uint32 listMask, uint32 count, then variable-size entries.
|
||||
// - Minimal/legacy keepalive-ish form observed on some servers: 1 byte.
|
||||
size_t remaining = packet.getSize() - packet.getReadPos();
|
||||
if (remaining >= 8) {
|
||||
lastContactListMask_ = packet.readUInt32();
|
||||
lastContactListCount_ = packet.readUInt32();
|
||||
} else if (remaining == 1) {
|
||||
/*uint8_t marker =*/ packet.readUInt8();
|
||||
lastContactListMask_ = 0;
|
||||
lastContactListCount_ = 0;
|
||||
} else if (remaining > 0) {
|
||||
// Unknown short variant: consume to keep stream aligned, no warning spam.
|
||||
packet.setReadPos(packet.getSize());
|
||||
}
|
||||
case Opcode::SMSG_CONTACT_LIST:
|
||||
handleContactList(packet);
|
||||
break;
|
||||
}
|
||||
case Opcode::SMSG_FRIEND_LIST:
|
||||
// Classic 1.12 and TBC friend list (WotLK uses SMSG_CONTACT_LIST instead)
|
||||
handleFriendList(packet);
|
||||
break;
|
||||
case Opcode::SMSG_IGNORE_LIST:
|
||||
// Legacy social list variants; CONTACT_LIST is primary in modern flow.
|
||||
// Ignore list: consume to avoid spurious warnings; not parsed.
|
||||
packet.setReadPos(packet.getSize());
|
||||
break;
|
||||
|
||||
|
|
@ -9909,6 +9898,12 @@ void GameHandler::handleNameQueryResponse(network::Packet& packet) {
|
|||
mail.senderName = data.name;
|
||||
}
|
||||
}
|
||||
|
||||
// Backfill friend list: if this GUID came from a friend list packet,
|
||||
// register the name in friendsCache now that we know it.
|
||||
if (friendGuids_.count(data.guid)) {
|
||||
friendsCache[data.name] = data.guid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -16429,6 +16424,92 @@ void GameHandler::handleWho(network::Packet& packet) {
|
|||
}
|
||||
}
|
||||
|
||||
void GameHandler::handleFriendList(network::Packet& packet) {
|
||||
// Classic 1.12 / TBC 2.4.3 SMSG_FRIEND_LIST format:
|
||||
// uint8 count
|
||||
// for each entry:
|
||||
// uint64 guid (full)
|
||||
// uint8 status (0=offline, 1=online, 2=AFK, 3=DND)
|
||||
// if status != 0:
|
||||
// uint32 area
|
||||
// uint32 level
|
||||
// uint32 class
|
||||
auto rem = [&]() { return packet.getSize() - packet.getReadPos(); };
|
||||
if (rem() < 1) return;
|
||||
uint8_t count = packet.readUInt8();
|
||||
LOG_INFO("SMSG_FRIEND_LIST: ", (int)count, " entries");
|
||||
for (uint8_t i = 0; i < count && rem() >= 9; ++i) {
|
||||
uint64_t guid = packet.readUInt64();
|
||||
uint8_t status = packet.readUInt8();
|
||||
uint32_t area = 0, level = 0, classId = 0;
|
||||
if (status != 0 && rem() >= 12) {
|
||||
area = packet.readUInt32();
|
||||
level = packet.readUInt32();
|
||||
classId = packet.readUInt32();
|
||||
}
|
||||
(void)area; (void)level; (void)classId;
|
||||
// Track as a friend GUID; resolve name via name query
|
||||
friendGuids_.insert(guid);
|
||||
auto nit = playerNameCache.find(guid);
|
||||
if (nit != playerNameCache.end()) {
|
||||
friendsCache[nit->second] = guid;
|
||||
LOG_INFO(" Friend: ", nit->second, " status=", (int)status);
|
||||
} else {
|
||||
LOG_INFO(" Friend guid=0x", std::hex, guid, std::dec,
|
||||
" status=", (int)status, " (name pending)");
|
||||
queryPlayerName(guid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameHandler::handleContactList(network::Packet& packet) {
|
||||
// WotLK SMSG_CONTACT_LIST format:
|
||||
// uint32 listMask (1=friend, 2=ignore, 4=mute)
|
||||
// uint32 count
|
||||
// for each entry:
|
||||
// uint64 guid (full)
|
||||
// uint32 flags
|
||||
// string note (null-terminated)
|
||||
// if flags & 0x1 (friend):
|
||||
// uint8 status (0=offline, 1=online, 2=AFK, 3=DND)
|
||||
// if status != 0:
|
||||
// uint32 area, uint32 level, uint32 class
|
||||
// Short/keepalive variant (1-7 bytes): consume silently.
|
||||
auto rem = [&]() { return packet.getSize() - packet.getReadPos(); };
|
||||
if (rem() < 8) {
|
||||
packet.setReadPos(packet.getSize());
|
||||
return;
|
||||
}
|
||||
lastContactListMask_ = packet.readUInt32();
|
||||
lastContactListCount_ = packet.readUInt32();
|
||||
for (uint32_t i = 0; i < lastContactListCount_ && rem() >= 8; ++i) {
|
||||
uint64_t guid = packet.readUInt64();
|
||||
if (rem() < 4) break;
|
||||
uint32_t flags = packet.readUInt32();
|
||||
std::string note = packet.readString(); // may be empty
|
||||
(void)note;
|
||||
if (flags & 0x1) { // SOCIAL_FLAG_FRIEND
|
||||
if (rem() < 1) break;
|
||||
uint8_t status = packet.readUInt8();
|
||||
if (status != 0 && rem() >= 12) {
|
||||
packet.readUInt32(); // area
|
||||
packet.readUInt32(); // level
|
||||
packet.readUInt32(); // class
|
||||
}
|
||||
friendGuids_.insert(guid);
|
||||
auto nit = playerNameCache.find(guid);
|
||||
if (nit != playerNameCache.end()) {
|
||||
friendsCache[nit->second] = guid;
|
||||
} else {
|
||||
queryPlayerName(guid);
|
||||
}
|
||||
}
|
||||
// ignore / mute entries: no additional fields beyond guid+flags+note
|
||||
}
|
||||
LOG_INFO("SMSG_CONTACT_LIST: mask=", lastContactListMask_,
|
||||
" count=", lastContactListCount_);
|
||||
}
|
||||
|
||||
void GameHandler::handleFriendStatus(network::Packet& packet) {
|
||||
FriendStatusData data;
|
||||
if (!FriendStatusParser::parse(packet, data)) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue