mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Fix quest system for Classic/Turtle: correct packet formats and log stride
- CMSG_QUESTGIVER_QUERY_QUEST: Classic override omits trailing unk1 byte (WotLK sends 13 bytes, Classic servers expect 12 — extra byte caused server to silently drop the packet, preventing quest details dialog) - SMSG_QUESTGIVER_QUEST_DETAILS: Classic override skips informUnit GUID (WotLK prepends 8-byte informUnit before questId; Vanilla does not) - Quest log UPDATE_OBJECT stride: use packetParsers_->questLogStride() (WotLK=5 fields/slot, Classic=3 fields/slot) - handleQuestDetails: route through packetParsers_->parseQuestDetails() - selectGossipQuest: route through packetParsers_->buildQueryQuestPacket()
This commit is contained in:
parent
2dffba63d8
commit
dd3149e3c1
3 changed files with 92 additions and 4 deletions
|
|
@ -148,6 +148,24 @@ public:
|
|||
return GossipMessageParser::parse(packet, data);
|
||||
}
|
||||
|
||||
// --- Quest details ---
|
||||
|
||||
/** Build CMSG_QUESTGIVER_QUERY_QUEST.
|
||||
* WotLK appends a trailing unk1 byte; Vanilla/Classic do not. */
|
||||
virtual network::Packet buildQueryQuestPacket(uint64_t npcGuid, uint32_t questId) {
|
||||
return QuestgiverQueryQuestPacket::build(npcGuid, questId); // includes unk1
|
||||
}
|
||||
|
||||
/** Parse SMSG_QUESTGIVER_QUEST_DETAILS.
|
||||
* WotLK has an extra informUnit GUID before questId; Vanilla/Classic do not. */
|
||||
virtual bool parseQuestDetails(network::Packet& packet, QuestDetailsData& data) {
|
||||
return QuestDetailsParser::parse(packet, data); // WotLK auto-detect
|
||||
}
|
||||
|
||||
/** Stride of PLAYER_QUEST_LOG fields in update-object blocks.
|
||||
* WotLK: 5 fields per slot, Classic/Vanilla: 3. */
|
||||
virtual uint8_t questLogStride() const { return 5; }
|
||||
|
||||
// --- Quest Giver Status ---
|
||||
|
||||
/** Read quest giver status from packet.
|
||||
|
|
@ -304,6 +322,9 @@ public:
|
|||
network::Packet buildItemQuery(uint32_t entry, uint64_t guid) override;
|
||||
bool parseItemQueryResponse(network::Packet& packet, ItemQueryResponseData& data) override;
|
||||
uint8_t readQuestGiverStatus(network::Packet& packet) override;
|
||||
network::Packet buildQueryQuestPacket(uint64_t npcGuid, uint32_t questId) override;
|
||||
bool parseQuestDetails(network::Packet& packet, QuestDetailsData& data) override;
|
||||
uint8_t questLogStride() const override { return 3; }
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -3858,7 +3858,8 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
const uint16_t ufPlayerLevel = fieldIndex(UF::UNIT_FIELD_LEVEL);
|
||||
const uint16_t ufCoinage = fieldIndex(UF::PLAYER_FIELD_COINAGE);
|
||||
const uint16_t ufQuestStart = fieldIndex(UF::PLAYER_QUEST_LOG_START);
|
||||
const uint16_t ufQuestEnd = ufQuestStart + 25 * 5; // 25 quest slots, stride 5
|
||||
const uint8_t qStride = packetParsers_ ? packetParsers_->questLogStride() : 5;
|
||||
const uint16_t ufQuestEnd = ufQuestStart + 25 * qStride; // 25 quest slots
|
||||
for (const auto& [key, val] : block.fields) {
|
||||
if (key == ufPlayerXp) { playerXp_ = val; }
|
||||
else if (key == ufPlayerNextXp) { playerNextLevelXp_ = val; }
|
||||
|
|
@ -3872,8 +3873,8 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
playerMoneyCopper_ = val;
|
||||
LOG_INFO("Money set from update fields: ", val, " copper");
|
||||
}
|
||||
// Parse quest log fields (stride 5, 25 slots)
|
||||
else if (key >= ufQuestStart && key < ufQuestEnd && (key - ufQuestStart) % 5 == 0) {
|
||||
// Parse quest log fields (stride varies by expansion: 5=WotLK, 3=Classic)
|
||||
else if (key >= ufQuestStart && key < ufQuestEnd && (key - ufQuestStart) % qStride == 0) {
|
||||
uint32_t questId = val;
|
||||
if (questId != 0) {
|
||||
// Check if quest is already in log
|
||||
|
|
@ -8306,7 +8307,9 @@ void GameHandler::selectGossipQuest(uint32_t questId) {
|
|||
|
||||
void GameHandler::handleQuestDetails(network::Packet& packet) {
|
||||
QuestDetailsData data;
|
||||
if (!QuestDetailsParser::parse(packet, data)) {
|
||||
bool ok = packetParsers_ ? packetParsers_->parseQuestDetails(packet, data)
|
||||
: QuestDetailsParser::parse(packet, data);
|
||||
if (!ok) {
|
||||
LOG_WARNING("Failed to parse SMSG_QUESTGIVER_QUEST_DETAILS");
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1154,5 +1154,69 @@ uint8_t ClassicPacketParsers::readQuestGiverStatus(network::Packet& packet) {
|
|||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Classic CMSG_QUESTGIVER_QUERY_QUEST — Vanilla 1.12 format
|
||||
// WotLK appends a trailing unk1 byte; Vanilla servers don't expect it and
|
||||
// some reject or misparse the 13-byte packet, preventing quest details from
|
||||
// being sent back. Classic format: guid(8) + questId(4) = 12 bytes.
|
||||
// ============================================================================
|
||||
network::Packet ClassicPacketParsers::buildQueryQuestPacket(uint64_t npcGuid, uint32_t questId) {
|
||||
network::Packet packet(wireOpcode(Opcode::CMSG_QUESTGIVER_QUERY_QUEST));
|
||||
packet.writeUInt64(npcGuid);
|
||||
packet.writeUInt32(questId);
|
||||
// No trailing unk byte (WotLK-only field)
|
||||
return packet;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Classic SMSG_QUESTGIVER_QUEST_DETAILS — Vanilla 1.12 format
|
||||
// WotLK inserts an informUnit GUID (8 bytes) between npcGuid and questId.
|
||||
// Vanilla has: npcGuid(8) + questId(4) + title + details + objectives + ...
|
||||
// ============================================================================
|
||||
bool ClassicPacketParsers::parseQuestDetails(network::Packet& packet, QuestDetailsData& data) {
|
||||
if (packet.getSize() < 16) return false;
|
||||
|
||||
data.npcGuid = packet.readUInt64();
|
||||
// Vanilla: questId follows immediately — no informUnit GUID
|
||||
data.questId = packet.readUInt32();
|
||||
data.title = packet.readString();
|
||||
data.details = packet.readString();
|
||||
data.objectives = packet.readString();
|
||||
|
||||
if (packet.getReadPos() + 5 > packet.getSize()) {
|
||||
LOG_INFO("Quest details classic (short): id=", data.questId, " title='", data.title, "'");
|
||||
return !data.title.empty() || data.questId != 0;
|
||||
}
|
||||
|
||||
/*activateAccept*/ packet.readUInt8();
|
||||
data.suggestedPlayers = packet.readUInt32();
|
||||
|
||||
// Choice reward items: variable count + 3 uint32s each
|
||||
if (packet.getReadPos() + 4 <= packet.getSize()) {
|
||||
uint32_t choiceCount = packet.readUInt32();
|
||||
for (uint32_t i = 0; i < choiceCount && packet.getReadPos() + 12 <= packet.getSize(); ++i) {
|
||||
packet.readUInt32(); // itemId
|
||||
packet.readUInt32(); // count
|
||||
packet.readUInt32(); // displayInfo
|
||||
}
|
||||
}
|
||||
|
||||
// Fixed reward items: variable count + 3 uint32s each
|
||||
if (packet.getReadPos() + 4 <= packet.getSize()) {
|
||||
uint32_t rewardCount = packet.readUInt32();
|
||||
for (uint32_t i = 0; i < rewardCount && packet.getReadPos() + 12 <= packet.getSize(); ++i) {
|
||||
packet.readUInt32(); // itemId
|
||||
packet.readUInt32(); // count
|
||||
packet.readUInt32(); // displayInfo
|
||||
}
|
||||
}
|
||||
|
||||
if (packet.getReadPos() + 4 <= packet.getSize())
|
||||
data.rewardMoney = packet.readUInt32();
|
||||
|
||||
LOG_INFO("Quest details classic: id=", data.questId, " title='", data.title, "'");
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
} // namespace wowee
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue