mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-03 00:03:50 +00:00
feat: parse SMSG_RESPOND_INSPECT_ACHIEVEMENTS and request on inspect
When the player inspects another player on WotLK 3.3.5a, also send CMSG_QUERY_INSPECT_ACHIEVEMENTS so the server responds with SMSG_RESPOND_INSPECT_ACHIEVEMENTS. The new handler parses the achievement-id/date sentinel-terminated block (same layout as SMSG_ALL_ACHIEVEMENT_DATA but prefixed with a packed guid) and stores the earned achievement IDs keyed by GUID in inspectedPlayerAchievements_. The new public getter getInspectedPlayerAchievements(guid) exposes this data for the inspect UI. The cache is cleared on world entry to prevent stale data. QueryInspectAchievementsPacket::build() handles the CMSG wire format (uint64 guid + uint8 unk=0).
This commit is contained in:
parent
0089b3a160
commit
1d9dc6dcae
4 changed files with 87 additions and 1 deletions
|
|
@ -1611,6 +1611,12 @@ public:
|
||||||
auto it = achievementPointsCache_.find(id);
|
auto it = achievementPointsCache_.find(id);
|
||||||
return (it != achievementPointsCache_.end()) ? it->second : 0u;
|
return (it != achievementPointsCache_.end()) ? it->second : 0u;
|
||||||
}
|
}
|
||||||
|
/// Returns the set of achievement IDs earned by an inspected player (via SMSG_RESPOND_INSPECT_ACHIEVEMENTS).
|
||||||
|
/// Returns nullptr if no inspect data is available for the given GUID.
|
||||||
|
const std::unordered_set<uint32_t>* getInspectedPlayerAchievements(uint64_t guid) const {
|
||||||
|
auto it = inspectedPlayerAchievements_.find(guid);
|
||||||
|
return (it != inspectedPlayerAchievements_.end()) ? &it->second : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Server-triggered music callback — fires when SMSG_PLAY_MUSIC is received.
|
// Server-triggered music callback — fires when SMSG_PLAY_MUSIC is received.
|
||||||
// The soundId corresponds to a SoundEntries.dbc record. The receiver is
|
// The soundId corresponds to a SoundEntries.dbc record. The receiver is
|
||||||
|
|
@ -2835,6 +2841,11 @@ private:
|
||||||
std::unordered_map<uint32_t, uint64_t> criteriaProgress_;
|
std::unordered_map<uint32_t, uint64_t> criteriaProgress_;
|
||||||
void handleAllAchievementData(network::Packet& packet);
|
void handleAllAchievementData(network::Packet& packet);
|
||||||
|
|
||||||
|
// Per-player achievement data from SMSG_RESPOND_INSPECT_ACHIEVEMENTS
|
||||||
|
// Key: inspected player's GUID; value: set of earned achievement IDs
|
||||||
|
std::unordered_map<uint64_t, std::unordered_set<uint32_t>> inspectedPlayerAchievements_;
|
||||||
|
void handleRespondInspectAchievements(network::Packet& packet);
|
||||||
|
|
||||||
// Area name cache (lazy-loaded from WorldMapArea.dbc; maps AreaTable ID → display name)
|
// Area name cache (lazy-loaded from WorldMapArea.dbc; maps AreaTable ID → display name)
|
||||||
std::unordered_map<uint32_t, std::string> areaNameCache_;
|
std::unordered_map<uint32_t, std::string> areaNameCache_;
|
||||||
bool areaNameCacheLoaded_ = false;
|
bool areaNameCacheLoaded_ = false;
|
||||||
|
|
|
||||||
|
|
@ -1448,6 +1448,12 @@ public:
|
||||||
static network::Packet build(uint64_t targetGuid);
|
static network::Packet build(uint64_t targetGuid);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** CMSG_QUERY_INSPECT_ACHIEVEMENTS packet builder (WotLK 3.3.5a) */
|
||||||
|
class QueryInspectAchievementsPacket {
|
||||||
|
public:
|
||||||
|
static network::Packet build(uint64_t targetGuid);
|
||||||
|
};
|
||||||
|
|
||||||
/** CMSG_NAME_QUERY packet builder */
|
/** CMSG_NAME_QUERY packet builder */
|
||||||
class NameQueryPacket {
|
class NameQueryPacket {
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
|
|
@ -6884,10 +6884,12 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
||||||
case Opcode::SMSG_REDIRECT_CLIENT:
|
case Opcode::SMSG_REDIRECT_CLIENT:
|
||||||
case Opcode::SMSG_PVP_QUEUE_STATS:
|
case Opcode::SMSG_PVP_QUEUE_STATS:
|
||||||
case Opcode::SMSG_NOTIFY_DEST_LOC_SPELL_CAST:
|
case Opcode::SMSG_NOTIFY_DEST_LOC_SPELL_CAST:
|
||||||
case Opcode::SMSG_RESPOND_INSPECT_ACHIEVEMENTS:
|
|
||||||
case Opcode::SMSG_PLAYER_SKINNED:
|
case Opcode::SMSG_PLAYER_SKINNED:
|
||||||
packet.setReadPos(packet.getSize());
|
packet.setReadPos(packet.getSize());
|
||||||
break;
|
break;
|
||||||
|
case Opcode::SMSG_RESPOND_INSPECT_ACHIEVEMENTS:
|
||||||
|
handleRespondInspectAchievements(packet);
|
||||||
|
break;
|
||||||
case Opcode::SMSG_QUEST_POI_QUERY_RESPONSE:
|
case Opcode::SMSG_QUEST_POI_QUERY_RESPONSE:
|
||||||
handleQuestPoiQueryResponse(packet);
|
handleQuestPoiQueryResponse(packet);
|
||||||
break;
|
break;
|
||||||
|
|
@ -8039,6 +8041,9 @@ void GameHandler::handleLoginVerifyWorld(network::Packet& packet) {
|
||||||
encounterUnitGuids_.fill(0);
|
encounterUnitGuids_.fill(0);
|
||||||
raidTargetGuids_.fill(0);
|
raidTargetGuids_.fill(0);
|
||||||
|
|
||||||
|
// Clear inspect caches on world entry to avoid showing stale data
|
||||||
|
inspectedPlayerAchievements_.clear();
|
||||||
|
|
||||||
// Reset talent initialization so the first SMSG_TALENTS_INFO after login
|
// Reset talent initialization so the first SMSG_TALENTS_INFO after login
|
||||||
// correctly sets the active spec (static locals don't reset across logins)
|
// correctly sets the active spec (static locals don't reset across logins)
|
||||||
talentsInitialized_ = false;
|
talentsInitialized_ = false;
|
||||||
|
|
@ -11301,6 +11306,12 @@ void GameHandler::inspectTarget() {
|
||||||
auto packet = InspectPacket::build(targetGuid);
|
auto packet = InspectPacket::build(targetGuid);
|
||||||
socket->send(packet);
|
socket->send(packet);
|
||||||
|
|
||||||
|
// WotLK: also query the player's achievement data so the inspect UI can display it
|
||||||
|
if (isActiveExpansion("wotlk")) {
|
||||||
|
auto achPkt = QueryInspectAchievementsPacket::build(targetGuid);
|
||||||
|
socket->send(achPkt);
|
||||||
|
}
|
||||||
|
|
||||||
auto player = std::static_pointer_cast<Player>(target);
|
auto player = std::static_pointer_cast<Player>(target);
|
||||||
std::string name = player->getName().empty() ? "Target" : player->getName();
|
std::string name = player->getName().empty() ? "Target" : player->getName();
|
||||||
addSystemChatMessage("Inspecting " + name + "...");
|
addSystemChatMessage("Inspecting " + name + "...");
|
||||||
|
|
@ -22077,6 +22088,55 @@ void GameHandler::handleAllAchievementData(network::Packet& packet) {
|
||||||
" achievements, ", criteriaProgress_.size(), " criteria");
|
" achievements, ", criteriaProgress_.size(), " criteria");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// SMSG_RESPOND_INSPECT_ACHIEVEMENTS (WotLK 3.3.5a)
|
||||||
|
// Wire format: packed_guid (inspected player) + same achievement/criteria
|
||||||
|
// blocks as SMSG_ALL_ACHIEVEMENT_DATA:
|
||||||
|
// Achievement records: repeated { uint32 id, uint32 packedDate } until 0xFFFFFFFF sentinel
|
||||||
|
// Criteria records: repeated { uint32 id, uint64 counter, uint32 date, uint32 unk }
|
||||||
|
// until 0xFFFFFFFF sentinel
|
||||||
|
// We store only the earned achievement IDs (not criteria) per inspected player.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
void GameHandler::handleRespondInspectAchievements(network::Packet& packet) {
|
||||||
|
loadAchievementNameCache();
|
||||||
|
|
||||||
|
// Read the inspected player's packed guid
|
||||||
|
if (packet.getSize() - packet.getReadPos() < 1) return;
|
||||||
|
uint64_t inspectedGuid = UpdateObjectParser::readPackedGuid(packet);
|
||||||
|
if (inspectedGuid == 0) {
|
||||||
|
packet.setReadPos(packet.getSize());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_set<uint32_t> achievements;
|
||||||
|
|
||||||
|
// Achievement records: { uint32 id, uint32 packedDate } until sentinel 0xFFFFFFFF
|
||||||
|
while (packet.getSize() - packet.getReadPos() >= 4) {
|
||||||
|
uint32_t id = packet.readUInt32();
|
||||||
|
if (id == 0xFFFFFFFF) break;
|
||||||
|
if (packet.getSize() - packet.getReadPos() < 4) break;
|
||||||
|
/*uint32_t date =*/ packet.readUInt32();
|
||||||
|
achievements.insert(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Criteria records: { uint32 id, uint64 counter, uint32 date, uint32 unk }
|
||||||
|
// until sentinel 0xFFFFFFFF — consume but don't store for inspect use
|
||||||
|
while (packet.getSize() - packet.getReadPos() >= 4) {
|
||||||
|
uint32_t id = packet.readUInt32();
|
||||||
|
if (id == 0xFFFFFFFF) break;
|
||||||
|
// counter(8) + date(4) + unk(4) = 16 bytes
|
||||||
|
if (packet.getSize() - packet.getReadPos() < 16) break;
|
||||||
|
packet.readUInt64(); // counter
|
||||||
|
packet.readUInt32(); // date
|
||||||
|
packet.readUInt32(); // unk
|
||||||
|
}
|
||||||
|
|
||||||
|
inspectedPlayerAchievements_[inspectedGuid] = std::move(achievements);
|
||||||
|
|
||||||
|
LOG_INFO("SMSG_RESPOND_INSPECT_ACHIEVEMENTS: guid=0x", std::hex, inspectedGuid, std::dec,
|
||||||
|
" achievements=", inspectedPlayerAchievements_[inspectedGuid].size());
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Faction name cache (lazily loaded from Faction.dbc)
|
// Faction name cache (lazily loaded from Faction.dbc)
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -1722,6 +1722,15 @@ network::Packet InspectPacket::build(uint64_t targetGuid) {
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
network::Packet QueryInspectAchievementsPacket::build(uint64_t targetGuid) {
|
||||||
|
// CMSG_QUERY_INSPECT_ACHIEVEMENTS: uint64 targetGuid + uint8 unk (always 0)
|
||||||
|
network::Packet packet(wireOpcode(Opcode::CMSG_QUERY_INSPECT_ACHIEVEMENTS));
|
||||||
|
packet.writeUInt64(targetGuid);
|
||||||
|
packet.writeUInt8(0); // unk / achievementSlot — always 0 for WotLK
|
||||||
|
LOG_DEBUG("Built CMSG_QUERY_INSPECT_ACHIEVEMENTS: target=0x", std::hex, targetGuid, std::dec);
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Server Info Commands
|
// Server Info Commands
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue