feat: add map name lookups and improve instance/RAF/misc feedback

- Add getMapName() helper backed by Map.dbc (parallel to getAreaName)
- SMSG_RAID_INSTANCE_MESSAGE: show dungeon name instead of raw map ID
- SMSG_INSTANCE_RESET: show dungeon name instead of raw map ID
- SMSG_INSTANCE_RESET_FAILED: show dungeon name instead of raw map ID
- SMSG_EQUIPMENT_SET_SAVED: show "Equipment set saved." confirmation
- SMSG_PROPOSE_LEVEL_GRANT: show mentor name offering a level grant (RAF)
- SMSG_REFER_A_FRIEND_EXPIRED: show link-expired message
- SMSG_REFER_A_FRIEND_FAILURE: show reason-mapped error message
- SMSG_REPORT_PVP_AFK_RESULT: show success/failure feedback
- SMSG_QUEST_CONFIRM_ACCEPT: add playerNameCache fallback for sharer name
This commit is contained in:
Kelsi 2026-03-13 07:10:10 -07:00
parent 28ce441214
commit 2c72d8462d
2 changed files with 107 additions and 12 deletions

View file

@ -2971,6 +2971,12 @@ private:
bool areaNameCacheLoaded_ = false; bool areaNameCacheLoaded_ = false;
void loadAreaNameCache(); void loadAreaNameCache();
std::string getAreaName(uint32_t areaId) const; std::string getAreaName(uint32_t areaId) const;
// Map name cache (lazy-loaded from Map.dbc; maps mapId → localized display name)
std::unordered_map<uint32_t, std::string> mapNameCache_;
bool mapNameCacheLoaded_ = false;
void loadMapNameCache();
std::string getMapName(uint32_t mapId) const;
std::vector<TrainerTab> trainerTabs_; std::vector<TrainerTab> trainerTabs_;
void handleTrainerList(network::Packet& packet); void handleTrainerList(network::Packet& packet);
void loadSpellNameCache(); void loadSpellNameCache();

View file

@ -3836,6 +3836,7 @@ void GameHandler::handlePacket(network::Packet& packet) {
} }
case Opcode::SMSG_EQUIPMENT_SET_SAVED: case Opcode::SMSG_EQUIPMENT_SET_SAVED:
// uint32 setIndex + uint64 guid — equipment set was successfully saved // uint32 setIndex + uint64 guid — equipment set was successfully saved
addSystemChatMessage("Equipment set saved.");
LOG_DEBUG("Equipment set saved"); LOG_DEBUG("Equipment set saved");
break; break;
case Opcode::SMSG_PERIODICAURALOG: { case Opcode::SMSG_PERIODICAURALOG: {
@ -5178,17 +5179,18 @@ void GameHandler::handlePacket(network::Packet& packet) {
uint32_t msgType = packet.readUInt32(); uint32_t msgType = packet.readUInt32();
uint32_t mapId = packet.readUInt32(); uint32_t mapId = packet.readUInt32();
/*uint32_t diff =*/ packet.readUInt32(); /*uint32_t diff =*/ packet.readUInt32();
std::string mapLabel = getMapName(mapId);
if (mapLabel.empty()) mapLabel = "instance #" + std::to_string(mapId);
// type: 1=warning(time left), 2=saved, 3=welcome // type: 1=warning(time left), 2=saved, 3=welcome
if (msgType == 1 && packet.getSize() - packet.getReadPos() >= 4) { if (msgType == 1 && packet.getSize() - packet.getReadPos() >= 4) {
uint32_t timeLeft = packet.readUInt32(); uint32_t timeLeft = packet.readUInt32();
uint32_t minutes = timeLeft / 60; uint32_t minutes = timeLeft / 60;
std::string msg = "Instance " + std::to_string(mapId) + addSystemChatMessage(mapLabel + " will reset in " +
" will reset in " + std::to_string(minutes) + " minute(s)."; std::to_string(minutes) + " minute(s).");
addSystemChatMessage(msg);
} else if (msgType == 2) { } else if (msgType == 2) {
addSystemChatMessage("You have been saved to instance " + std::to_string(mapId) + "."); addSystemChatMessage("You have been saved to " + mapLabel + ".");
} else if (msgType == 3) { } else if (msgType == 3) {
addSystemChatMessage("Welcome to instance " + std::to_string(mapId) + "."); addSystemChatMessage("Welcome to " + mapLabel + ".");
} }
LOG_INFO("SMSG_RAID_INSTANCE_MESSAGE: type=", msgType, " map=", mapId); LOG_INFO("SMSG_RAID_INSTANCE_MESSAGE: type=", msgType, " map=", mapId);
} }
@ -5201,7 +5203,9 @@ void GameHandler::handlePacket(network::Packet& packet) {
auto it = std::remove_if(instanceLockouts_.begin(), instanceLockouts_.end(), auto it = std::remove_if(instanceLockouts_.begin(), instanceLockouts_.end(),
[mapId](const InstanceLockout& lo){ return lo.mapId == mapId; }); [mapId](const InstanceLockout& lo){ return lo.mapId == mapId; });
instanceLockouts_.erase(it, instanceLockouts_.end()); instanceLockouts_.erase(it, instanceLockouts_.end());
addSystemChatMessage("Instance " + std::to_string(mapId) + " has been reset."); std::string mapLabel = getMapName(mapId);
if (mapLabel.empty()) mapLabel = "instance #" + std::to_string(mapId);
addSystemChatMessage(mapLabel + " has been reset.");
LOG_INFO("SMSG_INSTANCE_RESET: mapId=", mapId); LOG_INFO("SMSG_INSTANCE_RESET: mapId=", mapId);
} }
break; break;
@ -5214,8 +5218,10 @@ void GameHandler::handlePacket(network::Packet& packet) {
"Not max level.", "Offline party members.", "Party members inside.", "Not max level.", "Offline party members.", "Party members inside.",
"Party members changing zone.", "Heroic difficulty only." "Party members changing zone.", "Heroic difficulty only."
}; };
const char* msg = (reason < 5) ? resetFailReasons[reason] : "Unknown reason."; const char* reasonMsg = (reason < 5) ? resetFailReasons[reason] : "Unknown reason.";
addSystemChatMessage("Cannot reset instance " + std::to_string(mapId) + ": " + msg); std::string mapLabel = getMapName(mapId);
if (mapLabel.empty()) mapLabel = "instance #" + std::to_string(mapId);
addSystemChatMessage("Cannot reset " + mapLabel + ": " + reasonMsg);
LOG_INFO("SMSG_INSTANCE_RESET_FAILED: mapId=", mapId, " reason=", reason); LOG_INFO("SMSG_INSTANCE_RESET_FAILED: mapId=", mapId, " reason=", reason);
} }
break; break;
@ -7014,16 +7020,65 @@ void GameHandler::handlePacket(network::Packet& packet) {
// ---- Misc consume (no state change needed) ---- // ---- Misc consume (no state change needed) ----
case Opcode::SMSG_SET_PLAYER_DECLINED_NAMES_RESULT: case Opcode::SMSG_SET_PLAYER_DECLINED_NAMES_RESULT:
case Opcode::SMSG_PROPOSE_LEVEL_GRANT:
case Opcode::SMSG_REFER_A_FRIEND_EXPIRED:
case Opcode::SMSG_REFER_A_FRIEND_FAILURE:
case Opcode::SMSG_REPORT_PVP_AFK_RESULT:
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_PLAYER_SKINNED: case Opcode::SMSG_PLAYER_SKINNED:
packet.setReadPos(packet.getSize()); packet.setReadPos(packet.getSize());
break; break;
case Opcode::SMSG_PROPOSE_LEVEL_GRANT: {
// Recruit-A-Friend: a mentor is offering to grant you a level
if (packet.getSize() - packet.getReadPos() >= 8) {
uint64_t mentorGuid = packet.readUInt64();
std::string mentorName;
auto ent = entityManager.getEntity(mentorGuid);
if (auto* unit = dynamic_cast<Unit*>(ent.get())) mentorName = unit->getName();
if (mentorName.empty()) {
auto nit = playerNameCache.find(mentorGuid);
if (nit != playerNameCache.end()) mentorName = nit->second;
}
addSystemChatMessage(mentorName.empty()
? "A player is offering to grant you a level."
: (mentorName + " is offering to grant you a level."));
}
packet.setReadPos(packet.getSize());
break;
}
case Opcode::SMSG_REFER_A_FRIEND_EXPIRED:
addSystemChatMessage("Your Recruit-A-Friend link has expired.");
packet.setReadPos(packet.getSize());
break;
case Opcode::SMSG_REFER_A_FRIEND_FAILURE: {
if (packet.getSize() - packet.getReadPos() >= 4) {
uint32_t reason = packet.readUInt32();
static const char* kRafErrors[] = {
"Not eligible", // 0
"Target not eligible", // 1
"Too many referrals", // 2
"Wrong faction", // 3
"Not a recruit", // 4
"Recruit requirements not met", // 5
"Level above requirement", // 6
"Friend needs account upgrade", // 7
};
const char* msg = (reason < 8) ? kRafErrors[reason]
: "Recruit-A-Friend failed.";
addSystemChatMessage(std::string("Recruit-A-Friend: ") + msg);
}
packet.setReadPos(packet.getSize());
break;
}
case Opcode::SMSG_REPORT_PVP_AFK_RESULT: {
if (packet.getSize() - packet.getReadPos() >= 1) {
uint8_t result = packet.readUInt8();
if (result == 0)
addSystemChatMessage("AFK report submitted.");
else
addSystemChatMessage("Cannot report that player as AFK right now.");
}
packet.setReadPos(packet.getSize());
break;
}
case Opcode::SMSG_RESPOND_INSPECT_ACHIEVEMENTS: case Opcode::SMSG_RESPOND_INSPECT_ACHIEVEMENTS:
handleRespondInspectAchievements(packet); handleRespondInspectAchievements(packet);
break; break;
@ -22057,6 +22112,11 @@ void GameHandler::handleQuestConfirmAccept(network::Packet& packet) {
if (auto* unit = dynamic_cast<Unit*>(entity.get())) { if (auto* unit = dynamic_cast<Unit*>(entity.get())) {
sharedQuestSharerName_ = unit->getName(); sharedQuestSharerName_ = unit->getName();
} }
if (sharedQuestSharerName_.empty()) {
auto nit = playerNameCache.find(sharedQuestSharerGuid_);
if (nit != playerNameCache.end())
sharedQuestSharerName_ = nit->second;
}
if (sharedQuestSharerName_.empty()) { if (sharedQuestSharerName_.empty()) {
char tmp[32]; char tmp[32];
std::snprintf(tmp, sizeof(tmp), "0x%llX", std::snprintf(tmp, sizeof(tmp), "0x%llX",
@ -22871,6 +22931,35 @@ std::string GameHandler::getAreaName(uint32_t areaId) const {
return (it != areaNameCache_.end()) ? it->second : std::string{}; return (it != areaNameCache_.end()) ? it->second : std::string{};
} }
void GameHandler::loadMapNameCache() {
if (mapNameCacheLoaded_) return;
mapNameCacheLoaded_ = true;
auto* am = core::Application::getInstance().getAssetManager();
if (!am || !am->isInitialized()) return;
auto dbc = am->loadDBC("Map.dbc");
if (!dbc || !dbc->isLoaded()) return;
for (uint32_t i = 0; i < dbc->getRecordCount(); ++i) {
uint32_t id = dbc->getUInt32(i, 0);
// Field 2 = MapName_enUS (first localized); field 1 = InternalName fallback
std::string name = dbc->getString(i, 2);
if (name.empty()) name = dbc->getString(i, 1);
if (!name.empty() && !mapNameCache_.count(id)) {
mapNameCache_[id] = std::move(name);
}
}
LOG_INFO("Map.dbc: loaded ", mapNameCache_.size(), " map names");
}
std::string GameHandler::getMapName(uint32_t mapId) const {
if (mapId == 0) return {};
const_cast<GameHandler*>(this)->loadMapNameCache();
auto it = mapNameCache_.find(mapId);
return (it != mapNameCache_.end()) ? it->second : std::string{};
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Aura duration update // Aura duration update
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------