aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2024-10-03 12:22:22 +0200
committerShauren <shauren.trinity@gmail.com>2024-10-03 12:22:22 +0200
commit49ab2bbe3883749594fe92a752e1d618c625d1b6 (patch)
tree1c16172c51fe6ddfffa0835d9288b24eec806130
parent5959e6e374a75d2337c8c8bc1165576c33df3e3b (diff)
Core/Players: Prevent creation of characters with duplicate names
Closes #30299
-rw-r--r--sql/base/characters_database.sql9
-rw-r--r--sql/updates/characters/master/2024_10_03_00_characters.sql29
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.cpp2
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp6
4 files changed, 38 insertions, 8 deletions
diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql
index 5a01bb9d82d..35bcbbd9d1c 100644
--- a/sql/base/characters_database.sql
+++ b/sql/base/characters_database.sql
@@ -1881,7 +1881,7 @@ DROP TABLE IF EXISTS `characters`;
CREATE TABLE `characters` (
`guid` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'Global Unique Identifier',
`account` int unsigned NOT NULL DEFAULT '0' COMMENT 'Account Identifier',
- `name` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
+ `name` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
`slot` tinyint unsigned NOT NULL DEFAULT '0',
`race` tinyint unsigned NOT NULL DEFAULT '0',
`class` tinyint unsigned NOT NULL DEFAULT '0',
@@ -1980,9 +1980,9 @@ CREATE TABLE `characters` (
`personalTabardBorderColor` int NOT NULL DEFAULT '-1',
`personalTabardBackgroundColor` int NOT NULL DEFAULT '-1',
PRIMARY KEY (`guid`),
+ UNIQUE KEY `idx_name` (`name`),
KEY `idx_account` (`account`),
- KEY `idx_online` (`online`),
- KEY `idx_name` (`name`)
+ KEY `idx_online` (`online`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Player System';
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -3748,7 +3748,8 @@ INSERT INTO `updates` VALUES
('2024_08_05_00_characters.sql','7E4AE28F9EC370A1B22DBD8DD718EE027A321F33','ARCHIVED','2024-08-05 11:19:40',0),
('2024_08_26_00_characters.sql','68EEBE1D639D59B24F5121008C2D103CA67FFC9A','ARCHIVED','2024-08-26 00:49:08',0),
('2024_09_03_00_characters.sql','71ECC73A3F324EB64DA19B0CC4DF72A85E022BDC','ARCHIVED','2024-09-03 00:47:42',0),
-('2024_09_23_00_characters.sql','D8491BCEE728F40D55D47E3A4BC5A5F083EBD02E','ARCHIVED','2024-09-23 22:48:10',0);
+('2024_09_23_00_characters.sql','D8491BCEE728F40D55D47E3A4BC5A5F083EBD02E','ARCHIVED','2024-09-23 22:48:10',0),
+('2024_10_03_00_characters.sql','408249A6992999A36EB94089D184972E8E0767A3','RELEASED','2024-10-03 11:10:18',0);
/*!40000 ALTER TABLE `updates` ENABLE KEYS */;
UNLOCK TABLES;
diff --git a/sql/updates/characters/master/2024_10_03_00_characters.sql b/sql/updates/characters/master/2024_10_03_00_characters.sql
new file mode 100644
index 00000000000..7b5a6fcf357
--- /dev/null
+++ b/sql/updates/characters/master/2024_10_03_00_characters.sql
@@ -0,0 +1,29 @@
+-- append guid as hex to characters who have duplicate names in database except the one with lowest guid (assumed to have been created first)
+-- and flag them for rename
+CREATE TEMPORARY TABLE `characters_to_rename` SELECT
+ c2.`guid`
+ FROM
+ (
+ SELECT
+ c1.`name` AS `name`,
+ MIN(c1.`guid`) AS originalGuid
+ FROM
+ `characters` c1
+ WHERE
+ LENGTH(c1.`name`) > 0
+ GROUP BY
+ 1
+ HAVING
+ COUNT(*) > 1
+ ) c3
+ INNER JOIN `characters` c2 ON c3.`name` = c2.`name`
+ WHERE
+ c2.guid <> c3.originalGuid;
+
+UPDATE `characters` SET `name` = CONCAT(SUBSTRING(`name` FROM 1 FOR 12 - LENGTH(CONV(`guid`, 10, 16))), CONV(`guid`, 10, 16)), `at_login` = `at_login` | 1 WHERE `guid` IN (SELECT `guid` FROM `characters_to_rename`);
+
+-- recreate name index with unique constraint
+ALTER TABLE `characters` DROP INDEX `idx_name`;
+ALTER TABLE `characters` MODIFY COLUMN `name` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL AFTER `account`;
+UPDATE `characters` SET `name` = NULL WHERE `name` = '';
+ALTER TABLE `characters` ADD UNIQUE INDEX `idx_name` (`name` ASC);
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp
index 74c255d4066..bed9638de74 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp
@@ -513,7 +513,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_UPD_GROUP_LEGACY_RAID_DIFFICULTY, "UPDATE `groups` SET legacyRaidDifficulty = ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_GROUP_PING_RESTRICTION, "UPDATE `groups` SET pingRestriction = ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_INVALID_SPELL_SPELLS, "DELETE FROM character_spell WHERE spell = ?", CONNECTION_ASYNC);
- PrepareStatement(CHAR_UPD_DELETE_INFO, "UPDATE characters SET deleteInfos_Name = name, deleteInfos_Account = account, deleteDate = UNIX_TIMESTAMP(), name = '', account = 0 WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_DELETE_INFO, "UPDATE characters SET deleteInfos_Name = name, deleteInfos_Account = account, deleteDate = UNIX_TIMESTAMP(), name = NULL, account = 0 WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_RESTORE_DELETE_INFO, "UPDATE characters SET name = ?, account = ?, deleteDate = NULL, deleteInfos_Name = NULL, deleteInfos_Account = NULL WHERE deleteDate IS NOT NULL AND guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_ZONE, "UPDATE characters SET zone = ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_LEVEL, "UPDATE characters SET level = ?, xp = 0 WHERE guid = ?", CONNECTION_ASYNC);
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index ebd3489cf0e..1b22dafff94 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -970,12 +970,12 @@ void WorldSession::HandleCharCreateOpcode(WorldPackets::Character::CreateCharact
stmt->setUInt32(2, sRealmList->GetCurrentRealmId().Realm);
trans->Append(stmt);
- LoginDatabase.CommitTransaction(trans);
-
- AddTransactionCallback(CharacterDatabase.AsyncCommitTransaction(characterTransaction)).AfterComplete([this, newChar = std::move(newChar)](bool success)
+ AddTransactionCallback(CharacterDatabase.AsyncCommitTransaction(characterTransaction)).AfterComplete([this, newChar = std::move(newChar), trans](bool success)
{
if (success)
{
+ LoginDatabase.CommitTransaction(trans);
+
TC_LOG_INFO("entities.player.character", "Account: {} (IP: {}) Create Character: {} {}", GetAccountId(), GetRemoteAddress(), newChar->GetName(), newChar->GetGUID().ToString());
sScriptMgr->OnPlayerCreate(newChar.get());
sCharacterCache->AddCharacterCacheEntry(newChar->GetGUID(), GetAccountId(), newChar->GetName(), newChar->GetNativeGender(), newChar->GetRace(), newChar->GetClass(), newChar->GetLevel(), false);