mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-29 06:23:51 +00:00
refactor: extract spline math, consolidate packet parsing, decompose TransportManager
Extract CatmullRomSpline (include/math/spline.hpp, src/math/spline.cpp) as a
standalone, immutable, thread-safe spline module with O(log n) binary segment
search and fused position+tangent evaluation — replacing the duplicated O(n)
evalTimedCatmullRom/orientationFromTangent pair in TransportManager.
Consolidate 7 copies of spline packet parsing into shared functions in
game/spline_packet.{hpp,cpp}: parseMonsterMoveSplineBody (WotLK/TBC),
parseMonsterMoveSplineBodyVanilla, parseClassicMoveUpdateSpline,
parseWotlkMoveUpdateSpline, and decodePackedDelta. Named SplineFlag constants
replace magic hex literals throughout.
Extract TransportPathRepository (game/transport_path_repository.{hpp,cpp}) from
TransportManager — owns path data, DBC loading, and path inference. Paths stored
as PathEntry wrapping CatmullRomSpline + metadata (zOnly, fromDBC, worldCoords).
TransportManager reduced from ~1200 to ~500 lines, focused on transport lifecycle
and server sync.
Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
This commit is contained in:
parent
535cc20afe
commit
de0383aa6b
32 changed files with 2198 additions and 1293 deletions
|
|
@ -1,5 +1,6 @@
|
|||
#include "game/world_packets.hpp"
|
||||
#include "game/packet_parsers.hpp"
|
||||
#include "game/spline_packet.hpp"
|
||||
#include "game/opcodes.hpp"
|
||||
#include "game/character.hpp"
|
||||
#include "auth/crypto.hpp"
|
||||
|
|
@ -959,196 +960,10 @@ bool UpdateObjectParser::parseMovementBlock(network::Packet& packet, UpdateBlock
|
|||
|
||||
// Spline data
|
||||
if (moveFlags & 0x08000000) { // MOVEMENTFLAG_SPLINE_ENABLED
|
||||
auto bytesAvailable = [&](size_t n) -> bool { return packet.hasRemaining(n); };
|
||||
if (!bytesAvailable(4)) return false;
|
||||
uint32_t splineFlags = packet.readUInt32();
|
||||
LOG_DEBUG(" Spline: flags=0x", std::hex, splineFlags, std::dec);
|
||||
|
||||
if (splineFlags & 0x00010000) { // SPLINEFLAG_FINAL_POINT
|
||||
if (!bytesAvailable(12)) return false;
|
||||
/*float finalX =*/ packet.readFloat();
|
||||
/*float finalY =*/ packet.readFloat();
|
||||
/*float finalZ =*/ packet.readFloat();
|
||||
} else if (splineFlags & 0x00020000) { // SPLINEFLAG_FINAL_TARGET
|
||||
if (!bytesAvailable(8)) return false;
|
||||
/*uint64_t finalTarget =*/ packet.readUInt64();
|
||||
} else if (splineFlags & 0x00040000) { // SPLINEFLAG_FINAL_ANGLE
|
||||
if (!bytesAvailable(4)) return false;
|
||||
/*float finalAngle =*/ packet.readFloat();
|
||||
}
|
||||
|
||||
// WotLK spline data layout:
|
||||
// timePassed(4)+duration(4)+splineId(4)+durationMod(4)+durationModNext(4)
|
||||
// +[ANIMATION(5)]+verticalAccel(4)+effectStartTime(4)+pointCount(4)+points+mode(1)+endPoint(12)
|
||||
if (!bytesAvailable(12)) return false;
|
||||
/*uint32_t timePassed =*/ packet.readUInt32();
|
||||
/*uint32_t duration =*/ packet.readUInt32();
|
||||
/*uint32_t splineId =*/ packet.readUInt32();
|
||||
|
||||
// Helper: parse spline points + splineMode + endPoint.
|
||||
// WotLK uses compressed points by default (first=12 bytes, rest=4 bytes packed).
|
||||
auto tryParseSplinePoints = [&](bool compressed, const char* tag) -> bool {
|
||||
if (!bytesAvailable(4)) return false;
|
||||
size_t prePointCount = packet.getReadPos();
|
||||
uint32_t pc = packet.readUInt32();
|
||||
if (pc > 256) return false;
|
||||
size_t pointsBytes;
|
||||
if (compressed && pc > 0) {
|
||||
// First point = 3 floats (12 bytes), rest = packed uint32 (4 bytes each)
|
||||
pointsBytes = 12ull + (pc > 1 ? static_cast<size_t>(pc - 1) * 4ull : 0ull);
|
||||
} else {
|
||||
// All uncompressed: 3 floats each
|
||||
pointsBytes = static_cast<size_t>(pc) * 12ull;
|
||||
}
|
||||
size_t needed = pointsBytes + 13ull; // + splineMode(1) + endPoint(12)
|
||||
if (!bytesAvailable(needed)) {
|
||||
packet.setReadPos(prePointCount);
|
||||
return false;
|
||||
}
|
||||
packet.setReadPos(packet.getReadPos() + pointsBytes);
|
||||
uint8_t splineMode = packet.readUInt8();
|
||||
if (splineMode > 3) {
|
||||
packet.setReadPos(prePointCount);
|
||||
return false;
|
||||
}
|
||||
float epX = packet.readFloat();
|
||||
float epY = packet.readFloat();
|
||||
float epZ = packet.readFloat();
|
||||
// Validate endPoint: garbage bytes rarely produce finite world coords
|
||||
if (!std::isfinite(epX) || !std::isfinite(epY) || !std::isfinite(epZ) ||
|
||||
std::fabs(epX) > 65000.0f || std::fabs(epY) > 65000.0f ||
|
||||
std::fabs(epZ) > 65000.0f) {
|
||||
packet.setReadPos(prePointCount);
|
||||
return false;
|
||||
}
|
||||
LOG_DEBUG(" Spline pointCount=", pc, " compressed=", compressed,
|
||||
" endPt=(", epX, ",", epY, ",", epZ, ") (", tag, ")");
|
||||
return true;
|
||||
};
|
||||
|
||||
// Save position before WotLK spline header for fallback
|
||||
size_t beforeSplineHeader = packet.getReadPos();
|
||||
|
||||
// AzerothCore MoveSplineFlag constants:
|
||||
// CATMULLROM = 0x00080000 — uncompressed Catmull-Rom interpolation
|
||||
// CYCLIC = 0x00100000 — cyclic path
|
||||
// ENTER_CYCLE = 0x00200000 — entering cyclic path
|
||||
// ANIMATION = 0x00400000 — animation spline with animType+effectStart
|
||||
// PARABOLIC = 0x00000008 — vertical_acceleration+effectStartTime
|
||||
constexpr uint32_t SF_PARABOLIC = 0x00000008;
|
||||
constexpr uint32_t SF_CATMULLROM = 0x00080000;
|
||||
constexpr uint32_t SF_CYCLIC = 0x00100000;
|
||||
constexpr uint32_t SF_ENTER_CYCLE = 0x00200000;
|
||||
constexpr uint32_t SF_ANIMATION = 0x00400000;
|
||||
constexpr uint32_t SF_UNCOMPRESSED_MASK = SF_CATMULLROM | SF_CYCLIC | SF_ENTER_CYCLE;
|
||||
|
||||
// Try 1: WotLK format (durationMod+durationModNext+[ANIMATION]+vertAccel+effectStart+points)
|
||||
// Some servers (ChromieCraft) always write vertAccel+effectStart unconditionally.
|
||||
bool splineParsed = false;
|
||||
if (bytesAvailable(8)) {
|
||||
/*float durationMod =*/ packet.readFloat();
|
||||
/*float durationModNext =*/ packet.readFloat();
|
||||
bool wotlkOk = true;
|
||||
if (splineFlags & SF_ANIMATION) {
|
||||
if (!bytesAvailable(5)) { wotlkOk = false; }
|
||||
else { packet.readUInt8(); packet.readUInt32(); }
|
||||
}
|
||||
// Unconditional vertAccel+effectStart (ChromieCraft/some AzerothCore builds)
|
||||
if (wotlkOk) {
|
||||
if (!bytesAvailable(8)) { wotlkOk = false; }
|
||||
else { /*float vertAccel =*/ packet.readFloat(); /*uint32_t effectStart =*/ packet.readUInt32(); }
|
||||
}
|
||||
if (wotlkOk) {
|
||||
bool useCompressed = (splineFlags & SF_UNCOMPRESSED_MASK) == 0;
|
||||
splineParsed = tryParseSplinePoints(useCompressed, "wotlk-compressed");
|
||||
if (!splineParsed) {
|
||||
splineParsed = tryParseSplinePoints(false, "wotlk-uncompressed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try 2: ANIMATION present but vertAccel+effectStart gated by PARABOLIC
|
||||
// (standard AzerothCore: only writes vertAccel+effectStart when PARABOLIC is set)
|
||||
if (!splineParsed && (splineFlags & SF_ANIMATION)) {
|
||||
packet.setReadPos(beforeSplineHeader);
|
||||
if (bytesAvailable(8)) {
|
||||
packet.readFloat(); // durationMod
|
||||
packet.readFloat(); // durationModNext
|
||||
bool ok = true;
|
||||
if (!bytesAvailable(5)) { ok = false; }
|
||||
else { packet.readUInt8(); packet.readUInt32(); } // animType + effectStart
|
||||
if (ok && (splineFlags & SF_PARABOLIC)) {
|
||||
if (!bytesAvailable(8)) { ok = false; }
|
||||
else { packet.readFloat(); packet.readUInt32(); }
|
||||
}
|
||||
if (ok) {
|
||||
bool useCompressed = (splineFlags & SF_UNCOMPRESSED_MASK) == 0;
|
||||
splineParsed = tryParseSplinePoints(useCompressed, "wotlk-anim-conditional");
|
||||
if (!splineParsed) {
|
||||
splineParsed = tryParseSplinePoints(false, "wotlk-anim-conditional-uncomp");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try 3: No ANIMATION — vertAccel+effectStart only when PARABOLIC set
|
||||
if (!splineParsed) {
|
||||
packet.setReadPos(beforeSplineHeader);
|
||||
if (bytesAvailable(8)) {
|
||||
packet.readFloat(); // durationMod
|
||||
packet.readFloat(); // durationModNext
|
||||
bool ok = true;
|
||||
if (splineFlags & SF_PARABOLIC) {
|
||||
if (!bytesAvailable(8)) { ok = false; }
|
||||
else { packet.readFloat(); packet.readUInt32(); }
|
||||
}
|
||||
if (ok) {
|
||||
bool useCompressed = (splineFlags & SF_UNCOMPRESSED_MASK) == 0;
|
||||
splineParsed = tryParseSplinePoints(useCompressed, "wotlk-parabolic-gated");
|
||||
if (!splineParsed) {
|
||||
splineParsed = tryParseSplinePoints(false, "wotlk-parabolic-gated-uncomp");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try 4: No header at all — just durationMod+durationModNext then points
|
||||
if (!splineParsed) {
|
||||
packet.setReadPos(beforeSplineHeader);
|
||||
if (bytesAvailable(8)) {
|
||||
packet.readFloat(); // durationMod
|
||||
packet.readFloat(); // durationModNext
|
||||
splineParsed = tryParseSplinePoints(false, "wotlk-no-parabolic");
|
||||
if (!splineParsed) {
|
||||
bool useComp = (splineFlags & SF_UNCOMPRESSED_MASK) == 0;
|
||||
splineParsed = tryParseSplinePoints(useComp, "wotlk-no-parabolic-compressed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try 5: bare points (no WotLK header at all — some spline types skip everything)
|
||||
if (!splineParsed) {
|
||||
packet.setReadPos(beforeSplineHeader);
|
||||
splineParsed = tryParseSplinePoints(false, "bare-uncompressed");
|
||||
if (!splineParsed) {
|
||||
packet.setReadPos(beforeSplineHeader);
|
||||
bool useComp = (splineFlags & SF_UNCOMPRESSED_MASK) == 0;
|
||||
splineParsed = tryParseSplinePoints(useComp, "bare-compressed");
|
||||
}
|
||||
}
|
||||
|
||||
if (!splineParsed) {
|
||||
// Dump first 5 uint32s at beforeSplineHeader for format diagnosis
|
||||
packet.setReadPos(beforeSplineHeader);
|
||||
uint32_t d[5] = {};
|
||||
for (int di = 0; di < 5 && packet.hasRemaining(4); ++di)
|
||||
d[di] = packet.readUInt32();
|
||||
packet.setReadPos(beforeSplineHeader);
|
||||
LOG_WARNING("WotLK spline parse failed for guid=0x", std::hex, block.guid, std::dec,
|
||||
" splineFlags=0x", std::hex, splineFlags, std::dec,
|
||||
" remaining=", packet.getRemainingSize(),
|
||||
" header=[0x", std::hex, d[0], " 0x", d[1], " 0x", d[2],
|
||||
" 0x", d[3], " 0x", d[4], "]", std::dec);
|
||||
SplineBlockData splineData;
|
||||
glm::vec3 entityPos(block.x, block.y, block.z);
|
||||
if (!parseWotlkMoveUpdateSpline(packet, splineData, entityPos)) {
|
||||
LOG_WARNING("WotLK spline parse failed for guid=0x", std::hex, block.guid, std::dec);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue