mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-09 02:23:52 +00:00
refactor(core): decompose Application::setupUICallbacks() into 7 domain handlers
Extract ~1,700 lines / 60+ inline [this]-capturing lambdas from the monolithic
Application::setupUICallbacks() into 7 focused callback handler classes following
the ToastManager/ChatPanel::setupCallbacks() pattern already in the codebase.
New handlers (include/core/ + src/core/):
- NPCInteractionCallbackHandler NPC greeting/farewell/vendor/aggro voice
- AudioCallbackHandler Music, positional sound, level-up, achievement, LFG
- EntitySpawnCallbackHandler Creature/player/GO spawn, despawn, move, state
- AnimationCallbackHandler Death, respawn, combat, emotes, charge, sprint, vehicle
- TransportCallbackHandler Mount, taxi, transport spawn/move
- WorldEntryCallbackHandler World entry, unstuck, hearthstone, bind point
- UIScreenCallbackHandler Auth, realm selection, char selection/creation/deletion
application.cpp: 4,462 → 2,791 lines (−1,671)
setupUICallbacks: ~1,700 → ~50 lines (thin orchestrator)
Deduplication:
resolveSoundEntryPath() — was 3× copy-paste of SoundEntries.dbc lookup
resolveNpcVoiceType() — was 4× copy-paste of display-ID→voice detection
precacheNearbyTiles() — was 3× copy-paste of 17×17 tile loop
4 helper lambdas — promoted to private methods on WorldEntryCallbackHandler
State migration out of Application:
charge* (6 vars) → AnimationCallbackHandler
hearth*/worldEntry*/taxi* → WorldEntryCallbackHandler
pendingCreatedCharacterName_ → UIScreenCallbackHandler
Bug fixes:
- Duplicate `namespace core {` in application.hpp caused wowee::std pollution
- AppState forward decl in ui_screen_callback_handler.hpp was at wrong scope
- world_loader.cpp accessed moved member vars directly via friend; now uses handler API
This commit is contained in:
parent
a23c2172a8
commit
6dcc06697b
18 changed files with 2293 additions and 1765 deletions
275
src/core/transport_callback_handler.cpp
Normal file
275
src/core/transport_callback_handler.cpp
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
#include "core/transport_callback_handler.hpp"
|
||||
#include "core/entity_spawner.hpp"
|
||||
#include "core/world_loader.hpp"
|
||||
#include "core/coordinates.hpp"
|
||||
#include "core/logger.hpp"
|
||||
#include "rendering/renderer.hpp"
|
||||
#include "rendering/character_renderer.hpp"
|
||||
#include "rendering/camera_controller.hpp"
|
||||
#include "rendering/terrain_manager.hpp"
|
||||
#include "rendering/m2_renderer.hpp"
|
||||
#include "game/game_handler.hpp"
|
||||
#include "game/transport_manager.hpp"
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace wowee { namespace core {
|
||||
|
||||
TransportCallbackHandler::TransportCallbackHandler(
|
||||
EntitySpawner& entitySpawner,
|
||||
rendering::Renderer& renderer,
|
||||
game::GameHandler& gameHandler,
|
||||
WorldLoader* worldLoader)
|
||||
: entitySpawner_(entitySpawner)
|
||||
, renderer_(renderer)
|
||||
, gameHandler_(gameHandler)
|
||||
, worldLoader_(worldLoader)
|
||||
{
|
||||
}
|
||||
|
||||
void TransportCallbackHandler::setupCallbacks() {
|
||||
// Mount callback (online mode) - defer heavy model load to next frame
|
||||
gameHandler_.setMountCallback([this](uint32_t mountDisplayId) {
|
||||
if (mountDisplayId == 0) {
|
||||
// Dismount is instant (no loading needed)
|
||||
if (renderer_.getCharacterRenderer() && entitySpawner_.getMountInstanceId() != 0) {
|
||||
renderer_.getCharacterRenderer()->removeInstance(entitySpawner_.getMountInstanceId());
|
||||
entitySpawner_.clearMountState();
|
||||
}
|
||||
entitySpawner_.setMountDisplayId(0);
|
||||
renderer_.clearMount();
|
||||
LOG_INFO("Dismounted");
|
||||
return;
|
||||
}
|
||||
// Queue the mount for processing in the next update() frame
|
||||
entitySpawner_.setMountDisplayId(mountDisplayId);
|
||||
});
|
||||
|
||||
// Taxi precache callback - preload terrain tiles along flight path
|
||||
gameHandler_.setTaxiPrecacheCallback([this](const std::vector<glm::vec3>& path) {
|
||||
if (!renderer_.getTerrainManager()) return;
|
||||
|
||||
std::set<std::pair<int, int>> uniqueTiles;
|
||||
|
||||
// Sample waypoints along path and gather tiles.
|
||||
// Denser sampling + neighbor coverage reduces in-flight stream spikes.
|
||||
const size_t stride = 2;
|
||||
for (size_t i = 0; i < path.size(); i += stride) {
|
||||
const auto& waypoint = path[i];
|
||||
glm::vec3 renderPos = core::coords::canonicalToRender(waypoint);
|
||||
int tileX = static_cast<int>(32 - (renderPos.x / 533.33333f));
|
||||
int tileY = static_cast<int>(32 - (renderPos.y / 533.33333f));
|
||||
|
||||
if (tileX >= 0 && tileX <= 63 && tileY >= 0 && tileY <= 63) {
|
||||
for (int dx = -1; dx <= 1; ++dx) {
|
||||
for (int dy = -1; dy <= 1; ++dy) {
|
||||
int nx = tileX + dx;
|
||||
int ny = tileY + dy;
|
||||
if (nx >= 0 && nx <= 63 && ny >= 0 && ny <= 63) {
|
||||
uniqueTiles.insert({nx, ny});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ensure final destination tile is included.
|
||||
if (!path.empty()) {
|
||||
glm::vec3 renderPos = core::coords::canonicalToRender(path.back());
|
||||
int tileX = static_cast<int>(32 - (renderPos.x / 533.33333f));
|
||||
int tileY = static_cast<int>(32 - (renderPos.y / 533.33333f));
|
||||
if (tileX >= 0 && tileX <= 63 && tileY >= 0 && tileY <= 63) {
|
||||
for (int dx = -1; dx <= 1; ++dx) {
|
||||
for (int dy = -1; dy <= 1; ++dy) {
|
||||
int nx = tileX + dx;
|
||||
int ny = tileY + dy;
|
||||
if (nx >= 0 && nx <= 63 && ny >= 0 && ny <= 63) {
|
||||
uniqueTiles.insert({nx, ny});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, int>> tilesToLoad(uniqueTiles.begin(), uniqueTiles.end());
|
||||
if (tilesToLoad.size() > 512) {
|
||||
tilesToLoad.resize(512);
|
||||
}
|
||||
LOG_INFO("Precaching ", tilesToLoad.size(), " tiles for taxi route");
|
||||
renderer_.getTerrainManager()->precacheTiles(tilesToLoad);
|
||||
});
|
||||
|
||||
// Taxi orientation callback - update mount rotation during flight
|
||||
gameHandler_.setTaxiOrientationCallback([this](float yaw, float pitch, float roll) {
|
||||
if (renderer_.getCameraController()) {
|
||||
// Taxi callback now provides render-space yaw directly.
|
||||
float yawDegrees = glm::degrees(yaw);
|
||||
renderer_.getCameraController()->setFacingYaw(yawDegrees);
|
||||
renderer_.setCharacterYaw(yawDegrees);
|
||||
// Set mount pitch and roll for realistic flight animation
|
||||
renderer_.setMountPitchRoll(pitch, roll);
|
||||
}
|
||||
});
|
||||
|
||||
// Taxi flight start callback - keep non-blocking to avoid hitching at takeoff.
|
||||
gameHandler_.setTaxiFlightStartCallback([this]() {
|
||||
if (renderer_.getTerrainManager() && renderer_.getM2Renderer()) {
|
||||
LOG_INFO("Taxi flight start: incremental terrain/M2 streaming active");
|
||||
uint32_t m2Count = renderer_.getM2Renderer()->getModelCount();
|
||||
uint32_t instCount = renderer_.getM2Renderer()->getInstanceCount();
|
||||
LOG_INFO("Current M2 VRAM state: ", m2Count, " models (", instCount, " instances)");
|
||||
}
|
||||
});
|
||||
|
||||
// Transport spawn callback (online mode) - register transports with TransportManager
|
||||
gameHandler_.setTransportSpawnCallback([this](uint64_t guid, uint32_t entry, uint32_t displayId, float x, float y, float z, float orientation) {
|
||||
// Get the GameObject instance now so late queue processing can rely on stable IDs.
|
||||
auto& goInstances2 = entitySpawner_.getGameObjectInstances();
|
||||
auto it = goInstances2.find(guid);
|
||||
if (it == goInstances2.end()) {
|
||||
LOG_WARNING("Transport spawn callback: GameObject instance not found for GUID 0x", std::hex, guid, std::dec);
|
||||
return;
|
||||
}
|
||||
|
||||
auto pendingIt = entitySpawner_.hasTransportRegistrationPending(guid);
|
||||
if (pendingIt) {
|
||||
entitySpawner_.updateTransportRegistration(guid, displayId, x, y, z, orientation);
|
||||
} else {
|
||||
entitySpawner_.queueTransportRegistration(guid, entry, displayId, x, y, z, orientation);
|
||||
}
|
||||
});
|
||||
|
||||
// Transport move callback (online mode) - update transport gameobject positions
|
||||
gameHandler_.setTransportMoveCallback([this](uint64_t guid, float x, float y, float z, float orientation) {
|
||||
LOG_DEBUG("Transport move callback: GUID=0x", std::hex, guid, std::dec,
|
||||
" pos=(", x, ", ", y, ", ", z, ") orientation=", orientation);
|
||||
|
||||
auto* transportManager = gameHandler_.getTransportManager();
|
||||
if (!transportManager) {
|
||||
LOG_WARNING("Transport move callback: TransportManager is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (entitySpawner_.hasTransportRegistrationPending(guid)) {
|
||||
entitySpawner_.setTransportPendingMove(guid, x, y, z, orientation);
|
||||
LOG_DEBUG("Queued transport move for pending registration GUID=0x", std::hex, guid, std::dec);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if transport exists - if not, treat this as a late spawn (reconnection/server restart)
|
||||
if (!transportManager->getTransport(guid)) {
|
||||
LOG_DEBUG("Received position update for unregistered transport 0x", std::hex, guid, std::dec,
|
||||
" - auto-spawning from position update");
|
||||
|
||||
// Get transport info from entity manager
|
||||
auto entity = gameHandler_.getEntityManager().getEntity(guid);
|
||||
if (entity && entity->getType() == game::ObjectType::GAMEOBJECT) {
|
||||
auto go = std::static_pointer_cast<game::GameObject>(entity);
|
||||
uint32_t entry = go->getEntry();
|
||||
uint32_t displayId = go->getDisplayId();
|
||||
|
||||
// Find the WMO instance for this transport (should exist from earlier GameObject spawn)
|
||||
auto& goInstances3 = entitySpawner_.getGameObjectInstances();
|
||||
auto it = goInstances3.find(guid);
|
||||
if (it != goInstances3.end()) {
|
||||
uint32_t wmoInstanceId = it->second.instanceId;
|
||||
|
||||
// TransportAnimation.dbc is indexed by GameObject entry
|
||||
uint32_t pathId = entry;
|
||||
const bool preferServerData = gameHandler_.hasServerTransportUpdate(guid);
|
||||
|
||||
// Coordinates are already canonical (converted in game_handler.cpp)
|
||||
glm::vec3 canonicalSpawnPos(x, y, z);
|
||||
|
||||
// Check if we have a real usable path, otherwise remap/infer/fall back to stationary.
|
||||
const bool shipOrZeppelinDisplay =
|
||||
(displayId == 3015 || displayId == 3031 || displayId == 7546 ||
|
||||
displayId == 7446 || displayId == 1587 || displayId == 2454 ||
|
||||
displayId == 807 || displayId == 808);
|
||||
bool hasUsablePath = transportManager->hasPathForEntry(entry);
|
||||
if (shipOrZeppelinDisplay) {
|
||||
hasUsablePath = transportManager->hasUsableMovingPathForEntry(entry, 25.0f);
|
||||
}
|
||||
|
||||
if (preferServerData) {
|
||||
// Strict server-authoritative mode: no inferred/remapped fallback routes.
|
||||
if (!hasUsablePath) {
|
||||
std::vector<glm::vec3> path = { canonicalSpawnPos };
|
||||
transportManager->loadPathFromNodes(pathId, path, false, 0.0f);
|
||||
LOG_INFO("Auto-spawned transport in strict server-first mode (stationary fallback): entry=", entry,
|
||||
" displayId=", displayId, " wmoInstance=", wmoInstanceId);
|
||||
} else {
|
||||
LOG_INFO("Auto-spawned transport in server-first mode with entry DBC path: entry=", entry,
|
||||
" displayId=", displayId, " wmoInstance=", wmoInstanceId);
|
||||
}
|
||||
} else if (!hasUsablePath) {
|
||||
bool allowZOnly = (displayId == 455 || displayId == 462);
|
||||
uint32_t inferredPath = transportManager->inferDbcPathForSpawn(
|
||||
canonicalSpawnPos, 1200.0f, allowZOnly);
|
||||
if (inferredPath != 0) {
|
||||
pathId = inferredPath;
|
||||
LOG_INFO("Auto-spawned transport with inferred path: entry=", entry,
|
||||
" inferredPath=", pathId, " displayId=", displayId,
|
||||
" wmoInstance=", wmoInstanceId);
|
||||
} else {
|
||||
uint32_t remappedPath = transportManager->pickFallbackMovingPath(entry, displayId);
|
||||
if (remappedPath != 0) {
|
||||
pathId = remappedPath;
|
||||
LOG_INFO("Auto-spawned transport with remapped fallback path: entry=", entry,
|
||||
" remappedPath=", pathId, " displayId=", displayId,
|
||||
" wmoInstance=", wmoInstanceId);
|
||||
} else {
|
||||
std::vector<glm::vec3> path = { canonicalSpawnPos };
|
||||
transportManager->loadPathFromNodes(pathId, path, false, 0.0f);
|
||||
LOG_INFO("Auto-spawned transport with stationary path: entry=", entry,
|
||||
" displayId=", displayId, " wmoInstance=", wmoInstanceId);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG_INFO("Auto-spawned transport with real path: entry=", entry,
|
||||
" displayId=", displayId, " wmoInstance=", wmoInstanceId);
|
||||
}
|
||||
|
||||
transportManager->registerTransport(guid, wmoInstanceId, pathId, canonicalSpawnPos, entry);
|
||||
// Keep type in sync with the spawned instance; needed for M2 lift boarding/motion.
|
||||
if (!it->second.isWmo) {
|
||||
if (auto* tr = transportManager->getTransport(guid)) {
|
||||
tr->isM2 = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
entitySpawner_.setTransportPendingMove(guid, x, y, z, orientation);
|
||||
LOG_DEBUG("Cannot auto-spawn transport 0x", std::hex, guid, std::dec,
|
||||
" - WMO instance not found yet (queued move for replay)");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
entitySpawner_.setTransportPendingMove(guid, x, y, z, orientation);
|
||||
LOG_DEBUG("Cannot auto-spawn transport 0x", std::hex, guid, std::dec,
|
||||
" - entity not found in EntityManager (queued move for replay)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Update TransportManager's internal state (position, rotation, transform matrices)
|
||||
// This also updates the WMO renderer automatically
|
||||
// Coordinates are already canonical (converted in game_handler.cpp when entity was created)
|
||||
glm::vec3 canonicalPos(x, y, z);
|
||||
transportManager->updateServerTransport(guid, canonicalPos, orientation);
|
||||
|
||||
// Move player with transport if riding it
|
||||
if (gameHandler_.isOnTransport() && gameHandler_.getPlayerTransportGuid() == guid) {
|
||||
auto* cc = renderer_.getCameraController();
|
||||
if (cc) {
|
||||
glm::vec3* ft = cc->getFollowTargetMutable();
|
||||
if (ft) {
|
||||
// Get player world position from TransportManager (handles transform properly)
|
||||
glm::vec3 offset = gameHandler_.getPlayerTransportOffset();
|
||||
glm::vec3 worldPos = transportManager->getPlayerWorldPosition(guid, offset);
|
||||
*ft = worldPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}} // namespace wowee::core
|
||||
Loading…
Add table
Add a link
Reference in a new issue