diff --git a/data/sql/updates/pending_db_world/rev_1676164598799512000.sql b/data/sql/updates/pending_db_world/rev_1676164598799512000.sql new file mode 100644 index 000000000..93ad8fb58 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1676164598799512000.sql @@ -0,0 +1,16 @@ +-- +DROP TABLE IF EXISTS `namesreserved_dbc`; +CREATE TABLE `namesreserved_dbc` ( + `ID` INT UNSIGNED NOT NULL, + `Pattern` TINYTEXT NOT NULL, + `LanguagueID` TINYINT NOT NULL, + PRIMARY KEY (`ID`) +); + +DROP TABLE IF EXISTS `namesprofanity_dbc`; +CREATE TABLE `namesprofanity_dbc` ( + `ID` INT UNSIGNED NOT NULL, + `Pattern` TINYTEXT NOT NULL, + `LanguagueID` TINYINT NOT NULL, + PRIMARY KEY (`ID`) +); diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index dc296e4ab..4ef0b35d3 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -626,6 +626,24 @@ RealmZone = 1 World.RealmAvailability = 1 +# +# StrictNames.Reserved +# Description: Use the Reserved Filter from DBC. +# Prevents Player, Pet & Charter names from containing reserved names. +# Default: 1 - Enabled +# 0 - Disabled + +StrictNames.Reserved = 1 + +# +# StrictNames.Profanity +# Description: Use the Profanity Filter from DBC. +# Prevents Player, Pet & Charter names from containing profanity. +# Default: 1 - Enabled +# 0 - Disabled + +StrictNames.Profanity = 1 + # # StrictPlayerNames # Description: Limit player name to language specific symbol set. Prevents character diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index ab07cbb40..400b294f8 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -121,6 +121,9 @@ MapDifficultyMap sMapDifficultyMap; DBCStorage sMovieStore(MovieEntryfmt); +DBCStorage sNamesReservedStore(NamesReservedfmt); +DBCStorage sNamesProfanityStore(NamesProfanityfmt); + DBCStorage sOverrideSpellDataStore(OverrideSpellDatafmt); DBCStorage sPowerDisplayStore(PowerDisplayfmt); @@ -330,6 +333,8 @@ void LoadDBCStores(const std::string& dataPath) LOAD_DBC(sMapStore, "Map.dbc", "map_dbc"); LOAD_DBC(sMapDifficultyStore, "MapDifficulty.dbc", "mapdifficulty_dbc"); LOAD_DBC(sMovieStore, "Movie.dbc", "movie_dbc"); + LOAD_DBC(sNamesReservedStore, "NamesReserved.dbc", "namesreserved_dbc"); + LOAD_DBC(sNamesProfanityStore, "NamesProfanity.dbc", "namesprofanity_dbc"); LOAD_DBC(sOverrideSpellDataStore, "OverrideSpellData.dbc", "overridespelldata_dbc"); LOAD_DBC(sPowerDisplayStore, "PowerDisplay.dbc", "powerdisplay_dbc"); LOAD_DBC(sPvPDifficultyStore, "PvpDifficulty.dbc", "pvpdifficulty_dbc"); diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index c9e70faee..07bf72a63 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -138,6 +138,8 @@ extern DBCStorage sMapStore; //extern DBCStorage sMapDifficultyStore; -- use GetMapDifficultyData insteed extern MapDifficultyMap sMapDifficultyMap; extern DBCStorage sMovieStore; +extern DBCStorage sNamesReservedStore; +extern DBCStorage sNamesProfanityStore; extern DBCStorage sOverrideSpellDataStore; extern DBCStorage sPowerDisplayStore; extern DBCStorage sQuestSortStore; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index e05a041a2..a9fe13080 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -26,6 +26,7 @@ #include "Config.h" #include "Containers.h" #include "DatabaseEnv.h" +#include "DBCStructure.h" #include "DisableMgr.h" #include "GameObjectAIFactory.h" #include "GameEventMgr.h" @@ -53,6 +54,7 @@ #include "World.h" #include "StringConvert.h" #include "Tokenize.h" +#include ScriptMapMap sSpellScripts; ScriptMapMap sEventScripts; @@ -203,6 +205,62 @@ std::string ScriptInfo::GetDebugInfo() const return std::string(sz); } +/** + * @name ReservedNames + * @brief Checks NamesReserved.dbc for reserved names + * + * @param name Name to check for match in NamesReserved.dbc + * @return true/false + */ +bool ReservedNames(std::wstring& name) +{ + for (NamesReservedEntry const* reservedStore : sNamesReservedStore) + { + std::wstring PatternString; + + Utf8toWStr(reservedStore->Pattern, PatternString); + + boost::algorithm::replace_all(PatternString, "\\<", ""); + boost::algorithm::replace_all(PatternString, "\\>", ""); + + int stringCompare = name.compare(PatternString); + if (stringCompare == 0) + { + return true; + } + } + + return false; +}; + +/** + * @name ProfanityNames + * @brief Checks NamesProfanity.dbc for reserved names + * + * @param name Name to check for match in NamesProfanity.dbc + * @return true/false + */ +bool ProfanityNames(std::wstring& name) +{ + for (NamesProfanityEntry const* profanityStore : sNamesProfanityStore) + { + std::wstring PatternString; + + Utf8toWStr(profanityStore->Pattern, PatternString); + + boost::algorithm::replace_all(PatternString, "\\<", ""); + boost::algorithm::replace_all(PatternString, "\\>", ""); + + int stringCompare = name.compare(PatternString); + if (stringCompare == 0) + { + return true; + } + } + + return false; +} + bool normalizePlayerName(std::string& name) { if (name.empty()) @@ -8222,25 +8280,55 @@ bool isValidString(std::wstring wstr, uint32 strictMask, bool numericOrSpace, bo uint8 ObjectMgr::CheckPlayerName(std::string_view name, bool create) { std::wstring wname; + + // Check for invalid characters if (!Utf8toWStr(name, wname)) return CHAR_NAME_INVALID_CHARACTER; + // Check for too long name if (wname.size() > MAX_PLAYER_NAME) return CHAR_NAME_TOO_LONG; + // Check for too short name uint32 minName = sWorld->getIntConfig(CONFIG_MIN_PLAYER_NAME); if (wname.size() < minName) return CHAR_NAME_TOO_SHORT; + // Check for mixed languages uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_PLAYER_NAMES); if (!isValidString(wname, strictMask, false, create)) return CHAR_NAME_MIXED_LANGUAGES; + // Check for three consecutive letters wstrToLower(wname); for (size_t i = 2; i < wname.size(); ++i) if (wname[i] == wname[i - 1] && wname[i] == wname[i - 2]) return CHAR_NAME_THREE_CONSECUTIVE; + // Check Reserved Name from Database + if (sObjectMgr->IsReservedName(name)) + { + return CHAR_NAME_RESERVED; + } + + // Check for Reserved Name from DBC + if (sWorld->getBoolConfig(CONFIG_STRICT_NAMES_RESERVED)) + { + if (ReservedNames(wname)) + { + return CHAR_NAME_RESERVED; + } + } + + // Check for Profanity + if (sWorld->getBoolConfig(CONFIG_STRICT_NAMES_PROFANITY)) + { + if (ProfanityNames(wname)) + { + return CHAR_NAME_PROFANE; + } + } + return CHAR_NAME_SUCCESS; } @@ -8257,6 +8345,24 @@ bool ObjectMgr::IsValidCharterName(std::string_view name) if (wname.size() < minName) return false; + // Check for Reserved Name from DBC + if (sWorld->getBoolConfig(CONFIG_STRICT_NAMES_RESERVED)) + { + if (ReservedNames(wname)) + { + return false; + } + } + + // Check for Profanity + if (sWorld->getBoolConfig(CONFIG_STRICT_NAMES_PROFANITY)) + { + if (ProfanityNames(wname)) + { + return false; + } + } + uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_CHARTER_NAMES); return isValidString(wname, strictMask, true); @@ -8293,6 +8399,24 @@ PetNameInvalidReason ObjectMgr::CheckPetName(std::string_view name) if (!isValidString(wname, strictMask, false)) return PET_NAME_MIXED_LANGUAGES; + // Check for Reserved Name from DBC + if (sWorld->getBoolConfig(CONFIG_STRICT_NAMES_RESERVED)) + { + if (ReservedNames(wname)) + { + return PET_NAME_RESERVED; + } + } + + // Check for Profanity + if (sWorld->getBoolConfig(CONFIG_STRICT_NAMES_PROFANITY)) + { + if (ProfanityNames(wname)) + { + return PET_NAME_PROFANE; + } + } + return PET_NAME_SUCCESS; } diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 0344f6726..28b13f10c 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -691,6 +691,8 @@ SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const* rcEntry); #define MAX_CHARTER_NAME 24 // max allowed by client name length #define MAX_CHANNEL_NAME 50 // pussywizard +bool ReservedNames(std::wstring& name); +bool ProfanityNames(std::wstring& name); bool normalizePlayerName(std::string& name); struct LanguageDesc diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 623a0a517..55fd51650 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -356,12 +356,6 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) return; } - if (AccountMgr::IsPlayerAccount(GetSecurity()) && sObjectMgr->IsReservedName(createInfo->Name)) - { - SendCharCreate(CHAR_NAME_RESERVED); - return; - } - // speedup check for heroic class disabled case uint32 heroic_free_slots = sWorld->getIntConfig(CONFIG_HEROIC_CHARACTERS_PER_REALM); if (heroic_free_slots == 0 && AccountMgr::IsPlayerAccount(GetSecurity()) && createInfo->Class == CLASS_DEATH_KNIGHT) @@ -1351,13 +1345,6 @@ void WorldSession::HandleCharRenameOpcode(WorldPacket& recvData) return; } - // check name limitations - if (AccountMgr::IsPlayerAccount(GetSecurity()) && sObjectMgr->IsReservedName(renameInfo->Name)) - { - SendCharRename(CHAR_NAME_RESERVED, renameInfo.get()); - return; - } - // Ensure that the character belongs to the current account, that rename at login is enabled // and that there is no character with the desired new name CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_FREE_NAME); @@ -1698,13 +1685,6 @@ void WorldSession::HandleCharCustomizeCallback(std::shared_ptrIsReservedName(customizeInfo->Name)) - { - SendCharCustomize(CHAR_NAME_RESERVED, customizeInfo.get()); - return; - } - // character with this name already exist if (ObjectGuid newguid = sCharacterCache->GetCharacterGuidByName(customizeInfo->Name)) { @@ -2083,13 +2063,6 @@ void WorldSession::HandleCharFactionOrRaceChangeCallback(std::shared_ptrIsReservedName(factionChangeInfo->Name)) - { - SendCharFactionChange(CHAR_NAME_RESERVED, factionChangeInfo.get()); - return; - } - // character with this name already exist if (ObjectGuid newguid = sCharacterCache->GetCharacterGuidByName(factionChangeInfo->Name)) { diff --git a/src/server/game/World/IWorld.h b/src/server/game/World/IWorld.h index 38d6a4c0b..cd7e2f369 100644 --- a/src/server/game/World/IWorld.h +++ b/src/server/game/World/IWorld.h @@ -179,6 +179,8 @@ enum WorldBoolConfigs CONFIG_OBJECT_SPARKLES, CONFIG_LOW_LEVEL_REGEN_BOOST, CONFIG_OBJECT_QUEST_MARKERS, + CONFIG_STRICT_NAMES_RESERVED, + CONFIG_STRICT_NAMES_PROFANITY, CONFIG_ALLOWS_RANK_MOD_FOR_PET_HEALTH, BOOL_CONFIG_VALUE_COUNT }; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 234b483de..568cf32a9 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -718,6 +718,8 @@ void World::LoadConfigSettings(bool reload) else _int_configs[CONFIG_REALM_ZONE] = sConfigMgr->GetOption("RealmZone", REALM_ZONE_DEVELOPMENT); + _bool_configs[CONFIG_STRICT_NAMES_RESERVED] = sConfigMgr->GetOption ("StrictNames.Reserved", true); + _bool_configs[CONFIG_STRICT_NAMES_PROFANITY] = sConfigMgr->GetOption ("StrictNames.Profanity", true); _int_configs[CONFIG_STRICT_PLAYER_NAMES] = sConfigMgr->GetOption ("StrictPlayerNames", 0); _int_configs[CONFIG_STRICT_CHARTER_NAMES] = sConfigMgr->GetOption ("StrictCharterNames", 0); _int_configs[CONFIG_STRICT_CHANNEL_NAMES] = sConfigMgr->GetOption ("StrictChannelNames", 0); diff --git a/src/server/shared/DataStores/DBCStructure.h b/src/server/shared/DataStores/DBCStructure.h index 38f0d6405..669a83e19 100644 --- a/src/server/shared/DataStores/DBCStructure.h +++ b/src/server/shared/DataStores/DBCStructure.h @@ -1377,6 +1377,20 @@ struct MovieEntry //uint32 unk2; // 2 always 100 }; +struct NamesReservedEntry +{ + //uint32 ID; // 0 + char const* Pattern; // 1 + //uint32 Language; // 2 +}; + +struct NamesProfanityEntry +{ + //uint32 ID; // 0 + char const* Pattern; // 1 + //uint32 Language; // 2 +}; + #define MAX_OVERRIDE_SPELL 10 struct OverrideSpellDataEntry diff --git a/src/server/shared/DataStores/DBCfmt.h b/src/server/shared/DataStores/DBCfmt.h index abd37d1e3..dc8fefb35 100644 --- a/src/server/shared/DataStores/DBCfmt.h +++ b/src/server/shared/DataStores/DBCfmt.h @@ -84,6 +84,8 @@ char constexpr MailTemplateEntryfmt[] = "nxxxxxxxxxxxxxxxxxssssssssssssssssx"; char constexpr MapEntryfmt[] = "nxiixssssssssssssssssxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixiffxiii"; char constexpr MapDifficultyEntryfmt[] = "diisxxxxxxxxxxxxxxxxiix"; char constexpr MovieEntryfmt[] = "nxx"; +char constexpr NamesReservedfmt[] = "xsx"; +char constexpr NamesProfanityfmt[] = "xsx"; char constexpr OverrideSpellDatafmt[] = "niiiiiiiiiix"; char constexpr PowerDisplayfmt[] = "nixxxx"; char constexpr QuestSortEntryfmt[] = "nxxxxxxxxxxxxxxxxx";