fix: SMSG_IGNORE_LIST read phantom string field after each GUID

The packet only contains uint8 count + count×uint64 GUIDs, but the
handler called readString() after each GUID. This consumed raw bytes of
subsequent GUIDs as a string, corrupting all entries after the first.
Now stores GUIDs in ignoreListGuids_ and resolves names asynchronously
via SMSG_NAME_QUERY_RESPONSE, matching the friends list pattern.

Also fixes unsafe static_pointer_cast in ready check (no type guard)
and removes redundant packetHasRemaining wrapper (duplicates Packet API).
This commit is contained in:
Kelsi 2026-03-29 18:11:29 -07:00
parent 0e814e9c4a
commit 35b952bc6f
3 changed files with 27 additions and 15 deletions

View file

@ -2420,7 +2420,8 @@ private:
std::vector<FactionStandingInit> initialFactions_;
// ---- Ignore list cache ----
std::unordered_map<std::string, uint64_t> ignoreCache; // name -> guid
std::unordered_map<std::string, uint64_t> ignoreCache; // name -> guid (UI display)
std::unordered_set<uint64_t> ignoreListGuids_; // authoritative GUID set from server
// ---- Logout state ----
bool loggingOut_ = false;

View file

@ -1996,6 +1996,12 @@ void EntityController::handleNameQueryResponse(network::Packet& packet) {
owner_.friendsCache[data.name] = data.guid;
}
// Backfill ignore list: SMSG_IGNORE_LIST only contains GUIDs, so
// ignoreCache (name→guid for UI) is populated here once names resolve.
if (owner_.ignoreListGuids_.count(data.guid)) {
owner_.ignoreCache[data.name] = data.guid;
}
// Fire UNIT_NAME_UPDATE so nameplate/unit frame addons know the name is available
if (owner_.addonEventCallback_) {
std::string unitId;

View file

@ -18,11 +18,7 @@ namespace wowee {
namespace game {
static bool packetHasRemaining(const network::Packet& packet, size_t need) {
const size_t size = packet.getSize();
const size_t pos = packet.getReadPos();
return pos <= size && need <= (size - pos);
}
static const char* lfgJoinResultString(uint8_t result) {
switch (result) {
@ -98,13 +94,19 @@ void SocialHandler::registerOpcodes(DispatchTable& table) {
table[Opcode::SMSG_CONTACT_LIST] = [this](network::Packet& packet) { handleContactList(packet); };
table[Opcode::SMSG_FRIEND_LIST] = [this](network::Packet& packet) { handleFriendList(packet); };
table[Opcode::SMSG_IGNORE_LIST] = [this](network::Packet& packet) {
if (packet.getSize() - packet.getReadPos() < 1) return;
// Format: uint8 count + count × uint64 guid (no name strings in packet).
// Names are resolved via SMSG_NAME_QUERY_RESPONSE after the list arrives.
if (!packet.hasRemaining(1)) return;
uint8_t ignCount = packet.readUInt8();
owner_.ignoreListGuids_.clear();
for (uint8_t i = 0; i < ignCount; ++i) {
if (packet.getSize() - packet.getReadPos() < 8) break;
if (!packet.hasRemaining(8)) break;
uint64_t ignGuid = packet.readUInt64();
std::string ignName = packet.readString();
if (!ignName.empty() && ignGuid != 0) owner_.ignoreCache[ignName] = ignGuid;
if (ignGuid != 0) {
owner_.ignoreListGuids_.insert(ignGuid);
// Query name so UI can display it later
owner_.queryPlayerName(ignGuid);
}
}
LOG_DEBUG("SMSG_IGNORE_LIST: loaded ", (int)ignCount, " ignored players");
};
@ -176,8 +178,11 @@ void SocialHandler::registerOpcodes(DispatchTable& table) {
std::string rname;
if (nit != owner_.getPlayerNameCache().end()) rname = nit->second;
else {
// Only cast to Unit if the entity actually is one — a raw
// static_pointer_cast on a GameObject would be undefined behavior.
auto ent = owner_.getEntityManager().getEntity(respGuid);
if (ent) rname = std::static_pointer_cast<game::Unit>(ent)->getName();
if (ent && (ent->getType() == ObjectType::UNIT || ent->getType() == ObjectType::PLAYER))
rname = std::static_pointer_cast<game::Unit>(ent)->getName();
}
if (!rname.empty()) {
bool found = false;
@ -2151,7 +2156,7 @@ void SocialHandler::handleLfgUpdatePlayer(network::Packet& packet) {
}
void SocialHandler::handleLfgPlayerReward(network::Packet& packet) {
if (!packetHasRemaining(packet, 13)) return;
if (!packet.hasRemaining( 13)) return;
packet.readUInt32(); packet.readUInt32(); packet.readUInt8();
uint32_t money = packet.readUInt32();
uint32_t xp = packet.readUInt32();
@ -2161,9 +2166,9 @@ void SocialHandler::handleLfgPlayerReward(network::Packet& packet) {
else if (silver > 0) snprintf(moneyBuf, sizeof(moneyBuf), "%us %uc", silver, copper);
else snprintf(moneyBuf, sizeof(moneyBuf), "%uc", copper);
std::string rewardMsg = std::string("Dungeon Finder reward: ") + moneyBuf + ", " + std::to_string(xp) + " XP";
if (packetHasRemaining(packet, 4)) {
if (packet.hasRemaining( 4)) {
uint32_t rewardCount = packet.readUInt32();
for (uint32_t i = 0; i < rewardCount && packetHasRemaining(packet, 9); ++i) {
for (uint32_t i = 0; i < rewardCount && packet.hasRemaining( 9); ++i) {
uint32_t itemId = packet.readUInt32();
uint32_t itemCount = packet.readUInt32();
packet.readUInt8();
@ -2184,7 +2189,7 @@ void SocialHandler::handleLfgPlayerReward(network::Packet& packet) {
}
void SocialHandler::handleLfgBootProposalUpdate(network::Packet& packet) {
if (!packetHasRemaining(packet, 23)) return;
if (!packet.hasRemaining( 23)) return;
bool inProgress = packet.readUInt8() != 0;
packet.readUInt8(); packet.readUInt8();
uint32_t totalVotes = packet.readUInt32();