diff --git a/Data/expansions/classic/opcodes.json b/Data/expansions/classic/opcodes.json index 66804722..e364588e 100644 --- a/Data/expansions/classic/opcodes.json +++ b/Data/expansions/classic/opcodes.json @@ -32,6 +32,7 @@ "SMSG_TUTORIAL_FLAGS": "0x0FD", "SMSG_WARDEN_DATA": "0x2E6", "CMSG_WARDEN_DATA": "0x2E7", + "SMSG_NOTIFICATION": "0x1CB", "SMSG_ACCOUNT_DATA_TIMES": "0x209", "SMSG_UPDATE_OBJECT": "0x0A9", "SMSG_COMPRESSED_UPDATE_OBJECT": "0x1F6", diff --git a/Data/expansions/turtle/opcodes.json b/Data/expansions/turtle/opcodes.json index 09b3f977..608f5c83 100644 --- a/Data/expansions/turtle/opcodes.json +++ b/Data/expansions/turtle/opcodes.json @@ -34,6 +34,7 @@ "SMSG_INITIALIZE_FACTIONS": "0x122", "SMSG_WARDEN_DATA": "0x2E6", "CMSG_WARDEN_DATA": "0x2E7", + "SMSG_NOTIFICATION": "0x1CB", "SMSG_ACCOUNT_DATA_TIMES": "0x209", "SMSG_UPDATE_OBJECT": "0x0A9", "SMSG_COMPRESSED_UPDATE_OBJECT": "0x1F6", diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index 4699f501..ddfe9de3 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -1008,6 +1008,9 @@ private: */ void handleMotd(network::Packet& packet); + /** Handle SMSG_NOTIFICATION (vanilla/classic server notification string) */ + void handleNotification(network::Packet& packet); + /** * Handle SMSG_PONG from server */ diff --git a/include/game/opcode_table.hpp b/include/game/opcode_table.hpp index 0952615f..f2ed140b 100644 --- a/include/game/opcode_table.hpp +++ b/include/game/opcode_table.hpp @@ -60,6 +60,7 @@ enum class LogicalOpcode : uint16_t { SMSG_CLIENTCACHE_VERSION, SMSG_FEATURE_SYSTEM_STATUS, SMSG_MOTD, + SMSG_NOTIFICATION, // ---- Entity/Object updates ---- SMSG_UPDATE_OBJECT, diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index a6e10f3d..105e5b48 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -1119,6 +1119,11 @@ void GameHandler::handlePacket(network::Packet& packet) { handleMotd(packet); break; + case Opcode::SMSG_NOTIFICATION: + // Vanilla/Classic server notification (single string) + handleNotification(packet); + break; + case Opcode::SMSG_PONG: // Can be received at any time after entering world handlePong(packet); @@ -3599,6 +3604,15 @@ void GameHandler::handleMotd(network::Packet& packet) { } } +void GameHandler::handleNotification(network::Packet& packet) { + // SMSG_NOTIFICATION: single null-terminated string + std::string message = packet.readString(); + if (!message.empty()) { + LOG_INFO("Server notification: ", message); + addSystemChatMessage(message); + } +} + void GameHandler::sendPing() { if (state != WorldState::IN_WORLD) { return; diff --git a/src/game/opcode_table.cpp b/src/game/opcode_table.cpp index 6c609c19..e0e680ed 100644 --- a/src/game/opcode_table.cpp +++ b/src/game/opcode_table.cpp @@ -61,6 +61,7 @@ static const OpcodeNameEntry kOpcodeNames[] = { {"SMSG_CLIENTCACHE_VERSION", LogicalOpcode::SMSG_CLIENTCACHE_VERSION}, {"SMSG_FEATURE_SYSTEM_STATUS", LogicalOpcode::SMSG_FEATURE_SYSTEM_STATUS}, {"SMSG_MOTD", LogicalOpcode::SMSG_MOTD}, + {"SMSG_NOTIFICATION", LogicalOpcode::SMSG_NOTIFICATION}, {"SMSG_UPDATE_OBJECT", LogicalOpcode::SMSG_UPDATE_OBJECT}, {"SMSG_COMPRESSED_UPDATE_OBJECT", LogicalOpcode::SMSG_COMPRESSED_UPDATE_OBJECT}, {"SMSG_UNKNOWN_1F5", LogicalOpcode::SMSG_UNKNOWN_1F5},