diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index dc92abfc..d6b4227a 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -937,6 +937,12 @@ public: using AchievementEarnedCallback = std::function; void setAchievementEarnedCallback(AchievementEarnedCallback cb) { achievementEarnedCallback_ = std::move(cb); } + // Server-triggered music callback — fires when SMSG_PLAY_MUSIC is received. + // The soundId corresponds to a SoundEntries.dbc record. The receiver is + // responsible for looking up the file path and forwarding to MusicManager. + using PlayMusicCallback = std::function; + void setPlayMusicCallback(PlayMusicCallback cb) { playMusicCallback_ = std::move(cb); } + // Mount state using MountCallback = std::function; // 0 = dismount void setMountCallback(MountCallback cb) { mountCallback_ = std::move(cb); } @@ -2088,6 +2094,9 @@ private: // ---- Forced faction reactions (SMSG_SET_FORCED_REACTIONS) ---- std::unordered_map forcedReactions_; // factionId -> reaction tier + + // ---- Server-triggered music ---- + PlayMusicCallback playMusicCallback_; }; } // namespace game diff --git a/src/core/application.cpp b/src/core/application.cpp index 2e5d6007..4ff9a73b 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -2096,6 +2096,31 @@ void Application::setupUICallbacks() { } }); + // Server-triggered music callback (SMSG_PLAY_MUSIC) + // Resolves soundId → SoundEntries.dbc → MPQ path → MusicManager. + gameHandler->setPlayMusicCallback([this](uint32_t soundId) { + if (!assetManager || !renderer) return; + auto* music = renderer->getMusicManager(); + if (!music) return; + + auto dbc = assetManager->loadDBC("SoundEntries.dbc"); + if (!dbc || !dbc->isLoaded()) return; + + int32_t idx = dbc->findRecordById(soundId); + if (idx < 0) return; + + // SoundEntries.dbc (WotLK): fields 2-11 = Name[0..9], field 22 = DirectoryBase + const uint32_t row = static_cast(idx); + std::string dir = dbc->getString(row, 22); + for (uint32_t f = 2; f <= 11; ++f) { + std::string name = dbc->getString(row, f); + if (name.empty()) continue; + std::string path = dir.empty() ? name : dir + "\\" + name; + music->playMusic(path, /*loop=*/false); + return; + } + }); + // Other player level-up callback — trigger 3D effect + chat notification gameHandler->setOtherPlayerLevelUpCallback([this](uint64_t guid, uint32_t newLevel) { if (!gameHandler || !renderer) return; diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 214d438b..e94fc381 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -1174,6 +1174,7 @@ void GameHandler::handlePacket(network::Packet& packet) { if (packet.getSize() - packet.getReadPos() == 4) { uint32_t soundId = packet.readUInt32(); LOG_INFO("SMSG_PLAY_MUSIC (0x0103 alias): soundId=", soundId); + if (playMusicCallback_) playMusicCallback_(soundId); return; } } else if (opcode == 0x0480) { @@ -4496,8 +4497,8 @@ void GameHandler::handlePacket(network::Packet& packet) { // ---- Play music (WotLK standard opcode) ---- case Opcode::SMSG_PLAY_MUSIC: { if (packet.getSize() - packet.getReadPos() >= 4) { - /*uint32_t soundId =*/ packet.readUInt32(); - // TODO: hook into music manager when in-world music is reworked + uint32_t soundId = packet.readUInt32(); + if (playMusicCallback_) playMusicCallback_(soundId); } break; }