mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-17 17:43:52 +00:00
feat(game): introduce GameHandler domain interfaces and eliminate friend declarations
Add game_interfaces.hpp with five narrow domain contracts that GameHandler now publishes to its domain handlers, replacing the previous friend-class anti-pattern. Changes: - include/game/game_interfaces.hpp (new): IConnectionState, ITargetingState, IEntityAccess, ISocialState, IPvpState — each interface exposes only the state its consumer legitimately needs - include/game/game_handler.hpp: GameHandler inherits all five interfaces; include of game_interfaces.hpp added - include/game/movement_handler.hpp: remove `friend class GameHandler`; add public named accessors for previously-private fields (monsterMovePacketsThisTickRef, timeSinceLastMoveHeartbeatRef, resetMovementClock, setFalling, setFallStartMs) - include/game/spell_handler.hpp: remove `friend class GameHandler/InventoryHandler/ CombatHandler/EntityController`; promote private packet handlers (handlePetSpells, handleListStabledPets, pet stable commands, DBC loaders) to public; add accessor methods for aura cache, known spells, and player aura slot mutation - src/game/game_handler.cpp, game_handler_callbacks.cpp, game_handler_packets.cpp: replace direct private field access with the new accessor API (e.g. casting_ → isCasting(), monsterMovePacketsThisTick_ → ...ThisTickRef()) - src/game/inventory_handler.cpp, combat_handler.cpp, entity_controller.cpp: replace friend-class private access with public accessor calls No behaviour change. All 13 test suites pass. Zero build warnings.
This commit is contained in:
parent
34c0e3ca28
commit
65839287b4
10 changed files with 196 additions and 47 deletions
|
|
@ -1065,7 +1065,7 @@ void CombatHandler::setTarget(uint64_t guid) {
|
|||
|
||||
// Clear stale aura data from the previous target so the buff bar shows
|
||||
// an empty state until the server sends SMSG_AURA_UPDATE_ALL for the new target.
|
||||
if (owner_.getSpellHandler()) for (auto& slot : owner_.getSpellHandler()->targetAuras_) slot = AuraSlot{};
|
||||
if (owner_.getSpellHandler()) owner_.getSpellHandler()->clearTargetAuras();
|
||||
|
||||
// Clear previous target's cast bar on target change
|
||||
// (the new target's cast state is naturally fetched from spellHandler_->unitCastStates_ by GUID)
|
||||
|
|
|
|||
|
|
@ -449,15 +449,14 @@ void EntityController::syncClassicAurasFromFields(const std::shared_ptr<Entity>&
|
|||
}
|
||||
if (!hasAuraField) return;
|
||||
|
||||
owner_.getSpellHandler()->playerAuras_.clear();
|
||||
owner_.getSpellHandler()->playerAuras_.resize(48);
|
||||
owner_.getSpellHandler()->resetPlayerAuras(48);
|
||||
uint64_t nowMs = static_cast<uint64_t>(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch()).count());
|
||||
for (int slot = 0; slot < 48; ++slot) {
|
||||
auto it = allFields.find(static_cast<uint16_t>(ufAuras + slot));
|
||||
if (it != allFields.end() && it->second != 0) {
|
||||
AuraSlot& a = owner_.getSpellHandler()->playerAuras_[slot];
|
||||
AuraSlot& a = owner_.getSpellHandler()->getPlayerAuraSlotRef(slot);
|
||||
a.spellId = it->second;
|
||||
// Read aura flag byte: packed 4-per-uint32 at ufAuraFlags
|
||||
uint8_t aFlag = 0;
|
||||
|
|
@ -492,7 +491,7 @@ void EntityController::detectPlayerMountChange(uint32_t newMountDisplayId,
|
|||
if (old == 0 && newMountDisplayId != 0) {
|
||||
// Just mounted — find the mount aura (indefinite duration, self-cast)
|
||||
owner_.mountAuraSpellIdRef() = 0;
|
||||
if (owner_.getSpellHandler()) for (const auto& a : owner_.getSpellHandler()->playerAuras_) {
|
||||
if (owner_.getSpellHandler()) for (const auto& a : owner_.getSpellHandler()->getPlayerAuras()) {
|
||||
if (!a.isEmpty() && a.maxDurationMs < 0 && a.casterGuid == owner_.getPlayerGuid()) {
|
||||
owner_.mountAuraSpellIdRef() = a.spellId;
|
||||
}
|
||||
|
|
@ -518,7 +517,7 @@ void EntityController::detectPlayerMountChange(uint32_t newMountDisplayId,
|
|||
uint32_t mountSpell = owner_.mountAuraSpellIdRef();
|
||||
owner_.mountAuraSpellIdRef() = 0;
|
||||
if (mountSpell != 0 && owner_.getSpellHandler()) {
|
||||
for (auto& a : owner_.getSpellHandler()->playerAuras_) {
|
||||
for (auto& a : owner_.getSpellHandler()->getPlayerAurasMut()) {
|
||||
if (!a.isEmpty() && a.spellId == mountSpell) {
|
||||
a = AuraSlot{};
|
||||
break;
|
||||
|
|
@ -1950,9 +1949,9 @@ void EntityController::handleDestroyObject(network::Packet& packet) {
|
|||
if (owner_.getCombatHandler()) owner_.getCombatHandler()->removeCombatTextForGuid(data.guid);
|
||||
|
||||
// Clean up unit cast owner_.getState() (cast bar) for the destroyed unit
|
||||
if (owner_.getSpellHandler()) owner_.getSpellHandler()->unitCastStates_.erase(data.guid);
|
||||
if (owner_.getSpellHandler()) owner_.getSpellHandler()->removeUnitCastState(data.guid);
|
||||
// Clean up cached auras
|
||||
if (owner_.getSpellHandler()) owner_.getSpellHandler()->unitAurasCache_.erase(data.guid);
|
||||
if (owner_.getSpellHandler()) owner_.getSpellHandler()->removeUnitAuraCache(data.guid);
|
||||
|
||||
owner_.tabCycleStaleRef() = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -272,8 +272,8 @@ void GameHandler::disconnect() {
|
|||
otherPlayerVisibleItemEntries_.clear();
|
||||
otherPlayerVisibleDirty_.clear();
|
||||
otherPlayerMoveTimeMs_.clear();
|
||||
if (spellHandler_) spellHandler_->unitCastStates_.clear();
|
||||
if (spellHandler_) spellHandler_->unitAurasCache_.clear();
|
||||
if (spellHandler_) spellHandler_->clearUnitCastStates();
|
||||
if (spellHandler_) spellHandler_->clearUnitAurasCache();
|
||||
if (combatHandler_) combatHandler_->clearCombatText();
|
||||
entityController_->clearAll();
|
||||
setState(WorldState::DISCONNECTED);
|
||||
|
|
@ -315,8 +315,8 @@ bool GameHandler::isConnected() const {
|
|||
void GameHandler::updateNetworking(float deltaTime) {
|
||||
// Reset per-tick monster-move budget tracking (Classic/Turtle flood protection).
|
||||
if (movementHandler_) {
|
||||
movementHandler_->monsterMovePacketsThisTick_ = 0;
|
||||
movementHandler_->monsterMovePacketsDroppedThisTick_ = 0;
|
||||
movementHandler_->monsterMovePacketsThisTickRef() = 0;
|
||||
movementHandler_->monsterMovePacketsDroppedThisTickRef() = 0;
|
||||
}
|
||||
|
||||
// Update socket (processes incoming data and triggers callbacks)
|
||||
|
|
@ -649,7 +649,7 @@ void GameHandler::updateTimers(float deltaTime) {
|
|||
if (isInWorld()) {
|
||||
// Avoid sending CMSG_LOOT while a timed cast is active (e.g. gathering).
|
||||
// handleSpellGo will trigger loot after the cast completes.
|
||||
if (spellHandler_ && spellHandler_->casting_ && spellHandler_->currentCastSpellId_ != 0) {
|
||||
if (spellHandler_ && spellHandler_->isCasting() && spellHandler_->getCurrentCastSpellId() != 0) {
|
||||
it->timer = 0.20f;
|
||||
++it;
|
||||
continue;
|
||||
|
|
@ -785,7 +785,7 @@ void GameHandler::update(float deltaTime) {
|
|||
// Send periodic heartbeat if in world
|
||||
if (state == WorldState::IN_WORLD) {
|
||||
timeSinceLastPing += deltaTime;
|
||||
if (movementHandler_) movementHandler_->timeSinceLastMoveHeartbeat_ += deltaTime;
|
||||
if (movementHandler_) movementHandler_->timeSinceLastMoveHeartbeatRef() += deltaTime;
|
||||
|
||||
const float currentPingInterval =
|
||||
(isPreWotlk()) ? 10.0f : pingInterval;
|
||||
|
|
@ -823,9 +823,9 @@ void GameHandler::update(float deltaTime) {
|
|||
: (classicLikeStationaryCombatSync ? 0.75f
|
||||
: (classicLikeCombatSync ? 0.20f
|
||||
: moveHeartbeatInterval_));
|
||||
if (movementHandler_ && movementHandler_->timeSinceLastMoveHeartbeat_ >= heartbeatInterval) {
|
||||
if (movementHandler_ && movementHandler_->timeSinceLastMoveHeartbeatRef() >= heartbeatInterval) {
|
||||
sendMovement(Opcode::MSG_MOVE_HEARTBEAT);
|
||||
movementHandler_->timeSinceLastMoveHeartbeat_ = 0.0f;
|
||||
movementHandler_->timeSinceLastMoveHeartbeatRef() = 0.0f;
|
||||
}
|
||||
|
||||
// Check area triggers (instance portals, tavern rests, etc.)
|
||||
|
|
@ -845,7 +845,7 @@ void GameHandler::update(float deltaTime) {
|
|||
}
|
||||
// Check if client-side cast timer expired (tick-down is in SpellHandler::updateTimers).
|
||||
// Two paths depending on whether this is a GO interaction cast:
|
||||
if (spellHandler_ && spellHandler_->casting_ && spellHandler_->castTimeRemaining_ <= 0.0f) {
|
||||
if (spellHandler_ && spellHandler_->isCasting() && spellHandler_->getCastTimeRemaining() <= 0.0f) {
|
||||
if (pendingGameObjectInteractGuid_ != 0) {
|
||||
// GO interaction cast: do NOT call resetCastState() here. The server
|
||||
// sends SMSG_SPELL_GO when the cast completes server-side (~50-200ms
|
||||
|
|
|
|||
|
|
@ -573,13 +573,12 @@ void GameHandler::handleLoginVerifyWorld(network::Packet& packet) {
|
|||
movementInfo.flags = 0;
|
||||
movementInfo.flags2 = 0;
|
||||
if (movementHandler_) {
|
||||
movementHandler_->movementClockStart_ = std::chrono::steady_clock::now();
|
||||
movementHandler_->lastMovementTimestampMs_ = 0;
|
||||
movementHandler_->resetMovementClock();
|
||||
}
|
||||
movementInfo.time = nextMovementTimestampMs();
|
||||
if (movementHandler_) {
|
||||
movementHandler_->isFalling_ = false;
|
||||
movementHandler_->fallStartMs_ = 0;
|
||||
movementHandler_->setFalling(false);
|
||||
movementHandler_->setFallStartMs(0);
|
||||
}
|
||||
movementInfo.fallTime = 0;
|
||||
movementInfo.jumpVelocity = 0.0f;
|
||||
|
|
@ -1945,8 +1944,8 @@ void GameHandler::interactWithGameObject(uint64_t guid) {
|
|||
if (guid == 0) { LOG_WARNING("[GO-DIAG] BLOCKED: guid==0"); return; }
|
||||
if (!isInWorld()) { LOG_WARNING("[GO-DIAG] BLOCKED: not in world"); return; }
|
||||
// Do not overlap an actual spell cast.
|
||||
if (spellHandler_ && spellHandler_->casting_ && spellHandler_->currentCastSpellId_ != 0) {
|
||||
LOG_WARNING("[GO-DIAG] BLOCKED: already casting spellId=", spellHandler_->currentCastSpellId_);
|
||||
if (spellHandler_ && spellHandler_->isCasting() && spellHandler_->getCurrentCastSpellId() != 0) {
|
||||
LOG_WARNING("[GO-DIAG] BLOCKED: already casting spellId=", spellHandler_->getCurrentCastSpellId());
|
||||
return;
|
||||
}
|
||||
// Always clear melee intent before GO interactions.
|
||||
|
|
|
|||
|
|
@ -1019,10 +1019,11 @@ void GameHandler::registerOpcodeHandlers() {
|
|||
// SMSG_SPELL_COOLDOWN often arrives before SMSG_ACTION_BUTTONS during login,
|
||||
// so the per-slot cooldownRemaining would be 0 without this sync.
|
||||
if (spellHandler_) {
|
||||
const auto& cooldowns = spellHandler_->getSpellCooldowns();
|
||||
for (auto& slot : actionBar) {
|
||||
if (slot.type == ActionBarSlot::SPELL && slot.id != 0) {
|
||||
auto cdIt = spellHandler_->spellCooldowns_.find(slot.id);
|
||||
if (cdIt != spellHandler_->spellCooldowns_.end() && cdIt->second > 0.0f) {
|
||||
auto cdIt = cooldowns.find(slot.id);
|
||||
if (cdIt != cooldowns.end() && cdIt->second > 0.0f) {
|
||||
slot.cooldownRemaining = cdIt->second;
|
||||
slot.cooldownTotal = cdIt->second;
|
||||
}
|
||||
|
|
@ -1033,8 +1034,8 @@ void GameHandler::registerOpcodeHandlers() {
|
|||
if (qi && qi->valid) {
|
||||
for (const auto& sp : qi->spells) {
|
||||
if (sp.spellId == 0) continue;
|
||||
auto cdIt = spellHandler_->spellCooldowns_.find(sp.spellId);
|
||||
if (cdIt != spellHandler_->spellCooldowns_.end() && cdIt->second > 0.0f) {
|
||||
auto cdIt = cooldowns.find(sp.spellId);
|
||||
if (cdIt != cooldowns.end() && cdIt->second > 0.0f) {
|
||||
slot.cooldownRemaining = cdIt->second;
|
||||
slot.cooldownTotal = cdIt->second;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -3219,8 +3219,8 @@ void InventoryHandler::emitAllOtherPlayerEquipment() {
|
|||
void InventoryHandler::handleTrainerBuySucceeded(network::Packet& packet) {
|
||||
/*uint64_t guid =*/ packet.readUInt64();
|
||||
uint32_t spellId = packet.readUInt32();
|
||||
if (owner_.getSpellHandler() && !owner_.getSpellHandler()->knownSpells_.count(spellId)) {
|
||||
owner_.getSpellHandler()->knownSpells_.insert(spellId);
|
||||
if (owner_.getSpellHandler() && !owner_.getSpellHandler()->hasKnownSpell(spellId)) {
|
||||
owner_.getSpellHandler()->addKnownSpell(spellId);
|
||||
}
|
||||
const std::string& name = owner_.getSpellName(spellId);
|
||||
if (!name.empty())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue