mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Fix online mode creature spawning and packet parsing
- Fix encryption desync by tracking decrypted header bytes in world socket - Fix UPDATE_OBJECT movement block parsing to handle 3.3.5a update flags - Fix UNIT_FIELD_DISPLAYID index (67, not 71) - Add creature spawn/despawn callbacks with DBC-based model loading - Add SMSG_COMPRESSED_UPDATE_OBJECT opcode support
This commit is contained in:
parent
a4fcc38c12
commit
c28376e193
9 changed files with 445 additions and 30 deletions
|
|
@ -16,6 +16,7 @@
|
|||
#include <functional>
|
||||
#include <cstdlib>
|
||||
#include <sqlite3.h>
|
||||
#include <zlib.h>
|
||||
|
||||
namespace wowee {
|
||||
namespace game {
|
||||
|
|
@ -925,12 +926,21 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
break;
|
||||
|
||||
case Opcode::SMSG_UPDATE_OBJECT:
|
||||
LOG_INFO("Received SMSG_UPDATE_OBJECT, state=", static_cast<int>(state), " size=", packet.getSize());
|
||||
// Can be received after entering world
|
||||
if (state == WorldState::IN_WORLD) {
|
||||
handleUpdateObject(packet);
|
||||
}
|
||||
break;
|
||||
|
||||
case Opcode::SMSG_COMPRESSED_UPDATE_OBJECT:
|
||||
LOG_INFO("Received SMSG_COMPRESSED_UPDATE_OBJECT, state=", static_cast<int>(state), " size=", packet.getSize());
|
||||
// Compressed version of UPDATE_OBJECT
|
||||
if (state == WorldState::IN_WORLD) {
|
||||
handleCompressedUpdateObject(packet);
|
||||
}
|
||||
break;
|
||||
|
||||
case Opcode::SMSG_DESTROY_OBJECT:
|
||||
// Can be received after entering world
|
||||
if (state == WorldState::IN_WORLD) {
|
||||
|
|
@ -2288,6 +2298,10 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
for (uint64_t guid : data.outOfRangeGuids) {
|
||||
if (entityManager.hasEntity(guid)) {
|
||||
LOG_INFO("Entity went out of range: 0x", std::hex, guid, std::dec);
|
||||
// Trigger creature despawn callback before removing entity
|
||||
if (creatureDespawnCallback_) {
|
||||
creatureDespawnCallback_(guid);
|
||||
}
|
||||
entityManager.removeEntity(guid);
|
||||
}
|
||||
}
|
||||
|
|
@ -2363,9 +2377,17 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
case 32: unit->setMaxHealth(val); break;
|
||||
case 33: unit->setMaxPower(val); break;
|
||||
case 54: unit->setLevel(val); break;
|
||||
case 67: unit->setDisplayId(val); break; // UNIT_FIELD_DISPLAYID
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
// Trigger creature spawn callback for units with displayId
|
||||
if (block.objectType == ObjectType::UNIT && unit->getDisplayId() != 0) {
|
||||
if (creatureSpawnCallback_) {
|
||||
creatureSpawnCallback_(block.guid, unit->getDisplayId(),
|
||||
unit->getX(), unit->getY(), unit->getZ(), unit->getOrientation());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Extract XP fields for player entity
|
||||
if (block.guid == playerGuid && block.objectType == ObjectType::PLAYER) {
|
||||
|
|
@ -2444,6 +2466,44 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
LOG_INFO("Entity count: ", entityManager.getEntityCount());
|
||||
}
|
||||
|
||||
void GameHandler::handleCompressedUpdateObject(network::Packet& packet) {
|
||||
LOG_INFO("Handling SMSG_COMPRESSED_UPDATE_OBJECT, packet size: ", packet.getSize());
|
||||
|
||||
// First 4 bytes = decompressed size
|
||||
if (packet.getSize() < 4) {
|
||||
LOG_WARNING("SMSG_COMPRESSED_UPDATE_OBJECT too small");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t decompressedSize = packet.readUInt32();
|
||||
LOG_INFO(" Decompressed size: ", decompressedSize);
|
||||
|
||||
if (decompressedSize == 0 || decompressedSize > 1024 * 1024) {
|
||||
LOG_WARNING("Invalid decompressed size: ", decompressedSize);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remaining data is zlib compressed
|
||||
size_t compressedSize = packet.getSize() - packet.getReadPos();
|
||||
const uint8_t* compressedData = packet.getData().data() + packet.getReadPos();
|
||||
|
||||
// Decompress
|
||||
std::vector<uint8_t> decompressed(decompressedSize);
|
||||
uLongf destLen = decompressedSize;
|
||||
int ret = uncompress(decompressed.data(), &destLen, compressedData, compressedSize);
|
||||
|
||||
if (ret != Z_OK) {
|
||||
LOG_WARNING("Failed to decompress UPDATE_OBJECT: zlib error ", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(" Decompressed ", compressedSize, " -> ", destLen, " bytes");
|
||||
|
||||
// Create packet from decompressed data and parse it
|
||||
network::Packet decompressedPacket(static_cast<uint16_t>(Opcode::SMSG_UPDATE_OBJECT), decompressed);
|
||||
handleUpdateObject(decompressedPacket);
|
||||
}
|
||||
|
||||
void GameHandler::handleDestroyObject(network::Packet& packet) {
|
||||
LOG_INFO("Handling SMSG_DESTROY_OBJECT");
|
||||
|
||||
|
|
|
|||
|
|
@ -589,28 +589,195 @@ uint64_t UpdateObjectParser::readPackedGuid(network::Packet& packet) {
|
|||
}
|
||||
|
||||
bool UpdateObjectParser::parseMovementBlock(network::Packet& packet, UpdateBlock& block) {
|
||||
// Skip movement flags and other movement data for now
|
||||
// This is a simplified implementation
|
||||
// WoW 3.3.5a UPDATE_OBJECT movement block structure:
|
||||
// 1. UpdateFlags (1 byte, sometimes 2)
|
||||
// 2. Movement data depends on update flags
|
||||
|
||||
// Read movement flags (not used yet)
|
||||
/*uint32_t flags =*/ packet.readUInt32();
|
||||
/*uint16_t flags2 =*/ packet.readUInt16();
|
||||
// Update flags (3.3.5a uses 2 bytes for flags)
|
||||
uint16_t updateFlags = packet.readUInt16();
|
||||
|
||||
// Read timestamp (not used yet)
|
||||
/*uint32_t time =*/ packet.readUInt32();
|
||||
LOG_DEBUG(" UpdateFlags: 0x", std::hex, updateFlags, std::dec);
|
||||
|
||||
// Read position
|
||||
block.x = packet.readFloat();
|
||||
block.y = packet.readFloat();
|
||||
block.z = packet.readFloat();
|
||||
block.orientation = packet.readFloat();
|
||||
// UpdateFlags bit meanings:
|
||||
// 0x0001 = UPDATEFLAG_SELF
|
||||
// 0x0002 = UPDATEFLAG_TRANSPORT
|
||||
// 0x0004 = UPDATEFLAG_HAS_TARGET
|
||||
// 0x0008 = UPDATEFLAG_LOWGUID
|
||||
// 0x0010 = UPDATEFLAG_HIGHGUID
|
||||
// 0x0020 = UPDATEFLAG_LIVING
|
||||
// 0x0040 = UPDATEFLAG_STATIONARY_POSITION
|
||||
// 0x0080 = UPDATEFLAG_VEHICLE
|
||||
// 0x0100 = UPDATEFLAG_POSITION (transport)
|
||||
// 0x0200 = UPDATEFLAG_ROTATION
|
||||
|
||||
block.hasMovement = true;
|
||||
const uint16_t UPDATEFLAG_LIVING = 0x0020;
|
||||
const uint16_t UPDATEFLAG_STATIONARY_POSITION = 0x0040;
|
||||
const uint16_t UPDATEFLAG_HAS_TARGET = 0x0004;
|
||||
const uint16_t UPDATEFLAG_TRANSPORT = 0x0002;
|
||||
const uint16_t UPDATEFLAG_POSITION = 0x0100;
|
||||
const uint16_t UPDATEFLAG_VEHICLE = 0x0080;
|
||||
const uint16_t UPDATEFLAG_ROTATION = 0x0200;
|
||||
const uint16_t UPDATEFLAG_LOWGUID = 0x0008;
|
||||
const uint16_t UPDATEFLAG_HIGHGUID = 0x0010;
|
||||
|
||||
LOG_DEBUG(" Movement: (", block.x, ", ", block.y, ", ", block.z, "), orientation=", block.orientation);
|
||||
if (updateFlags & UPDATEFLAG_LIVING) {
|
||||
// Full movement block for living units
|
||||
uint32_t moveFlags = packet.readUInt32();
|
||||
uint16_t moveFlags2 = packet.readUInt16();
|
||||
/*uint32_t time =*/ packet.readUInt32();
|
||||
|
||||
// TODO: Parse additional movement fields based on flags
|
||||
// For now, we'll skip them to keep this simple
|
||||
// Position
|
||||
block.x = packet.readFloat();
|
||||
block.y = packet.readFloat();
|
||||
block.z = packet.readFloat();
|
||||
block.orientation = packet.readFloat();
|
||||
block.hasMovement = true;
|
||||
|
||||
LOG_DEBUG(" LIVING movement: (", block.x, ", ", block.y, ", ", block.z,
|
||||
"), o=", block.orientation, " moveFlags=0x", std::hex, moveFlags, std::dec);
|
||||
|
||||
// Transport data (if on transport)
|
||||
if (moveFlags & 0x00000200) { // MOVEMENTFLAG_ONTRANSPORT
|
||||
/*uint64_t transportGuid =*/ readPackedGuid(packet);
|
||||
/*float tX =*/ packet.readFloat();
|
||||
/*float tY =*/ packet.readFloat();
|
||||
/*float tZ =*/ packet.readFloat();
|
||||
/*float tO =*/ packet.readFloat();
|
||||
/*uint32_t tTime =*/ packet.readUInt32();
|
||||
/*int8_t tSeat =*/ packet.readUInt8();
|
||||
|
||||
if (moveFlags2 & 0x0200) { // MOVEMENTFLAG2_INTERPOLATED_MOVEMENT
|
||||
/*uint32_t tTime2 =*/ packet.readUInt32();
|
||||
}
|
||||
}
|
||||
|
||||
// Swimming/flying pitch
|
||||
if ((moveFlags & 0x02000000) || (moveFlags2 & 0x0010)) { // MOVEMENTFLAG_SWIMMING or MOVEMENTFLAG2_ALWAYS_ALLOW_PITCHING
|
||||
/*float pitch =*/ packet.readFloat();
|
||||
}
|
||||
|
||||
// Fall time
|
||||
/*uint32_t fallTime =*/ packet.readUInt32();
|
||||
|
||||
// Jumping
|
||||
if (moveFlags & 0x00001000) { // MOVEMENTFLAG_FALLING
|
||||
/*float jumpVelocity =*/ packet.readFloat();
|
||||
/*float jumpSinAngle =*/ packet.readFloat();
|
||||
/*float jumpCosAngle =*/ packet.readFloat();
|
||||
/*float jumpXYSpeed =*/ packet.readFloat();
|
||||
}
|
||||
|
||||
// Spline elevation
|
||||
if (moveFlags & 0x04000000) { // MOVEMENTFLAG_SPLINE_ELEVATION
|
||||
/*float splineElevation =*/ packet.readFloat();
|
||||
}
|
||||
|
||||
// Speeds (7 speed values)
|
||||
/*float walkSpeed =*/ packet.readFloat();
|
||||
/*float runSpeed =*/ packet.readFloat();
|
||||
/*float runBackSpeed =*/ packet.readFloat();
|
||||
/*float swimSpeed =*/ packet.readFloat();
|
||||
/*float swimBackSpeed =*/ packet.readFloat();
|
||||
/*float flightSpeed =*/ packet.readFloat();
|
||||
/*float flightBackSpeed =*/ packet.readFloat();
|
||||
/*float turnRate =*/ packet.readFloat();
|
||||
/*float pitchRate =*/ packet.readFloat();
|
||||
|
||||
// Spline data
|
||||
if (moveFlags & 0x08000000) { // MOVEMENTFLAG_SPLINE_ENABLED
|
||||
// Skip spline data for now - complex structure
|
||||
uint32_t splineFlags = packet.readUInt32();
|
||||
|
||||
if (splineFlags & 0x00010000) { // has final point
|
||||
/*float finalX =*/ packet.readFloat();
|
||||
/*float finalY =*/ packet.readFloat();
|
||||
/*float finalZ =*/ packet.readFloat();
|
||||
} else if (splineFlags & 0x00020000) { // has final target
|
||||
/*uint64_t finalTarget =*/ packet.readUInt64();
|
||||
} else if (splineFlags & 0x00040000) { // has final angle
|
||||
/*float finalAngle =*/ packet.readFloat();
|
||||
}
|
||||
|
||||
/*uint32_t timePassed =*/ packet.readUInt32();
|
||||
/*uint32_t duration =*/ packet.readUInt32();
|
||||
/*uint32_t splineId =*/ packet.readUInt32();
|
||||
|
||||
/*float durationMod =*/ packet.readFloat();
|
||||
/*float durationModNext =*/ packet.readFloat();
|
||||
|
||||
/*float verticalAccel =*/ packet.readFloat();
|
||||
|
||||
/*uint32_t effectStartTime =*/ packet.readUInt32();
|
||||
|
||||
uint32_t pointCount = packet.readUInt32();
|
||||
for (uint32_t i = 0; i < pointCount; i++) {
|
||||
/*float px =*/ packet.readFloat();
|
||||
/*float py =*/ packet.readFloat();
|
||||
/*float pz =*/ packet.readFloat();
|
||||
}
|
||||
|
||||
/*uint8_t splineMode =*/ packet.readUInt8();
|
||||
/*float endPointX =*/ packet.readFloat();
|
||||
/*float endPointY =*/ packet.readFloat();
|
||||
/*float endPointZ =*/ packet.readFloat();
|
||||
}
|
||||
}
|
||||
else if (updateFlags & UPDATEFLAG_POSITION) {
|
||||
// Transport position update
|
||||
/*uint64_t transportGuid =*/ readPackedGuid(packet);
|
||||
block.x = packet.readFloat();
|
||||
block.y = packet.readFloat();
|
||||
block.z = packet.readFloat();
|
||||
/*float transportOffsetX =*/ packet.readFloat();
|
||||
/*float transportOffsetY =*/ packet.readFloat();
|
||||
/*float transportOffsetZ =*/ packet.readFloat();
|
||||
block.orientation = packet.readFloat();
|
||||
/*float corpseOrientation =*/ packet.readFloat();
|
||||
block.hasMovement = true;
|
||||
|
||||
LOG_DEBUG(" POSITION: (", block.x, ", ", block.y, ", ", block.z, "), o=", block.orientation);
|
||||
}
|
||||
else if (updateFlags & UPDATEFLAG_STATIONARY_POSITION) {
|
||||
// Simple stationary position (4 floats)
|
||||
block.x = packet.readFloat();
|
||||
block.y = packet.readFloat();
|
||||
block.z = packet.readFloat();
|
||||
block.orientation = packet.readFloat();
|
||||
block.hasMovement = true;
|
||||
|
||||
LOG_DEBUG(" STATIONARY: (", block.x, ", ", block.y, ", ", block.z, "), o=", block.orientation);
|
||||
}
|
||||
|
||||
// Target GUID (for units with target)
|
||||
if (updateFlags & UPDATEFLAG_HAS_TARGET) {
|
||||
/*uint64_t targetGuid =*/ readPackedGuid(packet);
|
||||
}
|
||||
|
||||
// Transport time
|
||||
if (updateFlags & UPDATEFLAG_TRANSPORT) {
|
||||
/*uint32_t transportTime =*/ packet.readUInt32();
|
||||
}
|
||||
|
||||
// Vehicle
|
||||
if (updateFlags & UPDATEFLAG_VEHICLE) {
|
||||
/*uint32_t vehicleId =*/ packet.readUInt32();
|
||||
/*float vehicleOrientation =*/ packet.readFloat();
|
||||
}
|
||||
|
||||
// Rotation (GameObjects)
|
||||
if (updateFlags & UPDATEFLAG_ROTATION) {
|
||||
/*int64_t rotation =*/ packet.readUInt64();
|
||||
}
|
||||
|
||||
// Low GUID
|
||||
if (updateFlags & UPDATEFLAG_LOWGUID) {
|
||||
/*uint32_t lowGuid =*/ packet.readUInt32();
|
||||
}
|
||||
|
||||
// High GUID
|
||||
if (updateFlags & UPDATEFLAG_HIGHGUID) {
|
||||
/*uint32_t highGuid =*/ packet.readUInt32();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue