mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-03 08:03:50 +00:00
fix: align turtle world packet parsing
Some checks are pending
Build / Build (arm64) (push) Waiting to run
Build / Build (x86-64) (push) Waiting to run
Build / Build (macOS arm64) (push) Waiting to run
Build / Build (windows-arm64) (push) Waiting to run
Build / Build (windows-x86-64) (push) Waiting to run
Security / CodeQL (C/C++) (push) Waiting to run
Security / Semgrep (push) Waiting to run
Security / Sanitizer Build (ASan/UBSan) (push) Waiting to run
Some checks are pending
Build / Build (arm64) (push) Waiting to run
Build / Build (x86-64) (push) Waiting to run
Build / Build (macOS arm64) (push) Waiting to run
Build / Build (windows-arm64) (push) Waiting to run
Build / Build (windows-x86-64) (push) Waiting to run
Security / CodeQL (C/C++) (push) Waiting to run
Security / Semgrep (push) Waiting to run
Security / Sanitizer Build (ASan/UBSan) (push) Waiting to run
This commit is contained in:
parent
6ede9a2968
commit
43ebae217c
4 changed files with 252 additions and 77 deletions
|
|
@ -16506,12 +16506,48 @@ void GameHandler::handleOtherPlayerMovement(network::Packet& packet) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameHandler::handleCompressedMoves(network::Packet& packet) {
|
void GameHandler::handleCompressedMoves(network::Packet& packet) {
|
||||||
// Vanilla/Classic SMSG_COMPRESSED_MOVES: raw concatenated sub-packets, NOT zlib.
|
// Vanilla-family SMSG_COMPRESSED_MOVES carries concatenated movement sub-packets.
|
||||||
// Evidence: observed 1-byte "00" packets which are not valid zlib streams.
|
// Turtle can additionally wrap the batch in the same uint32 decompressedSize + zlib
|
||||||
// Each sub-packet: uint8 size (of opcode[2]+payload), uint16 opcode, uint8[] payload.
|
// envelope used by other compressed world packets.
|
||||||
// size=0 → invalid/empty, signals end of batch.
|
//
|
||||||
const auto& data = packet.getData();
|
// Within the decompressed stream, some realms encode the leading uint8 size as:
|
||||||
size_t dataLen = data.size();
|
// - opcode(2) + payload bytes
|
||||||
|
// - payload bytes only
|
||||||
|
// Try both framing modes and use the one that cleanly consumes the batch.
|
||||||
|
std::vector<uint8_t> decompressedStorage;
|
||||||
|
const std::vector<uint8_t>* dataPtr = &packet.getData();
|
||||||
|
|
||||||
|
const auto& rawData = packet.getData();
|
||||||
|
const bool hasCompressedWrapper =
|
||||||
|
rawData.size() >= 6 &&
|
||||||
|
rawData[4] == 0x78 &&
|
||||||
|
(rawData[5] == 0x01 || rawData[5] == 0x9C ||
|
||||||
|
rawData[5] == 0xDA || rawData[5] == 0x5E);
|
||||||
|
if (hasCompressedWrapper) {
|
||||||
|
uint32_t decompressedSize = static_cast<uint32_t>(rawData[0]) |
|
||||||
|
(static_cast<uint32_t>(rawData[1]) << 8) |
|
||||||
|
(static_cast<uint32_t>(rawData[2]) << 16) |
|
||||||
|
(static_cast<uint32_t>(rawData[3]) << 24);
|
||||||
|
if (decompressedSize == 0 || decompressedSize > 65536) {
|
||||||
|
LOG_WARNING("SMSG_COMPRESSED_MOVES: bad decompressedSize=", decompressedSize);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
decompressedStorage.resize(decompressedSize);
|
||||||
|
uLongf destLen = decompressedSize;
|
||||||
|
int ret = uncompress(decompressedStorage.data(), &destLen,
|
||||||
|
rawData.data() + 4, rawData.size() - 4);
|
||||||
|
if (ret != Z_OK) {
|
||||||
|
LOG_WARNING("SMSG_COMPRESSED_MOVES: zlib error ", ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
decompressedStorage.resize(destLen);
|
||||||
|
dataPtr = &decompressedStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& data = *dataPtr;
|
||||||
|
const size_t dataLen = data.size();
|
||||||
|
|
||||||
// Wire opcodes for sub-packet routing
|
// Wire opcodes for sub-packet routing
|
||||||
uint16_t monsterMoveWire = wireOpcode(Opcode::SMSG_MONSTER_MOVE);
|
uint16_t monsterMoveWire = wireOpcode(Opcode::SMSG_MONSTER_MOVE);
|
||||||
|
|
@ -16551,43 +16587,117 @@ void GameHandler::handleCompressedMoves(network::Packet& packet) {
|
||||||
wireOpcode(Opcode::MSG_MOVE_UNROOT),
|
wireOpcode(Opcode::MSG_MOVE_UNROOT),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CompressedMoveSubPacket {
|
||||||
|
uint16_t opcode = 0;
|
||||||
|
std::vector<uint8_t> payload;
|
||||||
|
};
|
||||||
|
struct DecodeResult {
|
||||||
|
bool ok = false;
|
||||||
|
bool overrun = false;
|
||||||
|
bool usedPayloadOnlySize = false;
|
||||||
|
size_t endPos = 0;
|
||||||
|
size_t recognizedCount = 0;
|
||||||
|
size_t subPacketCount = 0;
|
||||||
|
std::vector<CompressedMoveSubPacket> packets;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto isRecognizedSubOpcode = [&](uint16_t subOpcode) {
|
||||||
|
return subOpcode == monsterMoveWire ||
|
||||||
|
subOpcode == monsterMoveTransportWire ||
|
||||||
|
std::find(kMoveOpcodes.begin(), kMoveOpcodes.end(), subOpcode) != kMoveOpcodes.end();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto decodeSubPackets = [&](bool payloadOnlySize) -> DecodeResult {
|
||||||
|
DecodeResult result;
|
||||||
|
result.usedPayloadOnlySize = payloadOnlySize;
|
||||||
|
size_t pos = 0;
|
||||||
|
while (pos < dataLen) {
|
||||||
|
if (pos + 1 > dataLen) break;
|
||||||
|
uint8_t subSize = data[pos];
|
||||||
|
if (subSize == 0) {
|
||||||
|
result.ok = true;
|
||||||
|
result.endPos = pos + 1;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t payloadLen = payloadOnlySize
|
||||||
|
? static_cast<size_t>(subSize)
|
||||||
|
: (subSize >= 2 ? static_cast<size_t>(subSize) - 2 : 0);
|
||||||
|
if (!payloadOnlySize && subSize < 2) {
|
||||||
|
result.endPos = pos;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t packetLen = 1 + 2 + payloadLen;
|
||||||
|
if (pos + packetLen > dataLen) {
|
||||||
|
result.overrun = true;
|
||||||
|
result.endPos = pos;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t subOpcode = static_cast<uint16_t>(data[pos + 1]) |
|
||||||
|
(static_cast<uint16_t>(data[pos + 2]) << 8);
|
||||||
|
size_t payloadStart = pos + 3;
|
||||||
|
|
||||||
|
CompressedMoveSubPacket subPacket;
|
||||||
|
subPacket.opcode = subOpcode;
|
||||||
|
subPacket.payload.assign(data.begin() + payloadStart,
|
||||||
|
data.begin() + payloadStart + payloadLen);
|
||||||
|
result.packets.push_back(std::move(subPacket));
|
||||||
|
++result.subPacketCount;
|
||||||
|
if (isRecognizedSubOpcode(subOpcode)) {
|
||||||
|
++result.recognizedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += packetLen;
|
||||||
|
}
|
||||||
|
result.ok = (result.endPos == 0 || result.endPos == dataLen);
|
||||||
|
result.endPos = dataLen;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
DecodeResult decoded = decodeSubPackets(false);
|
||||||
|
if (!decoded.ok || decoded.overrun) {
|
||||||
|
DecodeResult payloadOnlyDecoded = decodeSubPackets(true);
|
||||||
|
const bool preferPayloadOnly =
|
||||||
|
payloadOnlyDecoded.ok &&
|
||||||
|
(!decoded.ok || decoded.overrun || payloadOnlyDecoded.recognizedCount > decoded.recognizedCount);
|
||||||
|
if (preferPayloadOnly) {
|
||||||
|
decoded = std::move(payloadOnlyDecoded);
|
||||||
|
static uint32_t payloadOnlyFallbackCount = 0;
|
||||||
|
++payloadOnlyFallbackCount;
|
||||||
|
if (payloadOnlyFallbackCount <= 10 || (payloadOnlyFallbackCount % 100) == 0) {
|
||||||
|
LOG_WARNING("SMSG_COMPRESSED_MOVES decoded via payload-only size fallback",
|
||||||
|
" (occurrence=", payloadOnlyFallbackCount, ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!decoded.ok || decoded.overrun) {
|
||||||
|
LOG_WARNING("SMSG_COMPRESSED_MOVES: sub-packet overruns buffer at pos=", decoded.endPos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Track unhandled sub-opcodes once per compressed packet (avoid log spam)
|
// Track unhandled sub-opcodes once per compressed packet (avoid log spam)
|
||||||
std::unordered_set<uint16_t> unhandledSeen;
|
std::unordered_set<uint16_t> unhandledSeen;
|
||||||
|
|
||||||
size_t pos = 0;
|
for (const auto& entry : decoded.packets) {
|
||||||
while (pos < dataLen) {
|
network::Packet subPacket(entry.opcode, entry.payload);
|
||||||
if (pos + 1 > dataLen) break;
|
|
||||||
uint8_t subSize = data[pos];
|
|
||||||
if (subSize < 2) break; // size=0 or 1 → empty/end-of-batch sentinel
|
|
||||||
if (pos + 1 + subSize > dataLen) {
|
|
||||||
LOG_WARNING("SMSG_COMPRESSED_MOVES: sub-packet overruns buffer at pos=", pos);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
uint16_t subOpcode = static_cast<uint16_t>(data[pos + 1]) |
|
|
||||||
(static_cast<uint16_t>(data[pos + 2]) << 8);
|
|
||||||
size_t payloadLen = subSize - 2;
|
|
||||||
size_t payloadStart = pos + 3;
|
|
||||||
|
|
||||||
std::vector<uint8_t> subPayload(data.begin() + payloadStart,
|
if (entry.opcode == monsterMoveWire) {
|
||||||
data.begin() + payloadStart + payloadLen);
|
|
||||||
network::Packet subPacket(subOpcode, subPayload);
|
|
||||||
|
|
||||||
if (subOpcode == monsterMoveWire) {
|
|
||||||
handleMonsterMove(subPacket);
|
handleMonsterMove(subPacket);
|
||||||
} else if (subOpcode == monsterMoveTransportWire) {
|
} else if (entry.opcode == monsterMoveTransportWire) {
|
||||||
handleMonsterMoveTransport(subPacket);
|
handleMonsterMoveTransport(subPacket);
|
||||||
} else if (state == WorldState::IN_WORLD &&
|
} else if (state == WorldState::IN_WORLD &&
|
||||||
std::find(kMoveOpcodes.begin(), kMoveOpcodes.end(), subOpcode) != kMoveOpcodes.end()) {
|
std::find(kMoveOpcodes.begin(), kMoveOpcodes.end(), entry.opcode) != kMoveOpcodes.end()) {
|
||||||
// Player/NPC movement update packed in SMSG_MULTIPLE_MOVES
|
// Player/NPC movement update packed in SMSG_MULTIPLE_MOVES
|
||||||
handleOtherPlayerMovement(subPacket);
|
handleOtherPlayerMovement(subPacket);
|
||||||
} else {
|
} else {
|
||||||
if (unhandledSeen.insert(subOpcode).second) {
|
if (unhandledSeen.insert(entry.opcode).second) {
|
||||||
LOG_INFO("SMSG_COMPRESSED_MOVES: unhandled sub-opcode 0x",
|
LOG_INFO("SMSG_COMPRESSED_MOVES: unhandled sub-opcode 0x",
|
||||||
std::hex, subOpcode, std::dec, " payloadLen=", payloadLen);
|
std::hex, entry.opcode, std::dec, " payloadLen=", entry.payload.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pos = payloadStart + payloadLen;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,18 @@ bool hasFullPackedGuid(const network::Packet& packet) {
|
||||||
return packet.getSize() - packet.getReadPos() >= guidBytes;
|
return packet.getSize() - packet.getReadPos() >= guidBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* updateTypeName(UpdateType type) {
|
||||||
|
switch (type) {
|
||||||
|
case UpdateType::VALUES: return "VALUES";
|
||||||
|
case UpdateType::MOVEMENT: return "MOVEMENT";
|
||||||
|
case UpdateType::CREATE_OBJECT: return "CREATE_OBJECT";
|
||||||
|
case UpdateType::CREATE_OBJECT2: return "CREATE_OBJECT2";
|
||||||
|
case UpdateType::OUT_OF_RANGE_OBJECTS: return "OUT_OF_RANGE_OBJECTS";
|
||||||
|
case UpdateType::NEAR_OBJECTS: return "NEAR_OBJECTS";
|
||||||
|
default: return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
@ -63,12 +75,12 @@ bool ClassicPacketParsers::parseMovementBlock(network::Packet& packet, UpdateBlo
|
||||||
|
|
||||||
LOG_DEBUG(" [Classic] UpdateFlags: 0x", std::hex, (int)updateFlags, std::dec);
|
LOG_DEBUG(" [Classic] UpdateFlags: 0x", std::hex, (int)updateFlags, std::dec);
|
||||||
|
|
||||||
const uint8_t UPDATEFLAG_LIVING = 0x20;
|
const uint8_t UPDATEFLAG_TRANSPORT = 0x02;
|
||||||
const uint8_t UPDATEFLAG_HAS_POSITION = 0x40;
|
const uint8_t UPDATEFLAG_MELEE_ATTACKING = 0x04;
|
||||||
const uint8_t UPDATEFLAG_HAS_TARGET = 0x04;
|
const uint8_t UPDATEFLAG_HIGHGUID = 0x08;
|
||||||
const uint8_t UPDATEFLAG_TRANSPORT = 0x02;
|
const uint8_t UPDATEFLAG_ALL = 0x10;
|
||||||
const uint8_t UPDATEFLAG_LOWGUID = 0x08;
|
const uint8_t UPDATEFLAG_LIVING = 0x20;
|
||||||
const uint8_t UPDATEFLAG_HIGHGUID = 0x10;
|
const uint8_t UPDATEFLAG_HAS_POSITION = 0x40;
|
||||||
|
|
||||||
if (updateFlags & UPDATEFLAG_LIVING) {
|
if (updateFlags & UPDATEFLAG_LIVING) {
|
||||||
// Movement flags (u32 only — NO extra flags byte in Classic)
|
// Movement flags (u32 only — NO extra flags byte in Classic)
|
||||||
|
|
@ -183,26 +195,26 @@ bool ClassicPacketParsers::parseMovementBlock(network::Packet& packet, UpdateBlo
|
||||||
LOG_DEBUG(" [Classic] STATIONARY: (", block.x, ", ", block.y, ", ", block.z, ")");
|
LOG_DEBUG(" [Classic] STATIONARY: (", block.x, ", ", block.y, ", ", block.z, ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Target GUID
|
|
||||||
if (updateFlags & UPDATEFLAG_HAS_TARGET) {
|
|
||||||
/*uint64_t targetGuid =*/ UpdateObjectParser::readPackedGuid(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transport time
|
|
||||||
if (updateFlags & UPDATEFLAG_TRANSPORT) {
|
|
||||||
/*uint32_t transportTime =*/ packet.readUInt32();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Low GUID
|
|
||||||
if (updateFlags & UPDATEFLAG_LOWGUID) {
|
|
||||||
/*uint32_t lowGuid =*/ packet.readUInt32();
|
|
||||||
}
|
|
||||||
|
|
||||||
// High GUID
|
// High GUID
|
||||||
if (updateFlags & UPDATEFLAG_HIGHGUID) {
|
if (updateFlags & UPDATEFLAG_HIGHGUID) {
|
||||||
/*uint32_t highGuid =*/ packet.readUInt32();
|
/*uint32_t highGuid =*/ packet.readUInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ALL/SELF extra uint32
|
||||||
|
if (updateFlags & UPDATEFLAG_ALL) {
|
||||||
|
/*uint32_t unkAll =*/ packet.readUInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current melee target as packed guid
|
||||||
|
if (updateFlags & UPDATEFLAG_MELEE_ATTACKING) {
|
||||||
|
/*uint64_t meleeTargetGuid =*/ UpdateObjectParser::readPackedGuid(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transport progress / world time
|
||||||
|
if (updateFlags & UPDATEFLAG_TRANSPORT) {
|
||||||
|
/*uint32_t transportTime =*/ packet.readUInt32();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1690,12 +1702,12 @@ bool TurtlePacketParsers::parseMovementBlock(network::Packet& packet, UpdateBloc
|
||||||
|
|
||||||
LOG_DEBUG(" [Turtle] UpdateFlags: 0x", std::hex, (int)updateFlags, std::dec);
|
LOG_DEBUG(" [Turtle] UpdateFlags: 0x", std::hex, (int)updateFlags, std::dec);
|
||||||
|
|
||||||
const uint8_t UPDATEFLAG_LIVING = 0x20;
|
const uint8_t UPDATEFLAG_TRANSPORT = 0x02;
|
||||||
const uint8_t UPDATEFLAG_HAS_POSITION = 0x40;
|
const uint8_t UPDATEFLAG_MELEE_ATTACKING = 0x04;
|
||||||
const uint8_t UPDATEFLAG_HAS_TARGET = 0x04;
|
const uint8_t UPDATEFLAG_HIGHGUID = 0x08;
|
||||||
const uint8_t UPDATEFLAG_TRANSPORT = 0x02;
|
const uint8_t UPDATEFLAG_ALL = 0x10;
|
||||||
const uint8_t UPDATEFLAG_LOWGUID = 0x08;
|
const uint8_t UPDATEFLAG_LIVING = 0x20;
|
||||||
const uint8_t UPDATEFLAG_HIGHGUID = 0x10;
|
const uint8_t UPDATEFLAG_HAS_POSITION = 0x40;
|
||||||
|
|
||||||
if (updateFlags & UPDATEFLAG_LIVING) {
|
if (updateFlags & UPDATEFLAG_LIVING) {
|
||||||
size_t livingStart = packet.getReadPos();
|
size_t livingStart = packet.getReadPos();
|
||||||
|
|
@ -1810,26 +1822,23 @@ bool TurtlePacketParsers::parseMovementBlock(network::Packet& packet, UpdateBloc
|
||||||
LOG_DEBUG(" [Turtle] STATIONARY: (", block.x, ", ", block.y, ", ", block.z, ")");
|
LOG_DEBUG(" [Turtle] STATIONARY: (", block.x, ", ", block.y, ", ", block.z, ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Target GUID
|
|
||||||
if (updateFlags & UPDATEFLAG_HAS_TARGET) {
|
|
||||||
/*uint64_t targetGuid =*/ UpdateObjectParser::readPackedGuid(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transport time
|
|
||||||
if (updateFlags & UPDATEFLAG_TRANSPORT) {
|
|
||||||
/*uint32_t transportTime =*/ packet.readUInt32();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Low GUID — Classic-style: 1×u32 (NOT TBC's 2×u32)
|
|
||||||
if (updateFlags & UPDATEFLAG_LOWGUID) {
|
|
||||||
/*uint32_t lowGuid =*/ packet.readUInt32();
|
|
||||||
}
|
|
||||||
|
|
||||||
// High GUID — 1×u32
|
// High GUID — 1×u32
|
||||||
if (updateFlags & UPDATEFLAG_HIGHGUID) {
|
if (updateFlags & UPDATEFLAG_HIGHGUID) {
|
||||||
/*uint32_t highGuid =*/ packet.readUInt32();
|
/*uint32_t highGuid =*/ packet.readUInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (updateFlags & UPDATEFLAG_ALL) {
|
||||||
|
/*uint32_t unkAll =*/ packet.readUInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateFlags & UPDATEFLAG_MELEE_ATTACKING) {
|
||||||
|
/*uint64_t meleeTargetGuid =*/ UpdateObjectParser::readPackedGuid(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateFlags & UPDATEFLAG_TRANSPORT) {
|
||||||
|
/*uint32_t transportTime =*/ packet.readUInt32();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1855,9 +1864,16 @@ bool TurtlePacketParsers::parseUpdateObject(network::Packet& packet, UpdateObjec
|
||||||
/*uint8_t hasTransport =*/ packet.readUInt8();
|
/*uint8_t hasTransport =*/ packet.readUInt8();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t remainingBlockCount = out.blockCount;
|
||||||
|
|
||||||
if (packet.getReadPos() + 1 <= packet.getSize()) {
|
if (packet.getReadPos() + 1 <= packet.getSize()) {
|
||||||
uint8_t firstByte = packet.readUInt8();
|
uint8_t firstByte = packet.readUInt8();
|
||||||
if (firstByte == static_cast<uint8_t>(UpdateType::OUT_OF_RANGE_OBJECTS)) {
|
if (firstByte == static_cast<uint8_t>(UpdateType::OUT_OF_RANGE_OBJECTS)) {
|
||||||
|
if (remainingBlockCount == 0) {
|
||||||
|
packet.setReadPos(start);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
--remainingBlockCount;
|
||||||
if (packet.getReadPos() + 4 > packet.getSize()) {
|
if (packet.getReadPos() + 4 > packet.getSize()) {
|
||||||
packet.setReadPos(start);
|
packet.setReadPos(start);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -1879,6 +1895,7 @@ bool TurtlePacketParsers::parseUpdateObject(network::Packet& packet, UpdateObjec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out.blockCount = remainingBlockCount;
|
||||||
out.blocks.reserve(out.blockCount);
|
out.blocks.reserve(out.blockCount);
|
||||||
for (uint32_t i = 0; i < out.blockCount; ++i) {
|
for (uint32_t i = 0; i < out.blockCount; ++i) {
|
||||||
if (packet.getReadPos() >= packet.getSize()) {
|
if (packet.getReadPos() >= packet.getSize()) {
|
||||||
|
|
@ -1905,7 +1922,7 @@ bool TurtlePacketParsers::parseUpdateObject(network::Packet& packet, UpdateObjec
|
||||||
|
|
||||||
switch (updateType) {
|
switch (updateType) {
|
||||||
case UpdateType::MOVEMENT:
|
case UpdateType::MOVEMENT:
|
||||||
block.guid = UpdateObjectParser::readPackedGuid(packet);
|
block.guid = packet.readUInt64();
|
||||||
if (!movementParser(packet, block)) return false;
|
if (!movementParser(packet, block)) return false;
|
||||||
LOG_DEBUG("[Turtle] Parsed MOVEMENT block via ", layoutName, " layout");
|
LOG_DEBUG("[Turtle] Parsed MOVEMENT block via ", layoutName, " layout");
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -1964,6 +1981,12 @@ bool TurtlePacketParsers::parseUpdateObject(network::Packet& packet, UpdateObjec
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
|
LOG_WARNING("[Turtle] SMSG_UPDATE_OBJECT block parse failed",
|
||||||
|
" blockIndex=", i,
|
||||||
|
" updateType=", updateTypeName(updateType),
|
||||||
|
" readPos=", packet.getReadPos(),
|
||||||
|
" blockStart=", blockStart,
|
||||||
|
" packetSize=", packet.getSize());
|
||||||
packet.setReadPos(start);
|
packet.setReadPos(start);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -425,9 +425,16 @@ bool TbcPacketParsers::parseUpdateObject(network::Packet& packet, UpdateObjectDa
|
||||||
/*uint8_t hasTransport =*/ packet.readUInt8();
|
/*uint8_t hasTransport =*/ packet.readUInt8();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t remainingBlockCount = out.blockCount;
|
||||||
|
|
||||||
if (packet.getReadPos() + 1 <= packet.getSize()) {
|
if (packet.getReadPos() + 1 <= packet.getSize()) {
|
||||||
uint8_t firstByte = packet.readUInt8();
|
uint8_t firstByte = packet.readUInt8();
|
||||||
if (firstByte == static_cast<uint8_t>(UpdateType::OUT_OF_RANGE_OBJECTS)) {
|
if (firstByte == static_cast<uint8_t>(UpdateType::OUT_OF_RANGE_OBJECTS)) {
|
||||||
|
if (remainingBlockCount == 0) {
|
||||||
|
packet.setReadPos(start);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
--remainingBlockCount;
|
||||||
if (packet.getReadPos() + 4 > packet.getSize()) {
|
if (packet.getReadPos() + 4 > packet.getSize()) {
|
||||||
packet.setReadPos(start);
|
packet.setReadPos(start);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -450,6 +457,7 @@ bool TbcPacketParsers::parseUpdateObject(network::Packet& packet, UpdateObjectDa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out.blockCount = remainingBlockCount;
|
||||||
out.blocks.reserve(out.blockCount);
|
out.blocks.reserve(out.blockCount);
|
||||||
for (uint32_t i = 0; i < out.blockCount; ++i) {
|
for (uint32_t i = 0; i < out.blockCount; ++i) {
|
||||||
if (packet.getReadPos() >= packet.getSize()) {
|
if (packet.getReadPos() >= packet.getSize()) {
|
||||||
|
|
@ -473,7 +481,7 @@ bool TbcPacketParsers::parseUpdateObject(network::Packet& packet, UpdateObjectDa
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case UpdateType::MOVEMENT: {
|
case UpdateType::MOVEMENT: {
|
||||||
block.guid = UpdateObjectParser::readPackedGuid(packet);
|
block.guid = packet.readUInt64();
|
||||||
ok = this->parseMovementBlock(packet, block);
|
ok = this->parseMovementBlock(packet, block);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,19 @@ namespace {
|
||||||
}
|
}
|
||||||
return packet.getSize() - packet.getReadPos() >= guidBytes;
|
return packet.getSize() - packet.getReadPos() >= guidBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* updateTypeName(wowee::game::UpdateType type) {
|
||||||
|
using wowee::game::UpdateType;
|
||||||
|
switch (type) {
|
||||||
|
case UpdateType::VALUES: return "VALUES";
|
||||||
|
case UpdateType::MOVEMENT: return "MOVEMENT";
|
||||||
|
case UpdateType::CREATE_OBJECT: return "CREATE_OBJECT";
|
||||||
|
case UpdateType::CREATE_OBJECT2: return "CREATE_OBJECT2";
|
||||||
|
case UpdateType::OUT_OF_RANGE_OBJECTS: return "OUT_OF_RANGE_OBJECTS";
|
||||||
|
case UpdateType::NEAR_OBJECTS: return "NEAR_OBJECTS";
|
||||||
|
default: return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace wowee {
|
namespace wowee {
|
||||||
|
|
@ -1225,7 +1238,13 @@ bool UpdateObjectParser::parseUpdateFields(network::Packet& packet, UpdateBlock&
|
||||||
for (int i = 0; i < blockCount; ++i) {
|
for (int i = 0; i < blockCount; ++i) {
|
||||||
// Validate 4 bytes available before each block read
|
// Validate 4 bytes available before each block read
|
||||||
if (packet.getReadPos() + 4 > packet.getSize()) {
|
if (packet.getReadPos() + 4 > packet.getSize()) {
|
||||||
LOG_WARNING("UpdateObjectParser: truncated update mask at block ", i);
|
LOG_WARNING("UpdateObjectParser: truncated update mask at block ", i,
|
||||||
|
" type=", updateTypeName(block.updateType),
|
||||||
|
" objectType=", static_cast<int>(block.objectType),
|
||||||
|
" guid=0x", std::hex, block.guid, std::dec,
|
||||||
|
" readPos=", packet.getReadPos(),
|
||||||
|
" size=", packet.getSize(),
|
||||||
|
" maskBlockCount=", static_cast<int>(blockCount));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
updateMask[i] = packet.readUInt32();
|
updateMask[i] = packet.readUInt32();
|
||||||
|
|
@ -1254,7 +1273,14 @@ bool UpdateObjectParser::parseUpdateFields(network::Packet& packet, UpdateBlock&
|
||||||
}
|
}
|
||||||
// Validate 4 bytes available before reading field value
|
// Validate 4 bytes available before reading field value
|
||||||
if (packet.getReadPos() + 4 > packet.getSize()) {
|
if (packet.getReadPos() + 4 > packet.getSize()) {
|
||||||
LOG_WARNING("UpdateObjectParser: truncated field value at field ", fieldIndex);
|
LOG_WARNING("UpdateObjectParser: truncated field value at field ", fieldIndex,
|
||||||
|
" type=", updateTypeName(block.updateType),
|
||||||
|
" objectType=", static_cast<int>(block.objectType),
|
||||||
|
" guid=0x", std::hex, block.guid, std::dec,
|
||||||
|
" readPos=", packet.getReadPos(),
|
||||||
|
" size=", packet.getSize(),
|
||||||
|
" maskBlockIndex=", blockIdx,
|
||||||
|
" maskBlock=0x", std::hex, updateMask[blockIdx], std::dec);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
uint32_t value = packet.readUInt32();
|
uint32_t value = packet.readUInt32();
|
||||||
|
|
@ -1298,7 +1324,7 @@ bool UpdateObjectParser::parseUpdateBlock(network::Packet& packet, UpdateBlock&
|
||||||
|
|
||||||
case UpdateType::MOVEMENT: {
|
case UpdateType::MOVEMENT: {
|
||||||
// Movement update
|
// Movement update
|
||||||
block.guid = readPackedGuid(packet);
|
block.guid = packet.readUInt64();
|
||||||
LOG_DEBUG(" MOVEMENT update for GUID: 0x", std::hex, block.guid, std::dec);
|
LOG_DEBUG(" MOVEMENT update for GUID: 0x", std::hex, block.guid, std::dec);
|
||||||
|
|
||||||
return parseMovementBlock(packet, block);
|
return parseMovementBlock(packet, block);
|
||||||
|
|
@ -1361,11 +1387,18 @@ bool UpdateObjectParser::parse(network::Packet& packet, UpdateObjectData& data)
|
||||||
LOG_DEBUG(" objectCount = ", data.blockCount);
|
LOG_DEBUG(" objectCount = ", data.blockCount);
|
||||||
LOG_DEBUG(" packetSize = ", packet.getSize());
|
LOG_DEBUG(" packetSize = ", packet.getSize());
|
||||||
|
|
||||||
|
uint32_t remainingBlockCount = data.blockCount;
|
||||||
|
|
||||||
// Check for out-of-range objects first
|
// Check for out-of-range objects first
|
||||||
if (packet.getReadPos() + 1 <= packet.getSize()) {
|
if (packet.getReadPos() + 1 <= packet.getSize()) {
|
||||||
uint8_t firstByte = packet.readUInt8();
|
uint8_t firstByte = packet.readUInt8();
|
||||||
|
|
||||||
if (firstByte == static_cast<uint8_t>(UpdateType::OUT_OF_RANGE_OBJECTS)) {
|
if (firstByte == static_cast<uint8_t>(UpdateType::OUT_OF_RANGE_OBJECTS)) {
|
||||||
|
if (remainingBlockCount == 0) {
|
||||||
|
LOG_ERROR("SMSG_UPDATE_OBJECT rejected: OUT_OF_RANGE_OBJECTS with zero blockCount");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
--remainingBlockCount;
|
||||||
// Read out-of-range GUID count
|
// Read out-of-range GUID count
|
||||||
uint32_t count = packet.readUInt32();
|
uint32_t count = packet.readUInt32();
|
||||||
if (count > kMaxReasonableOutOfRangeGuids) {
|
if (count > kMaxReasonableOutOfRangeGuids) {
|
||||||
|
|
@ -1389,6 +1422,7 @@ bool UpdateObjectParser::parse(network::Packet& packet, UpdateObjectData& data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse update blocks
|
// Parse update blocks
|
||||||
|
data.blockCount = remainingBlockCount;
|
||||||
data.blocks.reserve(data.blockCount);
|
data.blocks.reserve(data.blockCount);
|
||||||
|
|
||||||
for (uint32_t i = 0; i < data.blockCount; ++i) {
|
for (uint32_t i = 0; i < data.blockCount; ++i) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue