mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-17 17:43:52 +00:00
Fix quest accept/abandon flow and expansion-specific accept packet format
Normalize WoW quest text tokens during parsing so quest titles/details no longer leak raw markup like and |n into UI. Apply to WotLK and Classic parser paths, including quest list parsing in GameHandler. Harden quest state handling by mapping abandon requests to authoritative server quest-log slots (PLAYER_QUEST_LOG_START) instead of local vector order, with a guarded fallback when update fields are unavailable. Improve accept de-duplication by trusting server slot state over stale local cache; allow re-accept when local/server state diverges and trigger resync semantics. Add expansion-aware CMSG_QUESTGIVER_ACCEPT_QUEST builders: WotLK sends guid+questId+unk1(uint32), while TBC/Classic/Turtle send guid+questId only. Wire GameHandler through PacketParsers for compatibility across expansions and cores.
This commit is contained in:
parent
73273a6ab5
commit
ace24e8ccc
7 changed files with 125 additions and 34 deletions
|
|
@ -1130,6 +1130,7 @@ private:
|
||||||
void clearPendingQuestAccept(uint32_t questId);
|
void clearPendingQuestAccept(uint32_t questId);
|
||||||
void triggerQuestAcceptResync(uint32_t questId, uint64_t npcGuid, const char* reason);
|
void triggerQuestAcceptResync(uint32_t questId, uint64_t npcGuid, const char* reason);
|
||||||
bool hasQuestInLog(uint32_t questId) const;
|
bool hasQuestInLog(uint32_t questId) const;
|
||||||
|
int findQuestLogSlotIndexFromServer(uint32_t questId) const;
|
||||||
void addQuestToLocalLogIfMissing(uint32_t questId, const std::string& title, const std::string& objectives);
|
void addQuestToLocalLogIfMissing(uint32_t questId, const std::string& title, const std::string& objectives);
|
||||||
bool resyncQuestLogFromServerSlots(bool forceQueryMetadata);
|
bool resyncQuestLogFromServerSlots(bool forceQueryMetadata);
|
||||||
void handleListInventory(network::Packet& packet);
|
void handleListInventory(network::Packet& packet);
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,12 @@ public:
|
||||||
return QuestgiverQueryQuestPacket::build(npcGuid, questId); // includes unk1
|
return QuestgiverQueryQuestPacket::build(npcGuid, questId); // includes unk1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Build CMSG_QUESTGIVER_ACCEPT_QUEST.
|
||||||
|
* WotLK/AzerothCore expects trailing unk1 uint32; older expansions may not. */
|
||||||
|
virtual network::Packet buildAcceptQuestPacket(uint64_t npcGuid, uint32_t questId) {
|
||||||
|
return QuestgiverAcceptQuestPacket::build(npcGuid, questId);
|
||||||
|
}
|
||||||
|
|
||||||
/** Parse SMSG_QUESTGIVER_QUEST_DETAILS.
|
/** Parse SMSG_QUESTGIVER_QUEST_DETAILS.
|
||||||
* WotLK has an extra informUnit GUID before questId; Vanilla/Classic do not. */
|
* WotLK has an extra informUnit GUID before questId; Vanilla/Classic do not. */
|
||||||
virtual bool parseQuestDetails(network::Packet& packet, QuestDetailsData& data) {
|
virtual bool parseQuestDetails(network::Packet& packet, QuestDetailsData& data) {
|
||||||
|
|
@ -279,6 +285,7 @@ public:
|
||||||
bool parseAuraUpdate(network::Packet& packet, AuraUpdateData& data, bool isAll = false) override;
|
bool parseAuraUpdate(network::Packet& packet, AuraUpdateData& data, bool isAll = false) override;
|
||||||
bool parseNameQueryResponse(network::Packet& packet, NameQueryResponseData& data) override;
|
bool parseNameQueryResponse(network::Packet& packet, NameQueryResponseData& data) override;
|
||||||
bool parseItemQueryResponse(network::Packet& packet, ItemQueryResponseData& data) override;
|
bool parseItemQueryResponse(network::Packet& packet, ItemQueryResponseData& data) override;
|
||||||
|
network::Packet buildAcceptQuestPacket(uint64_t npcGuid, uint32_t questId) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -324,6 +331,7 @@ public:
|
||||||
bool parseItemQueryResponse(network::Packet& packet, ItemQueryResponseData& data) override;
|
bool parseItemQueryResponse(network::Packet& packet, ItemQueryResponseData& data) override;
|
||||||
uint8_t readQuestGiverStatus(network::Packet& packet) override;
|
uint8_t readQuestGiverStatus(network::Packet& packet) override;
|
||||||
network::Packet buildQueryQuestPacket(uint64_t npcGuid, uint32_t questId) override;
|
network::Packet buildQueryQuestPacket(uint64_t npcGuid, uint32_t questId) override;
|
||||||
|
network::Packet buildAcceptQuestPacket(uint64_t npcGuid, uint32_t questId) override;
|
||||||
bool parseQuestDetails(network::Packet& packet, QuestDetailsData& data) override;
|
bool parseQuestDetails(network::Packet& packet, QuestDetailsData& data) override;
|
||||||
uint8_t questLogStride() const override { return 3; }
|
uint8_t questLogStride() const override { return 3; }
|
||||||
bool parseMonsterMove(network::Packet& packet, MonsterMoveData& data) override {
|
bool parseMonsterMove(network::Packet& packet, MonsterMoveData& data) override {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,9 @@
|
||||||
namespace wowee {
|
namespace wowee {
|
||||||
namespace game {
|
namespace game {
|
||||||
|
|
||||||
|
// Normalize WoW in-text tokens (e.g. "$B", "|n") into plain text suitable for UI.
|
||||||
|
std::string normalizeWowTextTokens(std::string text);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SMSG_AUTH_CHALLENGE data (from server)
|
* SMSG_AUTH_CHALLENGE data (from server)
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -9957,6 +9957,20 @@ bool GameHandler::hasQuestInLog(uint32_t questId) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int GameHandler::findQuestLogSlotIndexFromServer(uint32_t questId) const {
|
||||||
|
if (questId == 0 || lastPlayerFields_.empty()) return -1;
|
||||||
|
const uint16_t ufQuestStart = fieldIndex(UF::PLAYER_QUEST_LOG_START);
|
||||||
|
const uint8_t qStride = packetParsers_ ? packetParsers_->questLogStride() : 5;
|
||||||
|
for (uint16_t slot = 0; slot < 25; ++slot) {
|
||||||
|
const uint16_t idField = ufQuestStart + slot * qStride;
|
||||||
|
auto it = lastPlayerFields_.find(idField);
|
||||||
|
if (it != lastPlayerFields_.end() && it->second == questId) {
|
||||||
|
return static_cast<int>(slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
void GameHandler::addQuestToLocalLogIfMissing(uint32_t questId, const std::string& title, const std::string& objectives) {
|
void GameHandler::addQuestToLocalLogIfMissing(uint32_t questId, const std::string& title, const std::string& objectives) {
|
||||||
if (questId == 0 || hasQuestInLog(questId)) return;
|
if (questId == 0 || hasQuestInLog(questId)) return;
|
||||||
QuestLogEntry entry;
|
QuestLogEntry entry;
|
||||||
|
|
@ -10039,18 +10053,23 @@ void GameHandler::acceptQuest() {
|
||||||
currentQuestDetails = QuestDetailsData{};
|
currentQuestDetails = QuestDetailsData{};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (hasQuestInLog(questId)) {
|
const bool inLocalLog = hasQuestInLog(questId);
|
||||||
LOG_INFO("Ignoring duplicate quest accept already in local log: questId=", questId);
|
const int serverSlot = findQuestLogSlotIndexFromServer(questId);
|
||||||
|
if (serverSlot >= 0) {
|
||||||
|
LOG_INFO("Ignoring duplicate quest accept already in server quest log: questId=", questId,
|
||||||
|
" slot=", serverSlot);
|
||||||
questDetailsOpen = false;
|
questDetailsOpen = false;
|
||||||
currentQuestDetails = QuestDetailsData{};
|
currentQuestDetails = QuestDetailsData{};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (inLocalLog) {
|
||||||
|
LOG_WARNING("Quest accept local/server mismatch, allowing re-accept: questId=", questId);
|
||||||
|
std::erase_if(questLog_, [&](const QuestLogEntry& q) { return q.questId == questId; });
|
||||||
|
}
|
||||||
|
|
||||||
// Keep quest accept payload minimal and expansion-safe: guid + questId.
|
network::Packet packet = packetParsers_
|
||||||
// Some server cores reject trailing bytes and throw ByteBufferException.
|
? packetParsers_->buildAcceptQuestPacket(npcGuid, questId)
|
||||||
network::Packet packet(wireOpcode(Opcode::CMSG_QUESTGIVER_ACCEPT_QUEST));
|
: QuestgiverAcceptQuestPacket::build(npcGuid, questId);
|
||||||
packet.writeUInt64(npcGuid);
|
|
||||||
packet.writeUInt32(questId);
|
|
||||||
socket->send(packet);
|
socket->send(packet);
|
||||||
pendingQuestAcceptTimeouts_[questId] = 5.0f;
|
pendingQuestAcceptTimeouts_[questId] = 5.0f;
|
||||||
pendingQuestAcceptNpcGuids_[questId] = npcGuid;
|
pendingQuestAcceptNpcGuids_[questId] = npcGuid;
|
||||||
|
|
@ -10073,20 +10092,34 @@ void GameHandler::declineQuest() {
|
||||||
|
|
||||||
void GameHandler::abandonQuest(uint32_t questId) {
|
void GameHandler::abandonQuest(uint32_t questId) {
|
||||||
clearPendingQuestAccept(questId);
|
clearPendingQuestAccept(questId);
|
||||||
// Find the quest's index in our local log
|
int localIndex = -1;
|
||||||
for (size_t i = 0; i < questLog_.size(); i++) {
|
for (size_t i = 0; i < questLog_.size(); ++i) {
|
||||||
if (questLog_[i].questId == questId) {
|
if (questLog_[i].questId == questId) {
|
||||||
// Tell server to remove it (slot index in server quest log)
|
localIndex = static_cast<int>(i);
|
||||||
// We send the local index; server maps it via PLAYER_QUEST_LOG fields
|
break;
|
||||||
if (state == WorldState::IN_WORLD && socket) {
|
|
||||||
network::Packet pkt(wireOpcode(Opcode::CMSG_QUESTLOG_REMOVE_QUEST));
|
|
||||||
pkt.writeUInt8(static_cast<uint8_t>(i));
|
|
||||||
socket->send(pkt);
|
|
||||||
}
|
|
||||||
questLog_.erase(questLog_.begin() + static_cast<ptrdiff_t>(i));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int slotIndex = findQuestLogSlotIndexFromServer(questId);
|
||||||
|
if (slotIndex < 0 && localIndex >= 0) {
|
||||||
|
// Best-effort fallback if update fields are stale/missing.
|
||||||
|
slotIndex = localIndex;
|
||||||
|
LOG_WARNING("Abandon quest using local slot fallback: questId=", questId, " slot=", slotIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slotIndex >= 0 && slotIndex < 25) {
|
||||||
|
if (state == WorldState::IN_WORLD && socket) {
|
||||||
|
network::Packet pkt(wireOpcode(Opcode::CMSG_QUESTLOG_REMOVE_QUEST));
|
||||||
|
pkt.writeUInt8(static_cast<uint8_t>(slotIndex));
|
||||||
|
socket->send(pkt);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG_WARNING("Abandon quest failed: no quest-log slot found for questId=", questId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localIndex >= 0) {
|
||||||
|
questLog_.erase(questLog_.begin() + static_cast<ptrdiff_t>(localIndex));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameHandler::handleQuestRequestItems(network::Packet& packet) {
|
void GameHandler::handleQuestRequestItems(network::Packet& packet) {
|
||||||
|
|
@ -10770,17 +10803,17 @@ void GameHandler::handleQuestgiverQuestList(network::Packet& packet) {
|
||||||
if (packet.getSize() - packet.getReadPos() >= 5) {
|
if (packet.getSize() - packet.getReadPos() >= 5) {
|
||||||
q.questFlags = packet.readUInt32();
|
q.questFlags = packet.readUInt32();
|
||||||
q.isRepeatable = packet.readUInt8();
|
q.isRepeatable = packet.readUInt8();
|
||||||
q.title = packet.readString();
|
q.title = normalizeWowTextTokens(packet.readString());
|
||||||
if (q.title.empty()) {
|
if (q.title.empty()) {
|
||||||
packet.setReadPos(titlePos);
|
packet.setReadPos(titlePos);
|
||||||
q.questFlags = 0;
|
q.questFlags = 0;
|
||||||
q.isRepeatable = 0;
|
q.isRepeatable = 0;
|
||||||
q.title = packet.readString();
|
q.title = normalizeWowTextTokens(packet.readString());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
q.questFlags = 0;
|
q.questFlags = 0;
|
||||||
q.isRepeatable = 0;
|
q.isRepeatable = 0;
|
||||||
q.title = packet.readString();
|
q.title = normalizeWowTextTokens(packet.readString());
|
||||||
}
|
}
|
||||||
if (q.questId != 0) {
|
if (q.questId != 0) {
|
||||||
data.quests.push_back(std::move(q));
|
data.quests.push_back(std::move(q));
|
||||||
|
|
|
||||||
|
|
@ -705,7 +705,7 @@ bool ClassicPacketParsers::parseGossipMessage(network::Packet& packet, GossipMes
|
||||||
// Classic: NO questFlags, NO isRepeatable
|
// Classic: NO questFlags, NO isRepeatable
|
||||||
quest.questFlags = 0;
|
quest.questFlags = 0;
|
||||||
quest.isRepeatable = 0;
|
quest.isRepeatable = 0;
|
||||||
quest.title = packet.readString();
|
quest.title = normalizeWowTextTokens(packet.readString());
|
||||||
data.quests.push_back(quest);
|
data.quests.push_back(quest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1220,6 +1220,14 @@ network::Packet ClassicPacketParsers::buildQueryQuestPacket(uint64_t npcGuid, ui
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
network::Packet ClassicPacketParsers::buildAcceptQuestPacket(uint64_t npcGuid, uint32_t questId) {
|
||||||
|
network::Packet packet(wireOpcode(Opcode::CMSG_QUESTGIVER_ACCEPT_QUEST));
|
||||||
|
packet.writeUInt64(npcGuid);
|
||||||
|
packet.writeUInt32(questId);
|
||||||
|
// Classic/Turtle: no trailing unk1 uint32
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Classic SMSG_QUESTGIVER_QUEST_DETAILS — Vanilla 1.12 format
|
// Classic SMSG_QUESTGIVER_QUEST_DETAILS — Vanilla 1.12 format
|
||||||
// WotLK inserts an informUnit GUID (8 bytes) between npcGuid and questId.
|
// WotLK inserts an informUnit GUID (8 bytes) between npcGuid and questId.
|
||||||
|
|
@ -1231,9 +1239,9 @@ bool ClassicPacketParsers::parseQuestDetails(network::Packet& packet, QuestDetai
|
||||||
data.npcGuid = packet.readUInt64();
|
data.npcGuid = packet.readUInt64();
|
||||||
// Vanilla: questId follows immediately — no informUnit GUID
|
// Vanilla: questId follows immediately — no informUnit GUID
|
||||||
data.questId = packet.readUInt32();
|
data.questId = packet.readUInt32();
|
||||||
data.title = packet.readString();
|
data.title = normalizeWowTextTokens(packet.readString());
|
||||||
data.details = packet.readString();
|
data.details = normalizeWowTextTokens(packet.readString());
|
||||||
data.objectives = packet.readString();
|
data.objectives = normalizeWowTextTokens(packet.readString());
|
||||||
|
|
||||||
if (packet.getReadPos() + 5 > packet.getSize()) {
|
if (packet.getReadPos() + 5 > packet.getSize()) {
|
||||||
LOG_INFO("Quest details classic (short): id=", data.questId, " title='", data.title, "'");
|
LOG_INFO("Quest details classic (short): id=", data.questId, " title='", data.title, "'");
|
||||||
|
|
|
||||||
|
|
@ -450,6 +450,14 @@ bool TbcPacketParsers::parseUpdateObject(network::Packet& packet, UpdateObjectDa
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
network::Packet TbcPacketParsers::buildAcceptQuestPacket(uint64_t npcGuid, uint32_t questId) {
|
||||||
|
network::Packet packet(wireOpcode(Opcode::CMSG_QUESTGIVER_ACCEPT_QUEST));
|
||||||
|
packet.writeUInt64(npcGuid);
|
||||||
|
packet.writeUInt32(questId);
|
||||||
|
// TBC servers generally expect guid + questId only.
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// TBC parseAuraUpdate - SMSG_AURA_UPDATE doesn't exist in TBC
|
// TBC parseAuraUpdate - SMSG_AURA_UPDATE doesn't exist in TBC
|
||||||
// TBC uses inline aura update fields + SMSG_INIT_EXTRA_AURA_INFO_OBSOLETE (0x3A3) /
|
// TBC uses inline aura update fields + SMSG_INIT_EXTRA_AURA_INFO_OBSOLETE (0x3A3) /
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,35 @@
|
||||||
namespace wowee {
|
namespace wowee {
|
||||||
namespace game {
|
namespace game {
|
||||||
|
|
||||||
|
std::string normalizeWowTextTokens(std::string text) {
|
||||||
|
if (text.empty()) return text;
|
||||||
|
|
||||||
|
size_t pos = 0;
|
||||||
|
while ((pos = text.find('$', pos)) != std::string::npos) {
|
||||||
|
if (pos + 1 >= text.size()) break;
|
||||||
|
const char code = text[pos + 1];
|
||||||
|
if (code == 'b' || code == 'B') {
|
||||||
|
text.replace(pos, 2, "\n");
|
||||||
|
++pos;
|
||||||
|
} else {
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = 0;
|
||||||
|
while ((pos = text.find("|n", pos)) != std::string::npos) {
|
||||||
|
text.replace(pos, 2, "\n");
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
pos = 0;
|
||||||
|
while ((pos = text.find("|N", pos)) != std::string::npos) {
|
||||||
|
text.replace(pos, 2, "\n");
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
network::Packet AuthSessionPacket::build(uint32_t build,
|
network::Packet AuthSessionPacket::build(uint32_t build,
|
||||||
const std::string& accountName,
|
const std::string& accountName,
|
||||||
uint32_t clientSeed,
|
uint32_t clientSeed,
|
||||||
|
|
@ -3068,6 +3097,7 @@ network::Packet QuestgiverAcceptQuestPacket::build(uint64_t npcGuid, uint32_t qu
|
||||||
network::Packet packet(wireOpcode(Opcode::CMSG_QUESTGIVER_ACCEPT_QUEST));
|
network::Packet packet(wireOpcode(Opcode::CMSG_QUESTGIVER_ACCEPT_QUEST));
|
||||||
packet.writeUInt64(npcGuid);
|
packet.writeUInt64(npcGuid);
|
||||||
packet.writeUInt32(questId);
|
packet.writeUInt32(questId);
|
||||||
|
packet.writeUInt32(0); // AzerothCore/WotLK expects trailing unk1
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3081,15 +3111,15 @@ bool QuestDetailsParser::parse(network::Packet& packet, QuestDetailsData& data)
|
||||||
size_t preInform = packet.getReadPos();
|
size_t preInform = packet.getReadPos();
|
||||||
/*informUnit*/ packet.readUInt64();
|
/*informUnit*/ packet.readUInt64();
|
||||||
data.questId = packet.readUInt32();
|
data.questId = packet.readUInt32();
|
||||||
data.title = packet.readString();
|
data.title = normalizeWowTextTokens(packet.readString());
|
||||||
if (data.title.empty() || data.questId > 100000) {
|
if (data.title.empty() || data.questId > 100000) {
|
||||||
// Likely vanilla format — rewind past informUnit
|
// Likely vanilla format — rewind past informUnit
|
||||||
packet.setReadPos(preInform);
|
packet.setReadPos(preInform);
|
||||||
data.questId = packet.readUInt32();
|
data.questId = packet.readUInt32();
|
||||||
data.title = packet.readString();
|
data.title = normalizeWowTextTokens(packet.readString());
|
||||||
}
|
}
|
||||||
data.details = packet.readString();
|
data.details = normalizeWowTextTokens(packet.readString());
|
||||||
data.objectives = packet.readString();
|
data.objectives = normalizeWowTextTokens(packet.readString());
|
||||||
|
|
||||||
if (packet.getReadPos() + 10 > packet.getSize()) {
|
if (packet.getReadPos() + 10 > packet.getSize()) {
|
||||||
LOG_INFO("Quest details (short): id=", data.questId, " title='", data.title, "'");
|
LOG_INFO("Quest details (short): id=", data.questId, " title='", data.title, "'");
|
||||||
|
|
@ -3162,7 +3192,7 @@ bool GossipMessageParser::parse(network::Packet& packet, GossipMessageData& data
|
||||||
quest.questLevel = static_cast<int32_t>(packet.readUInt32());
|
quest.questLevel = static_cast<int32_t>(packet.readUInt32());
|
||||||
quest.questFlags = packet.readUInt32();
|
quest.questFlags = packet.readUInt32();
|
||||||
quest.isRepeatable = packet.readUInt8();
|
quest.isRepeatable = packet.readUInt8();
|
||||||
quest.title = packet.readString();
|
quest.title = normalizeWowTextTokens(packet.readString());
|
||||||
data.quests.push_back(quest);
|
data.quests.push_back(quest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3194,8 +3224,8 @@ bool QuestRequestItemsParser::parse(network::Packet& packet, QuestRequestItemsDa
|
||||||
if (packet.getSize() - packet.getReadPos() < 20) return false;
|
if (packet.getSize() - packet.getReadPos() < 20) return false;
|
||||||
data.npcGuid = packet.readUInt64();
|
data.npcGuid = packet.readUInt64();
|
||||||
data.questId = packet.readUInt32();
|
data.questId = packet.readUInt32();
|
||||||
data.title = packet.readString();
|
data.title = normalizeWowTextTokens(packet.readString());
|
||||||
data.completionText = packet.readString();
|
data.completionText = normalizeWowTextTokens(packet.readString());
|
||||||
|
|
||||||
if (packet.getReadPos() + 9 > packet.getSize()) {
|
if (packet.getReadPos() + 9 > packet.getSize()) {
|
||||||
LOG_INFO("Quest request items (short): id=", data.questId, " title='", data.title, "'");
|
LOG_INFO("Quest request items (short): id=", data.questId, " title='", data.title, "'");
|
||||||
|
|
@ -3283,8 +3313,8 @@ bool QuestOfferRewardParser::parse(network::Packet& packet, QuestOfferRewardData
|
||||||
if (packet.getSize() - packet.getReadPos() < 20) return false;
|
if (packet.getSize() - packet.getReadPos() < 20) return false;
|
||||||
data.npcGuid = packet.readUInt64();
|
data.npcGuid = packet.readUInt64();
|
||||||
data.questId = packet.readUInt32();
|
data.questId = packet.readUInt32();
|
||||||
data.title = packet.readString();
|
data.title = normalizeWowTextTokens(packet.readString());
|
||||||
data.rewardText = packet.readString();
|
data.rewardText = normalizeWowTextTokens(packet.readString());
|
||||||
|
|
||||||
if (packet.getReadPos() + 10 > packet.getSize()) {
|
if (packet.getReadPos() + 10 > packet.getSize()) {
|
||||||
LOG_INFO("Quest offer reward (short): id=", data.questId, " title='", data.title, "'");
|
LOG_INFO("Quest offer reward (short): id=", data.questId, " title='", data.title, "'");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue