fix(render): code quality cleanup

Magic number elimination:
- Create protocol_constants.hpp, warden_constants.hpp,
  render_constants.hpp, ui_constants.hpp
- Replace ~55 magic numbers across game_handler, warden_handler,
  m2_renderer_render

Reduce nesting depth:
- Extract 5 parseEffect* methods from handleSpellLogExecute
  (max indent 52 → 16 cols)
- Extract resolveSpellSchool/playSpellCastSound/playSpellImpactSound
  from 3× duplicate audio blocks in handleSpellGo
- Flatten SMSG_INVENTORY_CHANGE_FAILURE with early-return guards
- Extract drawScreenEdgeVignette() for 3 duplicate vignette blocks

DRY extract patterns:
- Replace 12 compound expansion checks with isPreWotlk() across
  movement_handler (9), chat_handler (1), social_handler (1)

const to constexpr:
- Promote 23+ static const arrays/scalars to static constexpr across
  12 source files

Error handling:
- Convert PIN auth from exceptions to std::optional<PinProof>
- Add [[nodiscard]] to 15+ initialize/parse methods
- Wrap ~20 unchecked initialize() calls with LOG_WARNING/LOG_ERROR

Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
This commit is contained in:
Pavel Okhlopkov 2026-04-06 22:43:13 +03:00
parent 2e8856bacd
commit 97106bd6ae
41 changed files with 849 additions and 424 deletions

View file

@ -378,7 +378,7 @@ void ChatHandler::sendTextEmote(uint32_t textEmoteId, uint64_t targetGuid) {
}
void ChatHandler::handleTextEmote(network::Packet& packet) {
const bool legacyFormat = isClassicLikeExpansion() || isActiveExpansion("tbc");
const bool legacyFormat = isPreWotlk();
TextEmoteData data;
if (!TextEmoteParser::parse(packet, data, legacyFormat)) {
LOG_WARNING("Failed to parse SMSG_TEXT_EMOTE");

View file

@ -32,6 +32,7 @@
#include "pipeline/asset_manager.hpp"
#include "pipeline/dbc_loader.hpp"
#include "core/logger.hpp"
#include "game/protocol_constants.hpp"
#include "rendering/animation/animation_ids.hpp"
#include <glm/gtx/quaternion.hpp>
#include <algorithm>
@ -79,9 +80,9 @@ const char* worldStateName(WorldState state) {
} // end anonymous namespace
std::string formatCopperAmount(uint32_t amount) {
uint32_t gold = amount / 10000;
uint32_t silver = (amount / 100) % 100;
uint32_t copper = amount % 100;
uint32_t gold = amount / game::COPPER_PER_GOLD;
uint32_t silver = (amount / game::COPPER_PER_SILVER) % 100;
uint32_t copper = amount % game::COPPER_PER_SILVER;
std::ostringstream oss;
bool wrote = false;
@ -150,9 +151,9 @@ GameHandler::GameHandler(GameServices& services)
// Default action bar layout
actionBar[0].type = ActionBarSlot::SPELL;
actionBar[0].id = 6603; // Attack in slot 1
actionBar[0].id = game::SPELL_ID_ATTACK; // Attack in slot 1
actionBar[11].type = ActionBarSlot::SPELL;
actionBar[11].id = 8690; // Hearthstone in slot 12
actionBar[11].id = game::SPELL_ID_HEARTHSTONE; // Hearthstone in slot 12
// Build the opcode dispatch table (replaces switch(*logicalOp) in handlePacket)
registerOpcodeHandlers();
@ -365,11 +366,11 @@ void GameHandler::updateNetworking(float deltaTime) {
lastRxTime_.time_since_epoch().count() > 0) {
auto silenceMs = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - lastRxTime_).count();
if (silenceMs > 10000 && !rxSilenceLogged_) {
if (silenceMs > game::RX_SILENCE_WARNING_MS && !rxSilenceLogged_) {
rxSilenceLogged_ = true;
LOG_WARNING("RX SILENCE: No packets from server for ", silenceMs, "ms — possible soft disconnect");
}
if (silenceMs > 15000 && !rxSilence15sLogged_) {
if (silenceMs > game::RX_SILENCE_CRITICAL_MS && !rxSilence15sLogged_) {
rxSilence15sLogged_ = true;
LOG_WARNING("RX SILENCE: 15s — server appears to have stopped sending");
}
@ -393,7 +394,7 @@ void GameHandler::updateNetworking(float deltaTime) {
LOG_DEBUG("Warden gate status: elapsed=", wardenGateElapsed_,
"s connected=", socket->isConnected() ? "yes" : "no",
" packetsAfterGate=", wardenPacketsAfterGate_);
wardenGateNextStatusLog_ += 30.0f;
wardenGateNextStatusLog_ += game::WARDEN_GATE_LOG_INTERVAL_SEC;
}
}
}
@ -420,7 +421,7 @@ if (onTaxiFlight_) {
auto playerEntity = entityController_->getEntityManager().getEntity(playerGuid);
auto unit = std::dynamic_pointer_cast<Unit>(playerEntity);
if (unit &&
(unit->getUnitFlags() & 0x00000100) == 0 &&
(unit->getUnitFlags() & game::UNIT_FLAG_TAXI_FLIGHT) == 0 &&
!taxiClientActive_ &&
!taxiActivatePending_ &&
taxiStartGrace_ <= 0.0f) {
@ -452,7 +453,7 @@ if (!onTaxiFlight_ && taxiMountActive_) {
auto playerEntity = entityController_->getEntityManager().getEntity(playerGuid);
auto playerUnit = std::dynamic_pointer_cast<Unit>(playerEntity);
if (playerUnit) {
serverStillTaxi = (playerUnit->getUnitFlags() & 0x00000100) != 0;
serverStillTaxi = (playerUnit->getUnitFlags() & game::UNIT_FLAG_TAXI_FLIGHT) != 0;
}
if (taxiStartGrace_ > 0.0f || serverStillTaxi || taxiClientActive_ || taxiActivatePending_) {
@ -541,7 +542,7 @@ void GameHandler::updateAutoAttack(float deltaTime) {
void GameHandler::updateEntityInterpolation(float deltaTime) {
// Update entity movement interpolation (keeps targeting in sync with visuals)
// Only update entities within reasonable distance for performance
const float updateRadiusSq = 150.0f * 150.0f; // 150 unit radius
const float updateRadiusSq = game::ENTITY_UPDATE_RADIUS * game::ENTITY_UPDATE_RADIUS; // 150 unit radius
auto playerEntity = entityController_->getEntityManager().getEntity(playerGuid);
glm::vec3 playerPos = playerEntity ? glm::vec3(playerEntity->getX(), playerEntity->getY(), playerEntity->getZ()) : glm::vec3(0.0f);
@ -788,7 +789,7 @@ void GameHandler::update(float deltaTime) {
if (movementHandler_) movementHandler_->timeSinceLastMoveHeartbeatRef() += deltaTime;
const float currentPingInterval =
(isPreWotlk()) ? 10.0f : pingInterval;
(isPreWotlk()) ? game::CLASSIC_PING_INTERVAL_SEC : pingInterval;
if (timeSinceLastPing >= currentPingInterval) {
if (socket) {
sendPing();
@ -819,9 +820,9 @@ void GameHandler::update(float deltaTime) {
!taxiClientActive_ &&
(movementInfo.flags & locomotionFlags) == 0;
float heartbeatInterval = (onTaxiFlight_ || taxiActivatePending_ || taxiClientActive_)
? 0.25f
: (classicLikeStationaryCombatSync ? 0.75f
: (classicLikeCombatSync ? 0.20f
? game::HEARTBEAT_INTERVAL_TAXI
: (classicLikeStationaryCombatSync ? game::HEARTBEAT_INTERVAL_STATIONARY_COMBAT
: (classicLikeCombatSync ? game::HEARTBEAT_INTERVAL_MOVING_COMBAT
: moveHeartbeatInterval_));
if (movementHandler_ && movementHandler_->timeSinceLastMoveHeartbeatRef() >= heartbeatInterval) {
sendMovement(Opcode::MSG_MOVE_HEARTBEAT);
@ -830,7 +831,7 @@ void GameHandler::update(float deltaTime) {
// Check area triggers (instance portals, tavern rests, etc.)
areaTriggerCheckTimer_ += deltaTime;
if (areaTriggerCheckTimer_ >= 0.25f) {
if (areaTriggerCheckTimer_ >= game::AREA_TRIGGER_CHECK_INTERVAL) {
areaTriggerCheckTimer_ = 0.0f;
checkAreaTriggers();
}
@ -894,7 +895,7 @@ void GameHandler::update(float deltaTime) {
if (!npc) return;
float dx = movementInfo.x - npc->getX();
float dy = movementInfo.y - npc->getY();
if (std::sqrt(dx * dx + dy * dy) > 15.0f) {
if (std::sqrt(dx * dx + dy * dy) > game::NPC_INTERACT_MAX_DISTANCE) {
closeFn();
LOG_INFO(label, " closed: walked too far from NPC");
}
@ -918,7 +919,7 @@ void GameHandler::update(float deltaTime) {
// ============================================================
// WotLK 3.3.5a XP-to-next-level table (from player_xp_for_level)
static const uint32_t XP_TABLE[] = {
static constexpr uint32_t XP_TABLE[] = {
0, // level 0 (unused)
400, 900, 1400, 2100, 2800, 3600, 4500, 5400, 6500, 7600, // 1-10
8700, 9800, 11000, 12300, 13600, 15000, 16400, 17800, 19300, 20800, // 11-20

View file

@ -308,33 +308,37 @@ void InventoryHandler::registerOpcodes(DispatchTable& table) {
};
table[Opcode::SMSG_INVENTORY_CHANGE_FAILURE] = [this](network::Packet& packet) {
if ((packet.getRemainingSize()) >= 1) {
uint8_t error = packet.readUInt8();
if (error != 0) {
LOG_WARNING("SMSG_INVENTORY_CHANGE_FAILURE: error=", (int)error);
uint32_t requiredLevel = 0;
if (packet.hasRemaining(17)) {
packet.readUInt64();
packet.readUInt64();
packet.readUInt8();
if (error == 1 && packet.hasRemaining(4))
requiredLevel = packet.readUInt32();
}
const char* errMsg = nullptr;
char levelBuf[64];
switch (error) {
case 1:
if (requiredLevel > 0) {
std::snprintf(levelBuf, sizeof(levelBuf),
"You must reach level %u to use that item.", requiredLevel);
owner_.addUIError(levelBuf);
owner_.addSystemChatMessage(levelBuf);
} else {
owner_.addUIError("You must reach a higher level to use that item.");
owner_.addSystemChatMessage("You must reach a higher level to use that item.");
}
return;
case 2: errMsg = "You don't have the required skill."; break;
if (packet.getRemainingSize() < 1) return;
uint8_t error = packet.readUInt8();
if (error == 0) return;
LOG_WARNING("SMSG_INVENTORY_CHANGE_FAILURE: error=", (int)error);
uint32_t requiredLevel = 0;
if (packet.hasRemaining(17)) {
packet.readUInt64();
packet.readUInt64();
packet.readUInt8();
if (error == 1 && packet.hasRemaining(4))
requiredLevel = packet.readUInt32();
}
// Level requirement has its own formatting
if (error == 1) {
char levelBuf[64];
if (requiredLevel > 0) {
std::snprintf(levelBuf, sizeof(levelBuf),
"You must reach level %u to use that item.", requiredLevel);
} else {
std::snprintf(levelBuf, sizeof(levelBuf),
"You must reach a higher level to use that item.");
}
owner_.addUIError(levelBuf);
owner_.addSystemChatMessage(levelBuf);
return;
}
const char* errMsg = nullptr;
switch (error) {
case 3: errMsg = "That item doesn't go in that slot."; break;
case 4: errMsg = "That bag is full."; break;
case 5: errMsg = "Can't put bags in bags."; break;
@ -392,14 +396,12 @@ void InventoryHandler::registerOpcodes(DispatchTable& table) {
case 88: errMsg = "Requires the right talent."; break;
default: break;
}
std::string msg = errMsg ? errMsg : "Inventory error (" + std::to_string(error) + ").";
owner_.addUIError(msg);
owner_.addSystemChatMessage(msg);
if (auto* ac = owner_.services().audioCoordinator) {
if (auto* sfx = ac->getUiSoundManager())
sfx->playError();
}
}
std::string msg = errMsg ? errMsg : "Inventory error (" + std::to_string(error) + ").";
owner_.addUIError(msg);
owner_.addSystemChatMessage(msg);
if (auto* ac = owner_.services().audioCoordinator) {
if (auto* sfx = ac->getUiSoundManager())
sfx->playError();
}
};

View file

@ -354,7 +354,7 @@ void MovementHandler::sendMovement(Opcode opcode) {
movementInfo.time = movementTime;
if (opcode == Opcode::MSG_MOVE_SET_FACING &&
(isClassicLikeExpansion() || isActiveExpansion("tbc"))) {
isPreWotlk()) {
const float facingDelta = core::coords::normalizeAngleRad(
movementInfo.orientation - lastFacingSentOrientation_);
const uint32_t sinceLastFacingMs =
@ -833,7 +833,7 @@ network::Packet MovementHandler::buildForceAck(Opcode ackOpcode, uint32_t counte
void MovementHandler::handleForceSpeedChange(network::Packet& packet, const char* name,
Opcode ackOpcode, float* speedStorage) {
const bool fscTbcLike = isClassicLikeExpansion() || isActiveExpansion("tbc");
const bool fscTbcLike = isPreWotlk();
uint64_t guid = fscTbcLike
? packet.readUInt64() : packet.readPackedGuid();
uint32_t counter = packet.readUInt32();
@ -881,7 +881,7 @@ void MovementHandler::handleForceRunSpeedChange(network::Packet& packet) {
}
void MovementHandler::handleForceMoveRootState(network::Packet& packet, bool rooted) {
const bool rootTbc = isClassicLikeExpansion() || isActiveExpansion("tbc");
const bool rootTbc = isPreWotlk();
if (packet.getRemainingSize() < (rootTbc ? 8u : 2u)) return;
uint64_t guid = rootTbc
? packet.readUInt64() : packet.readPackedGuid();
@ -907,7 +907,7 @@ void MovementHandler::handleForceMoveRootState(network::Packet& packet, bool roo
void MovementHandler::handleForceMoveFlagChange(network::Packet& packet, const char* name,
Opcode ackOpcode, uint32_t flag, bool set) {
const bool fmfTbcLike = isClassicLikeExpansion() || isActiveExpansion("tbc");
const bool fmfTbcLike = isPreWotlk();
if (packet.getRemainingSize() < (fmfTbcLike ? 8u : 2u)) return;
uint64_t guid = fmfTbcLike
? packet.readUInt64() : packet.readPackedGuid();
@ -932,7 +932,7 @@ void MovementHandler::handleForceMoveFlagChange(network::Packet& packet, const c
}
void MovementHandler::handleMoveSetCollisionHeight(network::Packet& packet) {
const bool legacyGuid = isClassicLikeExpansion() || isActiveExpansion("tbc");
const bool legacyGuid = isPreWotlk();
if (packet.getRemainingSize() < (legacyGuid ? 8u : 2u)) return;
uint64_t guid = legacyGuid ? packet.readUInt64() : packet.readPackedGuid();
if (!packet.hasRemaining(8)) return;
@ -954,7 +954,7 @@ void MovementHandler::handleMoveSetCollisionHeight(network::Packet& packet) {
}
void MovementHandler::handleMoveKnockBack(network::Packet& packet) {
const bool mkbTbc = isClassicLikeExpansion() || isActiveExpansion("tbc");
const bool mkbTbc = isPreWotlk();
if (packet.getRemainingSize() < (mkbTbc ? 8u : 2u)) return;
uint64_t guid = mkbTbc
? packet.readUInt64() : packet.readPackedGuid();
@ -985,7 +985,7 @@ void MovementHandler::handleMoveKnockBack(network::Packet& packet) {
// ============================================================
void MovementHandler::handleMoveSetSpeed(network::Packet& packet) {
const bool useFull = isClassicLikeExpansion() || isActiveExpansion("tbc");
const bool useFull = isPreWotlk();
uint64_t moverGuid = useFull
? packet.readUInt64() : packet.readPackedGuid();
@ -1010,7 +1010,7 @@ void MovementHandler::handleMoveSetSpeed(network::Packet& packet) {
}
void MovementHandler::handleOtherPlayerMovement(network::Packet& packet) {
const bool otherMoveTbc = isClassicLikeExpansion() || isActiveExpansion("tbc");
const bool otherMoveTbc = isPreWotlk();
uint64_t moverGuid = otherMoveTbc
? packet.readUInt64() : packet.readPackedGuid();
if (moverGuid == owner_.getPlayerGuid() || moverGuid == 0) {
@ -1646,7 +1646,7 @@ void MovementHandler::handleMonsterMoveTransport(network::Packet& packet) {
// ============================================================
void MovementHandler::handleTeleportAck(network::Packet& packet) {
const bool taTbc = isClassicLikeExpansion() || isActiveExpansion("tbc");
const bool taTbc = isPreWotlk();
if (packet.getRemainingSize() < (taTbc ? 8u : 4u)) {
LOG_WARNING("MSG_MOVE_TELEPORT_ACK too short");
return;
@ -1657,7 +1657,7 @@ void MovementHandler::handleTeleportAck(network::Packet& packet) {
if (!packet.hasRemaining(4)) return;
uint32_t counter = packet.readUInt32();
const bool taNoFlags2 = isClassicLikeExpansion() || isActiveExpansion("tbc");
const bool taNoFlags2 = isPreWotlk();
const size_t minMoveSz = taNoFlags2 ? (4 + 4 + 4 * 4) : (4 + 2 + 4 + 4 * 4);
if (packet.getRemainingSize() < minMoveSz) {
LOG_WARNING("MSG_MOVE_TELEPORT_ACK: not enough data for movement info");
@ -2048,8 +2048,8 @@ void MovementHandler::applyTaxiMountForCurrentNode() {
else if (it->second.mountDisplayIdHorde != 0) mountId = it->second.mountDisplayIdHorde;
}
if (mountId == 0) {
static const uint32_t kAllianceTaxiDisplays[] = {1210u, 1211u, 1212u, 1213u};
static const uint32_t kHordeTaxiDisplays[] = {1310u, 1311u, 1312u};
static constexpr uint32_t kAllianceTaxiDisplays[] = {1210u, 1211u, 1212u, 1213u};
static constexpr uint32_t kHordeTaxiDisplays[] = {1310u, 1311u, 1312u};
mountId = isAlliance ? kAllianceTaxiDisplays[0] : kHordeTaxiDisplays[0];
}
if (mountId == 0) {

View file

@ -612,7 +612,7 @@ void SocialHandler::handleInspectResults(network::Packet& packet) {
}
// talentType == 1: inspect result
const bool talentTbc = isClassicLikeExpansion() || isActiveExpansion("tbc");
const bool talentTbc = isPreWotlk();
if (packet.getRemainingSize() < (talentTbc ? 8u : 2u)) return;
uint64_t guid = talentTbc
? packet.readUInt64() : packet.readPackedGuid();

View file

@ -62,6 +62,33 @@ static audio::SpellSoundManager::MagicSchool schoolMaskToMagicSchool(uint32_t ma
return audio::SpellSoundManager::MagicSchool::ARCANE;
}
// ---- Extracted helpers to reduce nesting in handleSpellGo ----
audio::SpellSoundManager::MagicSchool SpellHandler::resolveSpellSchool(uint32_t spellId) {
owner_.loadSpellNameCache();
auto it = owner_.spellNameCacheRef().find(spellId);
if (it != owner_.spellNameCacheRef().end() && it->second.schoolMask)
return schoolMaskToMagicSchool(it->second.schoolMask);
return audio::SpellSoundManager::MagicSchool::ARCANE;
}
void SpellHandler::playSpellCastSound(uint32_t spellId) {
auto* ac = owner_.services().audioCoordinator;
if (!ac) return;
auto* ssm = ac->getSpellSoundManager();
if (!ssm) return;
ssm->playCast(resolveSpellSchool(spellId));
}
void SpellHandler::playSpellImpactSound(uint32_t spellId) {
auto* ac = owner_.services().audioCoordinator;
if (!ac) return;
auto* ssm = ac->getSpellSoundManager();
if (!ssm) return;
ssm->playImpact(resolveSpellSchool(spellId),
audio::SpellSoundManager::SpellPower::MEDIUM);
}
static std::string displaySpellName(GameHandler& handler, uint32_t spellId) {
if (spellId == 0) return {};
@ -929,18 +956,8 @@ void SpellHandler::handleSpellGo(network::Packet& packet) {
if (data.casterUnit == owner_.getPlayerGuid()) {
// Play cast-complete sound
if (!owner_.isProfessionSpell(data.spellId)) {
if (auto* ac = owner_.services().audioCoordinator) {
if (auto* ssm = ac->getSpellSoundManager()) {
owner_.loadSpellNameCache();
auto it = owner_.spellNameCacheRef().find(data.spellId);
auto school = (it != owner_.spellNameCacheRef().end() && it->second.schoolMask)
? schoolMaskToMagicSchool(it->second.schoolMask)
: audio::SpellSoundManager::MagicSchool::ARCANE;
ssm->playCast(school);
}
}
}
if (!owner_.isProfessionSpell(data.spellId))
playSpellCastSound(data.spellId);
// Instant melee abilities → trigger attack animation
uint32_t sid = data.spellId;
@ -1039,18 +1056,8 @@ void SpellHandler::handleSpellGo(network::Packet& packet) {
for (const auto& tgt : data.hitTargets) {
if (tgt == owner_.getPlayerGuid()) { targetsPlayer = true; break; }
}
if (targetsPlayer) {
if (auto* ac = owner_.services().audioCoordinator) {
if (auto* ssm = ac->getSpellSoundManager()) {
owner_.loadSpellNameCache();
auto it = owner_.spellNameCacheRef().find(data.spellId);
auto school = (it != owner_.spellNameCacheRef().end() && it->second.schoolMask)
? schoolMaskToMagicSchool(it->second.schoolMask)
: audio::SpellSoundManager::MagicSchool::ARCANE;
ssm->playCast(school);
}
}
}
if (targetsPlayer)
playSpellCastSound(data.spellId);
}
// Clear unit cast bar
@ -1085,18 +1092,8 @@ void SpellHandler::handleSpellGo(network::Packet& packet) {
owner_.addonEventCallbackRef()("UNIT_SPELLCAST_SUCCEEDED", {unitId, std::to_string(data.spellId)});
}
if (playerIsHit || playerHitEnemy) {
if (auto* ac = owner_.services().audioCoordinator) {
if (auto* ssm = ac->getSpellSoundManager()) {
owner_.loadSpellNameCache();
auto it = owner_.spellNameCacheRef().find(data.spellId);
auto school = (it != owner_.spellNameCacheRef().end() && it->second.schoolMask)
? schoolMaskToMagicSchool(it->second.schoolMask)
: audio::SpellSoundManager::MagicSchool::ARCANE;
ssm->playImpact(school, audio::SpellSoundManager::SpellPower::MEDIUM);
}
}
}
if (playerIsHit || playerHitEnemy)
playSpellImpactSound(data.spellId);
}
void SpellHandler::handleSpellCooldown(network::Packet& packet) {
@ -1225,7 +1222,7 @@ void SpellHandler::handleAuraUpdate(network::Packet& packet, bool isAll) {
// Sprint aura detection — check if any sprint/dash speed buff is active
if (data.guid == owner_.getPlayerGuid() && owner_.sprintAuraCallbackRef()) {
static const uint32_t sprintSpells[] = {
static constexpr uint32_t sprintSpells[] = {
2983, 8696, 11305, // Rogue Sprint (ranks 1-3)
1850, 9821, 33357, // Druid Dash (ranks 1-3)
36554, // Shadowstep (speed component)
@ -1816,7 +1813,7 @@ void SpellHandler::loadSpellNameCache() const {
if (hasSchoolMask) {
entry.schoolMask = dbc->getUInt32(i, schoolMaskField);
} else if (hasSchoolEnum) {
static const uint32_t enumToBitmask[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40};
static constexpr uint32_t enumToBitmask[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40};
uint32_t e = dbc->getUInt32(i, schoolEnumField);
entry.schoolMask = (e < 7) ? enumToBitmask[e] : 0;
}
@ -2941,6 +2938,163 @@ void SpellHandler::handleSpellInstaKillLog(network::Packet& packet) {
packet.skipAll();
}
// ---- handleSpellLogExecute per-effect parsers (extracted to reduce nesting) ----
void SpellHandler::parseEffectPowerDrain(network::Packet& packet, uint32_t effectLogCount,
uint64_t caster, uint32_t spellId,
bool isPlayerCaster, bool usesFullGuid) {
// SPELL_EFFECT_POWER_DRAIN: packed_guid target + uint32 amount + uint32 powerType + float multiplier
const uint64_t playerGuid = owner_.getPlayerGuid();
for (uint32_t li = 0; li < effectLogCount; ++li) {
if (!packet.hasRemaining(usesFullGuid ? 8u : 1u)
|| (!usesFullGuid && !packet.hasFullPackedGuid())) {
packet.skipAll(); break;
}
uint64_t drainTarget = usesFullGuid ? packet.readUInt64() : packet.readPackedGuid();
if (!packet.hasRemaining(12)) { packet.skipAll(); break; }
uint32_t drainAmount = packet.readUInt32();
uint32_t drainPower = packet.readUInt32(); // 0=mana,1=rage,3=energy,6=runic
float drainMult = packet.readFloat();
LOG_DEBUG("SMSG_SPELLLOGEXECUTE POWER_DRAIN: spell=", spellId,
" power=", drainPower, " amount=", drainAmount,
" multiplier=", drainMult);
if (drainAmount == 0) continue;
const auto powerByte = static_cast<uint8_t>(drainPower);
if (drainTarget == playerGuid)
owner_.addCombatText(CombatTextEntry::POWER_DRAIN,
static_cast<int32_t>(drainAmount), spellId, false,
powerByte, caster, drainTarget);
if (!isPlayerCaster) continue;
if (drainTarget != playerGuid)
owner_.addCombatText(CombatTextEntry::POWER_DRAIN,
static_cast<int32_t>(drainAmount), spellId, true,
powerByte, caster, drainTarget);
if (drainMult <= 0.0f || !std::isfinite(drainMult)) continue;
const uint32_t gained = static_cast<uint32_t>(
std::lround(static_cast<double>(drainAmount) * static_cast<double>(drainMult)));
if (gained > 0)
owner_.addCombatText(CombatTextEntry::ENERGIZE,
static_cast<int32_t>(gained), spellId, true,
powerByte, caster, caster);
}
}
void SpellHandler::parseEffectHealthLeech(network::Packet& packet, uint32_t effectLogCount,
uint64_t caster, uint32_t spellId,
bool isPlayerCaster, bool usesFullGuid) {
// SPELL_EFFECT_HEALTH_LEECH: packed_guid target + uint32 amount + float multiplier
const uint64_t playerGuid = owner_.getPlayerGuid();
for (uint32_t li = 0; li < effectLogCount; ++li) {
if (!packet.hasRemaining(usesFullGuid ? 8u : 1u)
|| (!usesFullGuid && !packet.hasFullPackedGuid())) {
packet.skipAll(); break;
}
uint64_t leechTarget = usesFullGuid ? packet.readUInt64() : packet.readPackedGuid();
if (!packet.hasRemaining(8)) { packet.skipAll(); break; }
uint32_t leechAmount = packet.readUInt32();
float leechMult = packet.readFloat();
LOG_DEBUG("SMSG_SPELLLOGEXECUTE HEALTH_LEECH: spell=", spellId,
" amount=", leechAmount, " multiplier=", leechMult);
if (leechAmount == 0) continue;
if (leechTarget == playerGuid) {
owner_.addCombatText(CombatTextEntry::SPELL_DAMAGE,
static_cast<int32_t>(leechAmount), spellId, false, 0,
caster, leechTarget);
} else if (isPlayerCaster) {
owner_.addCombatText(CombatTextEntry::SPELL_DAMAGE,
static_cast<int32_t>(leechAmount), spellId, true, 0,
caster, leechTarget);
}
if (!isPlayerCaster || leechMult <= 0.0f || !std::isfinite(leechMult)) continue;
const uint32_t gained = static_cast<uint32_t>(
std::lround(static_cast<double>(leechAmount) * static_cast<double>(leechMult)));
if (gained > 0)
owner_.addCombatText(CombatTextEntry::HEAL,
static_cast<int32_t>(gained), spellId, true, 0,
caster, caster);
}
}
void SpellHandler::parseEffectCreateItem(network::Packet& packet, uint32_t effectLogCount,
uint64_t /*caster*/, uint32_t spellId,
bool isPlayerCaster) {
// SPELL_EFFECT_CREATE_ITEM / CREATE_ITEM2: uint32 itemEntry per log entry
for (uint32_t li = 0; li < effectLogCount; ++li) {
if (!packet.hasRemaining(4)) break;
uint32_t itemEntry = packet.readUInt32();
if (!isPlayerCaster || itemEntry == 0) continue;
owner_.ensureItemInfo(itemEntry);
const ItemQueryResponseData* info = owner_.getItemInfo(itemEntry);
std::string itemName = (info && !info->name.empty())
? info->name : ("item #" + std::to_string(itemEntry));
const auto& spellName = owner_.getSpellName(spellId);
std::string msg = spellName.empty()
? ("You create: " + itemName + ".")
: ("You create " + itemName + " using " + spellName + ".");
owner_.addSystemChatMessage(msg);
LOG_DEBUG("SMSG_SPELLLOGEXECUTE CREATE_ITEM: spell=", spellId,
" item=", itemEntry, " name=", itemName);
// Repeat-craft queue: re-cast if more crafts remaining
if (craftQueueRemaining_ > 0 && craftQueueSpellId_ == spellId) {
--craftQueueRemaining_;
if (craftQueueRemaining_ > 0)
castSpell(craftQueueSpellId_, 0);
else
craftQueueSpellId_ = 0;
}
}
}
void SpellHandler::parseEffectInterruptCast(network::Packet& packet, uint32_t effectLogCount,
uint64_t caster, uint32_t spellId,
bool isPlayerCaster, bool usesFullGuid) {
// SPELL_EFFECT_INTERRUPT_CAST: packed_guid target + uint32 interrupted_spell_id
const uint64_t playerGuid = owner_.getPlayerGuid();
for (uint32_t li = 0; li < effectLogCount; ++li) {
if (!packet.hasRemaining(usesFullGuid ? 8u : 1u)
|| (!usesFullGuid && !packet.hasFullPackedGuid())) {
packet.skipAll(); break;
}
uint64_t icTarget = usesFullGuid ? packet.readUInt64() : packet.readPackedGuid();
if (!packet.hasRemaining(4)) { packet.skipAll(); break; }
uint32_t icSpellId = packet.readUInt32();
// Clear the interrupted unit's cast bar immediately
unitCastStates_.erase(icTarget);
// Record interrupt in combat log when player is involved
if (isPlayerCaster || icTarget == playerGuid)
owner_.addCombatText(CombatTextEntry::INTERRUPT, 0, icSpellId, isPlayerCaster, 0,
caster, icTarget);
LOG_DEBUG("SMSG_SPELLLOGEXECUTE INTERRUPT_CAST: spell=", spellId,
" interrupted=", icSpellId, " target=0x", std::hex, icTarget, std::dec);
}
}
void SpellHandler::parseEffectFeedPet(network::Packet& packet, uint32_t effectLogCount,
uint64_t /*caster*/, uint32_t /*spellId*/,
bool isPlayerCaster) {
// SPELL_EFFECT_FEED_PET: uint32 itemEntry per log entry
for (uint32_t li = 0; li < effectLogCount; ++li) {
if (!packet.hasRemaining(4)) break;
uint32_t feedItem = packet.readUInt32();
if (!isPlayerCaster || feedItem == 0) continue;
owner_.ensureItemInfo(feedItem);
const ItemQueryResponseData* info = owner_.getItemInfo(feedItem);
std::string itemName = (info && !info->name.empty())
? info->name : ("item #" + std::to_string(feedItem));
uint32_t feedQuality = info ? info->quality : 1u;
owner_.addSystemChatMessage("You feed your pet " +
buildItemLink(feedItem, feedQuality, itemName) + ".");
LOG_DEBUG("SMSG_SPELLLOGEXECUTE FEED_PET: item=", feedItem, " name=", itemName);
}
}
void SpellHandler::handleSpellLogExecute(network::Packet& packet) {
// WotLK/Classic/Turtle: packed_guid caster + uint32 spellId + uint32 effectCount
// TBC: uint64 caster + uint32 spellId + uint32 effectCount
@ -2973,142 +3127,22 @@ void SpellHandler::handleSpellLogExecute(network::Packet& packet) {
uint8_t effectType = packet.readUInt8();
uint32_t effectLogCount = packet.readUInt32();
effectLogCount = std::min(effectLogCount, 64u); // sanity
if (effectType == SpellEffect::POWER_DRAIN) {
// SPELL_EFFECT_POWER_DRAIN: packed_guid target + uint32 amount + uint32 powerType + float multiplier
for (uint32_t li = 0; li < effectLogCount; ++li) {
if (!packet.hasRemaining(exeUsesFullGuid ? 8u : 1u)
|| (!exeUsesFullGuid && !packet.hasFullPackedGuid())) {
packet.skipAll(); break;
}
uint64_t drainTarget = exeUsesFullGuid
? packet.readUInt64()
: packet.readPackedGuid();
if (!packet.hasRemaining(12)) { packet.skipAll(); break; }
uint32_t drainAmount = packet.readUInt32();
uint32_t drainPower = packet.readUInt32(); // 0=mana,1=rage,3=energy,6=runic
float drainMult = packet.readFloat();
if (drainAmount > 0) {
if (drainTarget == owner_.getPlayerGuid())
owner_.addCombatText(CombatTextEntry::POWER_DRAIN, static_cast<int32_t>(drainAmount), exeSpellId, false,
static_cast<uint8_t>(drainPower),
exeCaster, drainTarget);
if (isPlayerCaster) {
if (drainTarget != owner_.getPlayerGuid()) {
owner_.addCombatText(CombatTextEntry::POWER_DRAIN, static_cast<int32_t>(drainAmount), exeSpellId, true,
static_cast<uint8_t>(drainPower), exeCaster, drainTarget);
}
if (drainMult > 0.0f && std::isfinite(drainMult)) {
const uint32_t gainedAmount = static_cast<uint32_t>(
std::lround(static_cast<double>(drainAmount) * static_cast<double>(drainMult)));
if (gainedAmount > 0) {
owner_.addCombatText(CombatTextEntry::ENERGIZE, static_cast<int32_t>(gainedAmount), exeSpellId, true,
static_cast<uint8_t>(drainPower), exeCaster, exeCaster);
}
}
}
}
LOG_DEBUG("SMSG_SPELLLOGEXECUTE POWER_DRAIN: spell=", exeSpellId,
" power=", drainPower, " amount=", drainAmount,
" multiplier=", drainMult);
}
} else if (effectType == SpellEffect::HEALTH_LEECH) {
// SPELL_EFFECT_HEALTH_LEECH: packed_guid target + uint32 amount + float multiplier
for (uint32_t li = 0; li < effectLogCount; ++li) {
if (!packet.hasRemaining(exeUsesFullGuid ? 8u : 1u)
|| (!exeUsesFullGuid && !packet.hasFullPackedGuid())) {
packet.skipAll(); break;
}
uint64_t leechTarget = exeUsesFullGuid
? packet.readUInt64()
: packet.readPackedGuid();
if (!packet.hasRemaining(8)) { packet.skipAll(); break; }
uint32_t leechAmount = packet.readUInt32();
float leechMult = packet.readFloat();
if (leechAmount > 0) {
if (leechTarget == owner_.getPlayerGuid()) {
owner_.addCombatText(CombatTextEntry::SPELL_DAMAGE, static_cast<int32_t>(leechAmount), exeSpellId, false, 0,
exeCaster, leechTarget);
} else if (isPlayerCaster) {
owner_.addCombatText(CombatTextEntry::SPELL_DAMAGE, static_cast<int32_t>(leechAmount), exeSpellId, true, 0,
exeCaster, leechTarget);
}
if (isPlayerCaster && leechMult > 0.0f && std::isfinite(leechMult)) {
const uint32_t gainedAmount = static_cast<uint32_t>(
std::lround(static_cast<double>(leechAmount) * static_cast<double>(leechMult)));
if (gainedAmount > 0) {
owner_.addCombatText(CombatTextEntry::HEAL, static_cast<int32_t>(gainedAmount), exeSpellId, true, 0,
exeCaster, exeCaster);
}
}
}
LOG_DEBUG("SMSG_SPELLLOGEXECUTE HEALTH_LEECH: spell=", exeSpellId,
" amount=", leechAmount, " multiplier=", leechMult);
}
} else if (effectType == SpellEffect::CREATE_ITEM || effectType == SpellEffect::CREATE_ITEM2) {
// SPELL_EFFECT_CREATE_ITEM / CREATE_ITEM2: uint32 itemEntry per log entry
for (uint32_t li = 0; li < effectLogCount; ++li) {
if (!packet.hasRemaining(4)) break;
uint32_t itemEntry = packet.readUInt32();
if (isPlayerCaster && itemEntry != 0) {
owner_.ensureItemInfo(itemEntry);
const ItemQueryResponseData* info = owner_.getItemInfo(itemEntry);
std::string itemName = info && !info->name.empty()
? info->name : ("item #" + std::to_string(itemEntry));
const auto& spellName = owner_.getSpellName(exeSpellId);
std::string msg = spellName.empty()
? ("You create: " + itemName + ".")
: ("You create " + itemName + " using " + spellName + ".");
owner_.addSystemChatMessage(msg);
LOG_DEBUG("SMSG_SPELLLOGEXECUTE CREATE_ITEM: spell=", exeSpellId,
" item=", itemEntry, " name=", itemName);
// Repeat-craft queue: re-cast if more crafts remaining
if (craftQueueRemaining_ > 0 && craftQueueSpellId_ == exeSpellId) {
--craftQueueRemaining_;
if (craftQueueRemaining_ > 0) {
castSpell(craftQueueSpellId_, 0);
} else {
craftQueueSpellId_ = 0;
}
}
}
}
if (effectType == SpellEffect::POWER_DRAIN) {
parseEffectPowerDrain(packet, effectLogCount, exeCaster, exeSpellId,
isPlayerCaster, exeUsesFullGuid);
} else if (effectType == SpellEffect::HEALTH_LEECH) {
parseEffectHealthLeech(packet, effectLogCount, exeCaster, exeSpellId,
isPlayerCaster, exeUsesFullGuid);
} else if (effectType == SpellEffect::CREATE_ITEM || effectType == SpellEffect::CREATE_ITEM2) {
parseEffectCreateItem(packet, effectLogCount, exeCaster, exeSpellId,
isPlayerCaster);
} else if (effectType == SpellEffect::INTERRUPT_CAST) {
// SPELL_EFFECT_INTERRUPT_CAST: packed_guid target + uint32 interrupted_spell_id
for (uint32_t li = 0; li < effectLogCount; ++li) {
if (!packet.hasRemaining(exeUsesFullGuid ? 8u : 1u)
|| (!exeUsesFullGuid && !packet.hasFullPackedGuid())) {
packet.skipAll(); break;
}
uint64_t icTarget = exeUsesFullGuid
? packet.readUInt64()
: packet.readPackedGuid();
if (!packet.hasRemaining(4)) { packet.skipAll(); break; }
uint32_t icSpellId = packet.readUInt32();
// Clear the interrupted unit's cast bar immediately
unitCastStates_.erase(icTarget);
// Record interrupt in combat log when player is involved
if (isPlayerCaster || icTarget == owner_.getPlayerGuid())
owner_.addCombatText(CombatTextEntry::INTERRUPT, 0, icSpellId, isPlayerCaster, 0,
exeCaster, icTarget);
LOG_DEBUG("SMSG_SPELLLOGEXECUTE INTERRUPT_CAST: spell=", exeSpellId,
" interrupted=", icSpellId, " target=0x", std::hex, icTarget, std::dec);
}
parseEffectInterruptCast(packet, effectLogCount, exeCaster, exeSpellId,
isPlayerCaster, exeUsesFullGuid);
} else if (effectType == SpellEffect::FEED_PET) {
// SPELL_EFFECT_FEED_PET: uint32 itemEntry per log entry
for (uint32_t li = 0; li < effectLogCount; ++li) {
if (!packet.hasRemaining(4)) break;
uint32_t feedItem = packet.readUInt32();
if (isPlayerCaster && feedItem != 0) {
owner_.ensureItemInfo(feedItem);
const ItemQueryResponseData* info = owner_.getItemInfo(feedItem);
std::string itemName = info && !info->name.empty()
? info->name : ("item #" + std::to_string(feedItem));
uint32_t feedQuality = info ? info->quality : 1u;
owner_.addSystemChatMessage("You feed your pet " + buildItemLink(feedItem, feedQuality, itemName) + ".");
LOG_DEBUG("SMSG_SPELLLOGEXECUTE FEED_PET: item=", feedItem, " name=", itemName);
}
}
parseEffectFeedPet(packet, effectLogCount, exeCaster, exeSpellId,
isPlayerCaster);
} else {
// Unknown effect type — stop parsing to avoid misalignment
packet.skipAll();

View file

@ -1282,14 +1282,14 @@ uint32_t TransportManager::pickFallbackMovingPath(uint32_t entry, uint32_t displ
(displayId == 3031u || displayId == 7546u || displayId == 1587u || displayId == 807u || displayId == 808u);
if (looksLikeShip) {
static const uint32_t kShipCandidates[] = {176080u, 176081u, 176082u, 176083u, 176084u, 176085u, 194675u};
static constexpr uint32_t kShipCandidates[] = {176080u, 176081u, 176082u, 176083u, 176084u, 176085u, 194675u};
for (uint32_t id : kShipCandidates) {
if (isUsableMovingPath(id)) return id;
}
}
if (looksLikeZeppelin) {
static const uint32_t kZeppelinCandidates[] = {193182u, 193183u, 188360u, 190587u};
static constexpr uint32_t kZeppelinCandidates[] = {193182u, 193183u, 188360u, 190587u};
for (uint32_t id : kZeppelinCandidates) {
if (isUsableMovingPath(id)) return id;
}

View file

@ -203,7 +203,7 @@ uint32_t WardenEmulator::hookAPI(const std::string& dllName,
// Write a RET (0xC3) at the stub address as a safe fallback in case
// the code hook fires after EIP has already advanced past our intercept.
if (uc_) {
static const uint8_t retInstr = 0xC3;
static constexpr uint8_t retInstr = 0xC3;
uc_mem_write(uc_, stubAddr, &retInstr, 1);
}

View file

@ -10,6 +10,7 @@
#include "core/application.hpp"
#include "pipeline/asset_manager.hpp"
#include "core/logger.hpp"
#include "game/warden_constants.hpp"
#include <algorithm>
#include <cctype>
#include <chrono>
@ -355,7 +356,7 @@ void WardenHandler::handleWardenData(network::Packet& packet) {
};
switch (wardenOpcode) {
case 0x00: { // WARDEN_SMSG_MODULE_USE
case WARDEN_SMSG_MODULE_USE: { // MODULE_USE
// Format: [1 opcode][16 moduleHash][16 moduleKey][4 moduleSize]
if (decrypted.size() < 37) {
LOG_ERROR("Warden: MODULE_USE too short (", decrypted.size(), " bytes, need 37)");
@ -379,15 +380,15 @@ void WardenHandler::handleWardenData(network::Packet& packet) {
loadWardenCRFile(hashHex);
}
// Respond with MODULE_MISSING (opcode 0x00) to request the module data
std::vector<uint8_t> resp = { 0x00 }; // WARDEN_CMSG_MODULE_MISSING
// Respond with MODULE_MISSING to request the module data
std::vector<uint8_t> resp = { WARDEN_CMSG_MODULE_MISSING };
sendWardenResponse(resp);
wardenState_ = WardenState::WAIT_MODULE_CACHE;
LOG_DEBUG("Warden: Sent MODULE_MISSING for module size=", wardenModuleSize_, ", waiting for data chunks");
break;
}
case 0x01: { // WARDEN_SMSG_MODULE_CACHE (module data chunk)
case WARDEN_SMSG_MODULE_CACHE: { // MODULE_CACHE (module data chunk)
// Format: [1 opcode][2 chunkSize LE][chunkSize bytes data]
if (decrypted.size() < 3) {
LOG_ERROR("Warden: MODULE_CACHE too short");
@ -463,8 +464,8 @@ void WardenHandler::handleWardenData(network::Packet& packet) {
wardenLoadedModule_.reset();
}
// Send MODULE_OK (opcode 0x01)
std::vector<uint8_t> resp = { 0x01 }; // WARDEN_CMSG_MODULE_OK
// Send MODULE_OK
std::vector<uint8_t> resp = { WARDEN_CMSG_MODULE_OK };
sendWardenResponse(resp);
LOG_DEBUG("Warden: Sent MODULE_OK");
}
@ -472,7 +473,7 @@ void WardenHandler::handleWardenData(network::Packet& packet) {
break;
}
case 0x05: { // WARDEN_SMSG_HASH_REQUEST
case WARDEN_SMSG_HASH_REQUEST: { // HASH_REQUEST
// Format: [1 opcode][16 seed]
if (decrypted.size() < 17) {
LOG_ERROR("Warden: HASH_REQUEST too short (", decrypted.size(), " bytes, need 17)");
@ -506,9 +507,9 @@ void WardenHandler::handleWardenData(network::Packet& packet) {
if (match) {
LOG_DEBUG("Warden: HASH_REQUEST — CR entry MATCHED, sending pre-computed reply");
// Send HASH_RESULT (opcode 0x04 + 20-byte reply)
// Send HASH_RESULT
std::vector<uint8_t> resp;
resp.push_back(0x04);
resp.push_back(WARDEN_CMSG_HASH_RESULT);
resp.insert(resp.end(), match->reply, match->reply + 20);
sendWardenResponse(resp);
@ -576,7 +577,7 @@ void WardenHandler::handleWardenData(network::Packet& packet) {
break;
}
case 0x02: { // WARDEN_SMSG_CHEAT_CHECKS_REQUEST
case WARDEN_SMSG_CHEAT_CHECKS_REQUEST: { // CHEAT_CHECKS_REQUEST
LOG_DEBUG("Warden: CHEAT_CHECKS_REQUEST (", decrypted.size(), " bytes)");
if (decrypted.size() < 3) {
@ -660,9 +661,9 @@ void WardenHandler::handleWardenData(network::Packet& packet) {
HMAC(EVP_sha1(), seed, 4, pat, patLen, out, &outLen);
return outLen == SHA_DIGEST_LENGTH && !std::memcmp(out, hash, SHA_DIGEST_LENGTH);
};
static const uint8_t p1[] = {0x33,0xD2,0x33,0xC9,0xE8,0x87,0x07,0x1B,0x00,0xE8};
static constexpr uint8_t p1[] = {0x33,0xD2,0x33,0xC9,0xE8,0x87,0x07,0x1B,0x00,0xE8};
if (off == 13856 && len == sizeof(p1) && tryMatch(p1, sizeof(p1))) return true;
static const uint8_t p2[] = {0x56,0x57,0xFC,0x8B,0x54,0x24,0x14,0x8B,
static constexpr uint8_t p2[] = {0x56,0x57,0xFC,0x8B,0x54,0x24,0x14,0x8B,
0x74,0x24,0x10,0x8B,0x44,0x24,0x0C,0x8B,0xCA,0x8B,0xF8,0xC1,
0xE9,0x02,0x74,0x02,0xF3,0xA5,0xB1,0x03,0x23,0xCA,0x74,0x02,
0xF3,0xA4,0x5F,0x5E,0xC3};
@ -706,7 +707,7 @@ void WardenHandler::handleWardenData(network::Packet& packet) {
LOG_WARNING("Warden: MEM offset=0x", [&]{char s[12];snprintf(s,12,"%08x",offset);return std::string(s);}(),
" len=", (int)readLen,
(strIdx ? " module=\"" + moduleName + "\"" : ""));
if (offset == 0x00CF0BC8 && readLen == 4 && wardenMemory_ && wardenMemory_->isLoaded()) {
if (offset == WARDEN_TICKCOUNT_ADDRESS && readLen == 4 && wardenMemory_ && wardenMemory_->isLoaded()) {
uint32_t now = static_cast<uint32_t>(
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch()).count());
@ -717,25 +718,25 @@ void WardenHandler::handleWardenData(network::Packet& packet) {
wardenMemory_->readMemory(offset, readLen, memBuf.data());
if (memOk) {
const char* region = "?";
if (offset >= 0x7FFE0000 && offset < 0x7FFF0000) region = "KUSER";
else if (offset >= 0x400000 && offset < 0x800000) region = ".text/.code";
else if (offset >= 0x7FF000 && offset < 0x827000) region = ".rdata";
else if (offset >= 0x827000 && offset < 0x883000) region = ".data(raw)";
else if (offset >= 0x883000 && offset < 0xD06000) region = ".data(BSS)";
if (offset >= KUSER_SHARED_DATA_BASE && offset < KUSER_SHARED_DATA_END) region = "KUSER";
else if (offset >= PE_TEXT_SECTION_BASE && offset < PE_TEXT_SECTION_END) region = ".text/.code";
else if (offset >= PE_RDATA_SECTION_BASE && offset < PE_DATA_RAW_SECTION_BASE) region = ".rdata";
else if (offset >= PE_DATA_RAW_SECTION_BASE && offset < PE_BSS_SECTION_BASE) region = ".data(raw)";
else if (offset >= PE_BSS_SECTION_BASE && offset < PE_BSS_SECTION_END) region = ".data(BSS)";
bool allZero = true;
for (int i = 0; i < (int)readLen; i++) { if (memBuf[i] != 0) { allZero = false; break; } }
std::string hexDump;
for (int i = 0; i < (int)readLen; i++) { char hx[4]; snprintf(hx,4,"%02x ",memBuf[i]); hexDump += hx; }
LOG_WARNING("Warden: MEM_CHECK served: [", hexDump, "] region=", region,
(allZero && offset >= 0x883000 ? " \xe2\x98\x85""BSS_ZERO\xe2\x98\x85" : ""));
if (offset == 0x7FFE026C && readLen == 12)
(allZero && offset >= PE_BSS_SECTION_BASE ? " \xe2\x98\x85""BSS_ZERO\xe2\x98\x85" : ""));
if (offset == WARDEN_WIN_VERSION_ADDRESS && readLen == 12)
LOG_WARNING("Warden: Applying 4-byte ULONG alignment padding for WinVersionGet");
resultData.push_back(0x00);
resultData.push_back(WARDEN_MEM_CHECK_SUCCESS);
resultData.insert(resultData.end(), memBuf.begin(), memBuf.end());
} else {
LOG_WARNING("Warden: MEM_CHECK -> 0xE9 (unmapped 0x",
[&]{char s[12];snprintf(s,12,"%08x",offset);return std::string(s);}(), ")");
resultData.push_back(0xE9);
resultData.push_back(WARDEN_MEM_CHECK_UNMAPPED);
}
break;
}
@ -936,7 +937,7 @@ void WardenHandler::handleWardenData(network::Packet& packet) {
};
// DB sanity check: "Warden packet process code search sanity check" (id=85)
static const uint8_t kPacketProcessSanityPattern[] = {
static constexpr uint8_t kPacketProcessSanityPattern[] = {
0x33, 0xD2, 0x33, 0xC9, 0xE8, 0x87, 0x07, 0x1B, 0x00, 0xE8
};
if (offset == 13856 && length == sizeof(kPacketProcessSanityPattern) &&
@ -945,7 +946,7 @@ void WardenHandler::handleWardenData(network::Packet& packet) {
}
// Scripted sanity check: "Warden Memory Read check" in wardenwin.cpp
static const uint8_t kWardenMemoryReadPattern[] = {
static constexpr uint8_t kWardenMemoryReadPattern[] = {
0x56, 0x57, 0xFC, 0x8B, 0x54, 0x24, 0x14, 0x8B,
0x74, 0x24, 0x10, 0x8B, 0x44, 0x24, 0x0C, 0x8B,
0xCA, 0x8B, 0xF8, 0xC1, 0xE9, 0x02, 0x74, 0x02,

View file

@ -426,7 +426,7 @@ void WardenMemory::patchRuntimeGlobals() {
// FIND_CODE_BY_HASH (PAGE_B) brute-force search can find it.
// This is the pattern VMaNGOS's "Warden Memory Read check" looks for.
constexpr uint32_t MEMCPY_PATTERN_VA = 0xCE8700;
static const uint8_t kWardenMemcpyPattern[37] = {
static constexpr uint8_t kWardenMemcpyPattern[37] = {
0x56, 0x57, 0xFC, 0x8B, 0x54, 0x24, 0x14, 0x8B,
0x74, 0x24, 0x10, 0x8B, 0x44, 0x24, 0x0C, 0x8B,
0xCA, 0x8B, 0xF8, 0xC1, 0xE9, 0x02, 0x74, 0x02,

View file

@ -141,7 +141,7 @@ network::Packet AuthSessionPacket::build(uint32_t build,
"Blizzard_InspectUI", "Blizzard_MacroUI", "Blizzard_RaidUI",
"Blizzard_TalentUI", "Blizzard_TradeSkillUI", "Blizzard_TrainerUI"
};
static const uint32_t standardModulusCRC = 0x4C1C776D;
static constexpr uint32_t standardModulusCRC = 0x4C1C776D;
for (const char* name : vanillaAddons) {
// string (null-terminated)
size_t len = strlen(name);