diff --git a/data/sql/updates/pending_db_auth/rev_1731984288401879600.sql b/data/sql/updates/pending_db_auth/rev_1731984288401879600.sql new file mode 100644 index 000000000..060f144ba --- /dev/null +++ b/data/sql/updates/pending_db_auth/rev_1731984288401879600.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS `motd_localized` ( + `realmid` INT, + `locale` VARCHAR(4) NOT NULL COLLATE 'utf8mb4_unicode_ci', + `text` LONGTEXT NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', + PRIMARY KEY (`realmid`, `locale`) +) +CHARSET = utf8mb4 +COLLATE = utf8mb4_unicode_ci +ENGINE = InnoDB +; diff --git a/data/sql/updates/pending_db_world/rev_1731983948814732800.sql b/data/sql/updates/pending_db_world/rev_1731983948814732800.sql new file mode 100644 index 000000000..18613cc24 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1731983948814732800.sql @@ -0,0 +1,4 @@ +-- +UPDATE `command` SET `help` = 'Syntax: .server set motd Optional($realmId) Optional($locale) $MOTD\r \r Set server Message of the day for the specified $realmId.\r If $realmId is not provided it will update for the current realm. \r Use $realmId -1 to set motd for all realms. If $locale is not provided enUS will be used.' WHERE (`name` = 'server set motd'); + +UPDATE `acore_string` SET `content_default` = 'Message of the day in realm {} and locale {} changed to:\r {}', `locale_deDE` = 'Nachricht des Tages für Realm {} und Sprache {} wurde geändert zu:\r {}', `locale_zhCN` = '每日消息更改为 in realm {} and locale {}:\r {}' WHERE (`entry` = 1101); diff --git a/src/common/Common.cpp b/src/common/Common.cpp index 435d83b39..e25611e06 100644 --- a/src/common/Common.cpp +++ b/src/common/Common.cpp @@ -41,6 +41,16 @@ LocaleConstant GetLocaleByName(const std::string& name) return LOCALE_enUS; // including enGB case } +const std::string GetNameByLocaleConstant(LocaleConstant localeConstant) +{ + if (localeConstant < TOTAL_LOCALES) + { + return localeNames[localeConstant]; + } + + return "enUS"; // Default value for unsupported or invalid LocaleConstant +} + void CleanStringForMysqlQuery(std::string& str) { std::string::size_type n = 0; diff --git a/src/common/Common.h b/src/common/Common.h index 9baec924e..6b7f482db 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -84,6 +84,7 @@ enum LocaleConstant AC_COMMON_API extern char const* localeNames[TOTAL_LOCALES]; AC_COMMON_API LocaleConstant GetLocaleByName(const std::string& name); +AC_COMMON_API const std::string GetNameByLocaleConstant(LocaleConstant localeConstant); AC_COMMON_API void CleanStringForMysqlQuery(std::string& str); #define MAX_QUERY_LEN 32*1024 diff --git a/src/server/apps/worldserver/RemoteAccess/RASession.cpp b/src/server/apps/worldserver/RemoteAccess/RASession.cpp index 0a4703a21..a6926dd5b 100644 --- a/src/server/apps/worldserver/RemoteAccess/RASession.cpp +++ b/src/server/apps/worldserver/RemoteAccess/RASession.cpp @@ -76,7 +76,7 @@ void RASession::Start() LOG_INFO("commands.ra", "User {} (IP: {}) authenticated correctly to RA", username, GetRemoteIpAddress()); // Authentication successful, send the motd - Send(std::string(std::string(sMotdMgr->GetMotd()) + "\r\n").c_str()); + Send(std::string(std::string(sMotdMgr->GetMotd(DEFAULT_LOCALE)) + "\r\n").c_str()); // Read commands for (;;) diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp index 79559524f..51eb7d8ec 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.cpp +++ b/src/server/database/Database/Implementation/LoginDatabase.cpp @@ -116,7 +116,9 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_DEL_ACCOUNT, "DELETE FROM account WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_AUTOBROADCAST, "SELECT id, weight, text FROM autobroadcast WHERE realmid = ? OR realmid = -1", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_MOTD, "SELECT text FROM motd WHERE realmid = ? OR realmid = -1 ORDER BY realmid DESC", CONNECTION_SYNCH); - PrepareStatement(LOGIN_REP_MOTD, "REPLACE INTO motd (realmid, text) VALUES (?, ?)", CONNECTION_ASYNC); + PrepareStatement(LOGIN_SEL_MOTD_LOCALE, "SELECT locale, text FROM motd_localized WHERE realmid = ? OR realmid = -1 ORDER BY realmid DESC", CONNECTION_SYNCH); + PrepareStatement(LOGIN_INS_MOTD, "INSERT INTO motd (realmid, text) VALUES (?, ?) ON DUPLICATE KEY UPDATE text = ?", CONNECTION_ASYNC); + PrepareStatement(LOGIN_INS_MOTD_LOCALE, "INSERT INTO motd_localized (realmid, locale, text) VALUES(?, ?, ?) ON DUPLICATE KEY UPDATE text = ?;", CONNECTION_ASYNC); PrepareStatement(LOGIN_INS_ACCOUNT_MUTE, "INSERT INTO account_muted VALUES (?, UNIX_TIMESTAMP(), ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_ACCOUNT_MUTE_INFO, "SELECT mutedate, mutetime, mutereason, mutedby FROM account_muted WHERE guid = ? ORDER BY mutedate ASC", CONNECTION_SYNCH); PrepareStatement(LOGIN_DEL_ACCOUNT_MUTED, "DELETE FROM account_muted WHERE guid = ?", CONNECTION_ASYNC); diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h index 713dc3b07..f6791ac80 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.h +++ b/src/server/database/Database/Implementation/LoginDatabase.h @@ -98,7 +98,9 @@ enum LoginDatabaseStatements : uint32 LOGIN_DEL_ACCOUNT, LOGIN_SEL_AUTOBROADCAST, LOGIN_SEL_MOTD, - LOGIN_REP_MOTD, + LOGIN_SEL_MOTD_LOCALE, + LOGIN_INS_MOTD, + LOGIN_INS_MOTD_LOCALE, LOGIN_SEL_LAST_ATTEMPT_IP, LOGIN_SEL_LAST_IP, LOGIN_INS_ALDL_IP_LOGGING, diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index b7e809e9c..b5d85760e 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -830,7 +830,7 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder const& holder) // Send MOTD { - SendPacket(sMotdMgr->GetMotdPacket()); + SendPacket(sMotdMgr->GetMotdPacket(pCurrChar->GetSession()->GetSessionDbLocaleIndex())); // send server info if (sWorld->getIntConfig(CONFIG_ENABLE_SINFO_LOGIN) == 1) @@ -1153,7 +1153,7 @@ void WorldSession::HandlePlayerLoginToCharInWorld(Player* pCurrChar) // Send MOTD { - SendPacket(sMotdMgr->GetMotdPacket()); + SendPacket(sMotdMgr->GetMotdPacket(pCurrChar->GetSession()->GetSessionDbLocaleIndex())); // send server info if (sWorld->getIntConfig(CONFIG_ENABLE_SINFO_LOGIN) == 1) diff --git a/src/server/game/Motd/MotdMgr.cpp b/src/server/game/Motd/MotdMgr.cpp index 7b382f3af..eb16810bd 100644 --- a/src/server/game/Motd/MotdMgr.cpp +++ b/src/server/game/Motd/MotdMgr.cpp @@ -27,8 +27,10 @@ namespace { - WorldPacket MotdPacket; - std::string FormattedMotd; + // Stores translated worldpackets + std::unordered_map MotdPackets; + // Stores the localized motd to prevent database queries + std::unordered_map MotdMap; } MotdMgr* MotdMgr::instance() @@ -37,69 +39,143 @@ MotdMgr* MotdMgr::instance() return &instance; } -void MotdMgr::SetMotd(std::string motd) +bool MotdMgr::IsValidLocale(std::string const& locale) { + // Use std::find to search for the locale in the array + return std::find(std::begin(localeNames), std::end(localeNames), locale) != std::end(localeNames); +} + +void MotdMgr::SetMotd(std::string motd, LocaleConstant locale) { // scripts may change motd - sScriptMgr->OnMotdChange(motd); + sScriptMgr->OnMotdChange(motd, locale); - WorldPacket data(SMSG_MOTD); // new in 2.0.1 + MotdMap[locale] = motd; + MotdPackets[locale] = CreateWorldPacket(motd); +} +void MotdMgr::CreateWorldPackages() +{ + for (auto const& [locale, motd] : MotdMap) + // Store the constructed packet in MotdPackets with the locale as the key + MotdPackets[locale] = CreateWorldPacket(motd); +} + +void MotdMgr::LoadMotd() +{ + uint32 realmId = sConfigMgr->GetOption("RealmID", 0); + + // Load the main motd for the realm and assign it to enUS if available + std::string motd = LoadDefaultMotd(realmId); + + // Check if motd was loaded; if not, set default only for enUS + if (motd.empty()) + SetDefaultMotd(); // Only sets enUS default if motd is empty + else + MotdMap[DEFAULT_LOCALE] = motd; // Assign the loaded motd to enUS + + // Load localized texts if available + LoadLocalizedMotds(realmId); + + // Create all world packages after loading motd and localized texts + CreateWorldPackages(); +} + +char const* MotdMgr::GetMotd(LocaleConstant locale) +{ + // Return localized motd if available, otherwise fallback to enUS + auto it = MotdMap.find(locale); + if (it != MotdMap.end()) + return it->second.c_str(); + + return MotdMap[DEFAULT_LOCALE].c_str(); // Fallback to enUS if locale is not found +} + +WorldPacket const* MotdMgr::GetMotdPacket(LocaleConstant locale) +{ + // Return localized packet if available, otherwise fallback to enUS + auto it = MotdPackets.find(locale); + if (it != MotdPackets.end()) + return &it->second; + + return &MotdPackets[DEFAULT_LOCALE]; // Fallback to enUS if locale is not found +} + +std::string MotdMgr::LoadDefaultMotd(uint32 realmId) +{ + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_MOTD); + stmt->SetData(0, realmId); + PreparedQueryResult result = LoginDatabase.Query(stmt); + + if (result) + { + Field* fields = result->Fetch(); + return fields[0].Get(); // Return the main motd if found + } + + return ""; // Return empty string if no motd found +} + +void MotdMgr::SetDefaultMotd() +{ + std::string motd = /* fctlsup << //0x338// "63"+"cx""d2"+"1e""dd"+"cx""ds"+"ce""dd"+"ce""7D"+ << */ + /*"d3"+"ce"*/ std::string("@|") + "cf" +/*"as"+"k4"*/"fF" + "F4" +/*"d5"+"f3"*/"A2" + "DT"/*"F4"+"Az"*/ + "hi" + "s " + /*"fd"+"hy"*/ + "se" + "rv" +/*"nh"+"k3"*/"er" + " r" +/*"x1"+"A2"*/"un" + "s "/*"F2"+"Ay"*/ + "on" + " Az" + /*"xs"+"5n"*/ + "er" + "ot" +/*"xs"+"A2"*/"hC" + "or" +/*"a4"+"f3"*/"e|" + "r "/*"f2"+"A2"*/ + "|c" + "ff" + /*"5g"+"A2"*/ + "3C" + "E7" +/*"k5"+"AX"*/"FF" + "ww" +/*"sx"+"Gj"*/"w." + "az"/*"a1"+"vf"*/ + "er" + "ot" + /*"ds"+"sx"*/ + "hc" + "or" +/*"F4"+"k5"*/"e." + "or" +/*"po"+"xs"*/"g|r"/*"F4"+"p2"+"o4"+"A2"+"i2"*/; + + MotdMap[DEFAULT_LOCALE] = motd; + + // Log that no motd was found and a default is being used for enUS + LOG_WARN("server.loading", ">> Loaded 0 motd definitions. DB table `motd` is empty for this realm!"); + LOG_INFO("server.loading", " "); +} + +void MotdMgr::LoadLocalizedMotds(uint32 realmId) { + // First, check if base MOTD exists + LoginDatabasePreparedStatement* baseStmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_MOTD); + baseStmt->SetData(0, realmId); + PreparedQueryResult baseResult = LoginDatabase.Query(baseStmt); + + if (!baseResult) + { + LOG_ERROR("server.loading", "No base MOTD found for realm %u. Localized MOTDs will not be loaded.", realmId); + return; + } + + // Now load localized versions + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_MOTD_LOCALE); + stmt->SetData(0, realmId); + PreparedQueryResult result = LoginDatabase.Query(stmt); + + if (result) + { + do { + Field* fields = result->Fetch(); + // fields[0] is the locale string and fields[1] is the localized motd text + std::string localizedText = fields[1].Get(); + // Convert locale string to LocaleConstant + LocaleConstant localeId = GetLocaleByName(fields[0].Get()); + + if (localeId == DEFAULT_LOCALE) + continue; + + MotdMap[localeId] = localizedText; + } while (result->NextRow()); + } +} + +WorldPacket MotdMgr::CreateWorldPacket(std::string const& motd) +{ + // Create a new WorldPacket for this locale + WorldPacket data(SMSG_MOTD); // new in 2.0.1 + + // Tokenize the motd string by '@' std::vector motdTokens = Acore::Tokenize(motd, '@', true); data << uint32(motdTokens.size()); // line count for (std::string_view token : motdTokens) data << token; - MotdPacket = data; - - if (!motdTokens.size()) - return; - - std::ostringstream oss; - std::copy(motdTokens.begin(), motdTokens.end() - 1, std::ostream_iterator(oss, "\n")); - oss << *(motdTokens.end() - 1); // copy back element - FormattedMotd = oss.str(); -} - -void MotdMgr::LoadMotd() -{ - uint32 oldMSTime = getMSTime(); - - uint32 realmId = sConfigMgr->GetOption("RealmID", 0); - LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_MOTD); - stmt->SetData(0, realmId); - PreparedQueryResult result = LoginDatabase.Query(stmt); - std::string motd; - - if (result) - { - Field* fields = result->Fetch(); - motd = fields[0].Get(); - } - else - { - LOG_WARN("server.loading", ">> Loaded 0 motd definitions. DB table `motd` is empty for this realm!"); - LOG_INFO("server.loading", " "); - } - - motd = /* fctlsup << //0x338// "63"+"cx""d2"+"1e""dd"+"cx""ds"+"ce""dd"+"ce""7D"+ << */ motd - /*"d3"+"ce"*/ + "@|" + "cf" +/*"as"+"k4"*/"fF" + "F4" +/*"d5"+"f3"*/"A2" + "DT"/*"F4"+"Az"*/ + "hi" + "s " - /*"fd"+"hy"*/ + "se" + "rv" +/*"nh"+"k3"*/"er" + " r" +/*"x1"+"A2"*/"un" + "s "/*"F2"+"Ay"*/ + "on" + " Az" - /*"xs"+"5n"*/ + "er" + "ot" +/*"xs"+"A2"*/"hC" + "or" +/*"a4"+"f3"*/"e|" + "r "/*"f2"+"A2"*/ + "|c" + "ff" - /*"5g"+"A2"*/ + "3C" + "E7" +/*"k5"+"AX"*/"FF" + "ww" +/*"sx"+"Gj"*/"w." + "az"/*"a1"+"vf"*/ + "er" + "ot" - /*"ds"+"sx"*/ + "hc" + "or" +/*"F4"+"k5"*/"e." + "or" +/*"po"+"xs"*/"g|r"/*"F4"+"p2"+"o4"+"A2"+"i2"*/;; - MotdMgr::SetMotd(motd); - - LOG_INFO("server.loading", ">> Loaded Motd Definitions in {} ms", GetMSTimeDiffToNow(oldMSTime)); - LOG_INFO("server.loading", " "); -} - -char const* MotdMgr::GetMotd() -{ - return FormattedMotd.c_str(); -} - -WorldPacket const* MotdMgr::GetMotdPacket() -{ - return &MotdPacket; + return data; } diff --git a/src/server/game/Motd/MotdMgr.h b/src/server/game/Motd/MotdMgr.h index 60ef18ed3..9374ef2f7 100644 --- a/src/server/game/Motd/MotdMgr.h +++ b/src/server/game/Motd/MotdMgr.h @@ -20,6 +20,7 @@ #include "Define.h" #include +#include "Common.h" class WorldPacket; @@ -28,17 +29,33 @@ class AC_GAME_API MotdMgr public: static MotdMgr* instance(); + /// Converts the localized string to world packages + void CreateWorldPackages(); + /// Set a new Message of the Day - void SetMotd(std::string motd); + void SetMotd(std::string motd, LocaleConstant locale); /// Load Message of the Day void LoadMotd(); /// Get the current Message of the Day - char const* GetMotd(); + char const* GetMotd(LocaleConstant locale); - /// Get the motd packet to send at login - WorldPacket const* GetMotdPacket(); + /// Returns the current motd packet for the given locale + WorldPacket const* GetMotdPacket(LocaleConstant locale); + + // Checks if string is valid locale + bool IsValidLocale(std::string const& locale); + +private: + // Loads the default motd from the motd table + std::string LoadDefaultMotd(uint32 realmId); + // Loads all available localized motd for the realm + void LoadLocalizedMotds(uint32 realmId); + // Sets the default mode if none is found in the database + void SetDefaultMotd(); + // Create a worldpacket for a given motd localization + WorldPacket CreateWorldPacket(std::string const& motd); }; #define sMotdMgr MotdMgr::instance() diff --git a/src/server/game/Scripting/ScriptDefines/WorldScript.cpp b/src/server/game/Scripting/ScriptDefines/WorldScript.cpp index ec53f99e3..dc328a126 100644 --- a/src/server/game/Scripting/ScriptDefines/WorldScript.cpp +++ b/src/server/game/Scripting/ScriptDefines/WorldScript.cpp @@ -39,9 +39,9 @@ void ScriptMgr::OnBeforeConfigLoad(bool reload) CALL_ENABLED_HOOKS(WorldScript, WORLDHOOK_ON_BEFORE_CONFIG_LOAD, script->OnBeforeConfigLoad(reload)); } -void ScriptMgr::OnMotdChange(std::string& newMotd) +void ScriptMgr::OnMotdChange(std::string& newMotd, LocaleConstant& locale) { - CALL_ENABLED_HOOKS(WorldScript, WORLDHOOK_ON_MOTD_CHANGE, script->OnMotdChange(newMotd)); + CALL_ENABLED_HOOKS(WorldScript, WORLDHOOK_ON_MOTD_CHANGE, script->OnMotdChange(newMotd, locale)); } void ScriptMgr::OnShutdownInitiate(ShutdownExitCode code, ShutdownMask mask) diff --git a/src/server/game/Scripting/ScriptDefines/WorldScript.h b/src/server/game/Scripting/ScriptDefines/WorldScript.h index c7f715a0a..1c1c0885b 100644 --- a/src/server/game/Scripting/ScriptDefines/WorldScript.h +++ b/src/server/game/Scripting/ScriptDefines/WorldScript.h @@ -18,6 +18,7 @@ #ifndef SCRIPT_OBJECT_WORLD_SCRIPT_H_ #define SCRIPT_OBJECT_WORLD_SCRIPT_H_ +#include "Common.h" #include "ScriptObject.h" #include @@ -58,7 +59,7 @@ public: virtual void OnBeforeConfigLoad(bool /*reload*/) { } // Called before the message of the day is changed. - virtual void OnMotdChange(std::string& /*newMotd*/) { } + virtual void OnMotdChange(std::string& /*newMotd*/, LocaleConstant& /*locale*/) { } // Called when a world shutdown is initiated. virtual void OnShutdownInitiate(ShutdownExitCode /*code*/, ShutdownMask /*mask*/) { } diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 1825319bb..232a5a937 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -166,7 +166,7 @@ public: /* WorldScript */ void OnBeforeConfigLoad(bool reload); void OnAfterConfigLoad(bool reload); void OnBeforeFinalizePlayerWorldSession(uint32& cacheVersion); - void OnMotdChange(std::string& newMotd); + void OnMotdChange(std::string& newMotd, LocaleConstant& locale); void OnShutdownInitiate(ShutdownExitCode code, ShutdownMask mask); void OnShutdownCancel(); void OnWorldUpdate(uint32 diff); diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index 575e30ac8..a8544c218 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -414,7 +414,12 @@ public: LOG_INFO("server.loading", "Reloading Motd..."); sMotdMgr->LoadMotd(); handler->SendGlobalGMSysMessage("DB table `motd` reloaded."); - handler->SendGlobalSysMessage(sMotdMgr->GetMotd()); + LocaleConstant locale = DEFAULT_LOCALE; + + if (Player* player = handler->GetPlayer()) + locale = player->GetSession()->GetSessionDbLocaleIndex(); + + handler->SendGlobalSysMessage(sMotdMgr->GetMotd(locale)); return true; } diff --git a/src/server/scripts/Commands/cs_server.cpp b/src/server/scripts/Commands/cs_server.cpp index 795734d6f..2e5f3ff34 100644 --- a/src/server/scripts/Commands/cs_server.cpp +++ b/src/server/scripts/Commands/cs_server.cpp @@ -24,6 +24,7 @@ #include "Chat.h" #include "CommandScript.h" +#include "Common.h" #include "GameTime.h" #include "GitRevision.h" #include "Log.h" @@ -288,7 +289,11 @@ public: // Display the 'Message of the day' for the realm static bool HandleServerMotdCommand(ChatHandler* handler) { - handler->PSendSysMessage(LANG_MOTD_CURRENT, sMotdMgr->GetMotd()); + LocaleConstant localeConstant = DEFAULT_LOCALE; + if (Player* player = handler->GetPlayer()) + localeConstant = player->GetSession()->GetSessionDbLocaleIndex(); + + handler->PSendSysMessage(LANG_MOTD_CURRENT, sMotdMgr->GetMotd(localeConstant)); return true; } @@ -520,32 +525,84 @@ public: } // Define the 'Message of the day' for the realm - static bool HandleServerSetMotdCommand(ChatHandler* handler, Optional realmId, Tail motd) + static bool HandleServerSetMotdCommand(ChatHandler* handler, Optional realmId, Optional locale, Tail motd) { - std::wstring wMotd = std::wstring(); - std::string strMotd = std::string(); + std::wstring wMotd = std::wstring(); + std::string strMotd = std::string(); + // Default realmId to the current realm if not provided if (!realmId) realmId = static_cast(realm.Id.Realm); if (motd.empty()) return false; - if (!Utf8toWStr(motd, wMotd)) + // Convert Tail (motd) to std::string + std::ostringstream motdStream; + motdStream << motd; + std::string motdString = motdStream.str(); // Convert Tail to std::string + // Determine the locale; default to "enUS" if not provided + LocaleConstant localeConstant = DEFAULT_LOCALE; + if (locale.has_value()) + { + if (sMotdMgr->IsValidLocale(locale.value())) + { + localeConstant = GetLocaleByName(locale.value()); + } + else + { + motdStream.str(""); + motdStream << locale.value() << " " << motd; + motdString = motdStream.str(); + localeConstant = DEFAULT_LOCALE; + locale = GetNameByLocaleConstant(localeConstant); + } + } + else + { + // Set to default locale string + localeConstant = DEFAULT_LOCALE; + locale = GetNameByLocaleConstant(localeConstant); + } + + // Convert the concatenated motdString to UTF-8 and ensure encoding consistency + if (!Utf8toWStr(motdString, wMotd)) return false; if (!WStrToUtf8(wMotd, strMotd)) return false; + // Start a transaction for the database operations LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction(); - LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_REP_MOTD); - stmt->SetData(0, realmId.value()); - stmt->SetData(1, strMotd); - trans->Append(stmt); + + if (localeConstant == DEFAULT_LOCALE) + { + // Insert or update in the main motd table for enUS + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_MOTD); + stmt->SetData(0, realmId.value()); // realmId for insertion + stmt->SetData(1, strMotd); // motd text for insertion + stmt->SetData(2, strMotd); // motd text for ON DUPLICATE KEY UPDATE + trans->Append(stmt); + } + else + { + // Insert or update in the motd_localized table for other locales + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_MOTD_LOCALE); + stmt->SetData(0, realmId.value()); // realmId for insertion + stmt->SetData(1, locale.value()); // locale for insertion + stmt->SetData(2, strMotd); // motd text for insertion + stmt->SetData(3, strMotd); // motd text for ON DUPLICATE KEY UPDATE + trans->Append(stmt); + } + + // Commit the transaction & update db LoginDatabase.CommitTransaction(trans); - sMotdMgr->LoadMotd(); - handler->PSendSysMessage(LANG_MOTD_NEW, realmId.value(), strMotd); + // Update the in-memory maps for the current realm. Otherwise, do not update + if (realmId == -1 || realmId == static_cast(realm.Id.Realm)) + sMotdMgr->SetMotd(strMotd, localeConstant); + + handler->PSendSysMessage(LANG_MOTD_NEW, realmId.value(), locale.value(), strMotd); return true; }