diff options
26 files changed, 996 insertions, 23 deletions
diff --git a/sql/base/auth_database.sql b/sql/base/auth_database.sql index 490fae0289d..5d596e3d8ca 100644 --- a/sql/base/auth_database.sql +++ b/sql/base/auth_database.sql @@ -361,6 +361,55 @@ LOCK TABLES `battlenet_account_mounts` WRITE; UNLOCK TABLES; -- +-- Table structure for table `battlenet_account_player_data_element` +-- + +DROP TABLE IF EXISTS `battlenet_account_player_data_element`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `battlenet_account_player_data_element` ( + `battlenetAccountId` int unsigned NOT NULL, + `playerDataElementCharacterId` int unsigned NOT NULL, + `floatValue` float DEFAULT NULL, + `int64Value` bigint DEFAULT NULL, + PRIMARY KEY (`battlenetAccountId`,`playerDataElementCharacterId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `battlenet_account_player_data_element` +-- + +LOCK TABLES `battlenet_account_player_data_element` WRITE; +/*!40000 ALTER TABLE `battlenet_account_player_data_element` DISABLE KEYS */; +/*!40000 ALTER TABLE `battlenet_account_player_data_element` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `battlenet_account_player_data_flag` +-- + +DROP TABLE IF EXISTS `battlenet_account_player_data_flag`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `battlenet_account_player_data_flag` ( + `battlenetAccountId` int unsigned NOT NULL, + `storageIndex` int unsigned NOT NULL, + `mask` bigint unsigned NOT NULL, + PRIMARY KEY (`battlenetAccountId`,`storageIndex`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `battlenet_account_player_data_flag` +-- + +LOCK TABLES `battlenet_account_player_data_flag` WRITE; +/*!40000 ALTER TABLE `battlenet_account_player_data_flag` DISABLE KEYS */; +/*!40000 ALTER TABLE `battlenet_account_player_data_flag` ENABLE KEYS */; +UNLOCK TABLES; + +-- -- Table structure for table `battlenet_account_toys` -- @@ -3658,7 +3707,8 @@ INSERT INTO `updates` VALUES ('2025_06_05_02_auth.sql','C2B67F688AC54CF6994F4709D0ECE692C968F346','RELEASED','2025-06-05 16:22:53',0), ('2025_06_18_00_auth.sql','AB5F6069BD37C93050022700F1C4B814D9D139C1','RELEASED','2025-06-17 23:13:05',0), ('2025_06_19_00_auth.sql','1C0ACAEEFC934F91F44C113E6CD9D7A40ED1C979','RELEASED','2025-06-18 22:51:15',0), -('2025_06_25_00_auth.sql','27DC7FB423FFB3788082CCFC18D5432650B09FB3','RELEASED','2025-06-25 01:15:04',0); +('2025_06_25_00_auth.sql','27DC7FB423FFB3788082CCFC18D5432650B09FB3','RELEASED','2025-06-25 01:15:04',0), +('2025_06_27_00_auth.sql','243C89DFED0058323EF9690D124C1F20036D461B','RELEASED','2025-06-27 14:22:49',0); /*!40000 ALTER TABLE `updates` ENABLE KEYS */; UNLOCK TABLES; diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql index c847062f228..28b1021d2c2 100644 --- a/sql/base/characters_database.sql +++ b/sql/base/characters_database.sql @@ -1226,6 +1226,55 @@ LOCK TABLES `character_pet_declinedname` WRITE; UNLOCK TABLES; -- +-- Table structure for table `character_player_data_element` +-- + +DROP TABLE IF EXISTS `character_player_data_element`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `character_player_data_element` ( + `characterGuid` bigint unsigned NOT NULL, + `playerDataElementCharacterId` int unsigned NOT NULL, + `floatValue` float DEFAULT NULL, + `int64Value` bigint DEFAULT NULL, + PRIMARY KEY (`characterGuid`,`playerDataElementCharacterId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `character_player_data_element` +-- + +LOCK TABLES `character_player_data_element` WRITE; +/*!40000 ALTER TABLE `character_player_data_element` DISABLE KEYS */; +/*!40000 ALTER TABLE `character_player_data_element` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `character_player_data_flag` +-- + +DROP TABLE IF EXISTS `character_player_data_flag`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `character_player_data_flag` ( + `characterGuid` bigint unsigned NOT NULL, + `storageIndex` int unsigned NOT NULL, + `mask` bigint unsigned NOT NULL, + PRIMARY KEY (`characterGuid`,`storageIndex`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `character_player_data_flag` +-- + +LOCK TABLES `character_player_data_flag` WRITE; +/*!40000 ALTER TABLE `character_player_data_flag` DISABLE KEYS */; +/*!40000 ALTER TABLE `character_player_data_flag` ENABLE KEYS */; +UNLOCK TABLES; + +-- -- Table structure for table `character_pvp_talent` -- @@ -3781,7 +3830,8 @@ INSERT INTO `updates` VALUES ('2024_12_22_00_characters.sql','A2F24564430C5BCC96C279E843FA3548B1F831EE','ARCHIVED','2024-12-22 02:56:17',0), ('2025_01_04_00_characters.sql','403E8B642A67765A04A0A4D5BC0752288208079C','ARCHIVED','2025-01-04 16:31:39',0), ('2025_03_29_00_characters.sql','6A49C236D0B8CCD8A5B6B51F60E116B3380772D7','ARCHIVED','2025-03-29 01:12:13',0), -('2025_05_31_00_characters.sql','C240EB5C4008B6AA0514802A18D7DD875680DE82','ARCHIVED','2025-05-31 19:45:56',0); +('2025_05_31_00_characters.sql','C240EB5C4008B6AA0514802A18D7DD875680DE82','ARCHIVED','2025-05-31 19:45:56',0), +('2025_06_27_00_characters.sql','35088BA5BA4BD3B7FAAD6FD4FAE38E52A5B71CD8','RELEASED','2025-06-27 14:22:08',0); /*!40000 ALTER TABLE `updates` ENABLE KEYS */; UNLOCK TABLES; diff --git a/sql/updates/auth/master/2025_06_27_00_auth.sql b/sql/updates/auth/master/2025_06_27_00_auth.sql new file mode 100644 index 00000000000..d7ace8563f1 --- /dev/null +++ b/sql/updates/auth/master/2025_06_27_00_auth.sql @@ -0,0 +1,22 @@ +-- +-- Table structure for table `battlenet_account_player_data_element` +-- +DROP TABLE IF EXISTS `battlenet_account_player_data_element`; +CREATE TABLE `battlenet_account_player_data_element` ( + `battlenetAccountId` int unsigned NOT NULL, + `playerDataElementAccountId` int unsigned NOT NULL, + `floatValue` float DEFAULT NULL, + `int64Value` bigint DEFAULT NULL, + PRIMARY KEY (`battlenetAccountId`,`playerDataElementAccountId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `battlenet_account_player_data_flag` +-- +DROP TABLE IF EXISTS `battlenet_account_player_data_flag`; +CREATE TABLE `battlenet_account_player_data_flag` ( + `battlenetAccountId` int unsigned NOT NULL, + `storageIndex` int unsigned NOT NULL, + `mask` bigint unsigned NOT NULL, + PRIMARY KEY (`battlenetAccountId`,`storageIndex`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/sql/updates/characters/master/2025_06_27_00_characters.sql b/sql/updates/characters/master/2025_06_27_00_characters.sql new file mode 100644 index 00000000000..4cdc504d89c --- /dev/null +++ b/sql/updates/characters/master/2025_06_27_00_characters.sql @@ -0,0 +1,22 @@ +-- +-- Table structure for table `character_player_data_element` +-- +DROP TABLE IF EXISTS `character_player_data_element`; +CREATE TABLE `character_player_data_element` ( + `characterGuid` bigint unsigned NOT NULL, + `playerDataElementCharacterId` int unsigned NOT NULL, + `floatValue` float DEFAULT NULL, + `int64Value` bigint DEFAULT NULL, + PRIMARY KEY (`characterGuid`,`playerDataElementCharacterId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `character_player_data_flag` +-- +DROP TABLE IF EXISTS `character_player_data_flag`; +CREATE TABLE `character_player_data_flag` ( + `characterGuid` bigint unsigned NOT NULL, + `storageIndex` int unsigned NOT NULL, + `mask` bigint unsigned NOT NULL, + PRIMARY KEY (`characterGuid`,`storageIndex`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/sql/updates/hotfixes/master/2025_06_27_00_hotfixes.sql b/sql/updates/hotfixes/master/2025_06_27_00_hotfixes.sql new file mode 100644 index 00000000000..debe7a8a4ea --- /dev/null +++ b/sql/updates/hotfixes/master/2025_06_27_00_hotfixes.sql @@ -0,0 +1,47 @@ +-- +-- Table structure for table `player_data_element_account` +-- +DROP TABLE IF EXISTS `player_data_element_account`; +CREATE TABLE `player_data_element_account` ( + `ID` int unsigned NOT NULL DEFAULT '0', + `StorageIndex` int NOT NULL DEFAULT '0', + `Type` int NOT NULL DEFAULT '0', + `VerifiedBuild` int NOT NULL DEFAULT '0', + PRIMARY KEY (`ID`,`VerifiedBuild`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `player_data_element_character` +-- +DROP TABLE IF EXISTS `player_data_element_character`; +CREATE TABLE `player_data_element_character` ( + `ID` int unsigned NOT NULL DEFAULT '0', + `StorageIndex` int NOT NULL DEFAULT '0', + `Type` int NOT NULL DEFAULT '0', + `VerifiedBuild` int NOT NULL DEFAULT '0', + PRIMARY KEY (`ID`,`VerifiedBuild`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `player_data_flag_account` +-- +DROP TABLE IF EXISTS `player_data_flag_account`; +CREATE TABLE `player_data_flag_account` ( + `ID` int unsigned NOT NULL DEFAULT '0', + `StorageIndex` int NOT NULL DEFAULT '0', + `Unknown1107` int NOT NULL DEFAULT '0', + `VerifiedBuild` int NOT NULL DEFAULT '0', + PRIMARY KEY (`ID`,`VerifiedBuild`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `player_data_flag_character` +-- +DROP TABLE IF EXISTS `player_data_flag_character`; +CREATE TABLE `player_data_flag_character` ( + `ID` int unsigned NOT NULL DEFAULT '0', + `StorageIndex` int NOT NULL DEFAULT '0', + `Unknown1107` int NOT NULL DEFAULT '0', + `VerifiedBuild` int NOT NULL DEFAULT '0', + PRIMARY KEY (`ID`,`VerifiedBuild`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index e54d96518a0..037155d610a 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -796,6 +796,13 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_UPD_CHARACTER_INSTANCE_LOCK_FORCE_EXPIRE, "UPDATE character_instance_lock SET expiryTime = ?, extended = 0 WHERE guid = ? AND mapId = ? AND lockId = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_INSTANCE, "DELETE FROM instance WHERE instanceId = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_INSTANCE, "INSERT INTO instance (instanceId, data, completedEncountersMask, entranceWorldSafeLocId) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); + + PrepareStatement(CHAR_SEL_PLAYER_DATA_ELEMENTS_CHARACTER, "SELECT playerDataElementCharacterId, floatValue, int64Value FROM character_player_data_element WHERE characterGuid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_PLAYER_DATA_ELEMENTS_CHARACTER, "DELETE FROM character_player_data_element WHERE characterGuid = ? AND playerDataElementCharacterId = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_PLAYER_DATA_ELEMENTS_CHARACTER, "INSERT INTO character_player_data_element (characterGuid, playerDataElementCharacterId, floatValue, int64Value) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_PLAYER_DATA_FLAGS_CHARACTER, "SELECT storageIndex, mask FROM character_player_data_flag WHERE characterGuid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_PLAYER_DATA_FLAGS_CHARACTER, "DELETE FROM character_player_data_flag WHERE characterGuid = ? AND storageIndex = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_PLAYER_DATA_FLAGS_CHARACTER, "INSERT INTO character_player_data_flag (characterGuid, storageIndex, mask) VALUES (?, ?, ?)", CONNECTION_ASYNC); } CharacterDatabaseConnection::CharacterDatabaseConnection(MySQLConnectionInfo& connInfo, ConnectionFlags connectionFlags) : MySQLConnection(connInfo, connectionFlags) diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index 7a3e6b24467..98c80c96746 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -650,6 +650,13 @@ enum CharacterDatabaseStatements : uint32 CHAR_DEL_INSTANCE, CHAR_INS_INSTANCE, + CHAR_SEL_PLAYER_DATA_ELEMENTS_CHARACTER, + CHAR_DEL_PLAYER_DATA_ELEMENTS_CHARACTER, + CHAR_INS_PLAYER_DATA_ELEMENTS_CHARACTER, + CHAR_SEL_PLAYER_DATA_FLAGS_CHARACTER, + CHAR_DEL_PLAYER_DATA_FLAGS_CHARACTER, + CHAR_INS_PLAYER_DATA_FLAGS_CHARACTER, + MAX_CHARACTERDATABASE_STATEMENTS }; diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp index 7e92324ec00..308d83da6b9 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp @@ -1320,6 +1320,26 @@ void HotfixDatabaseConnection::DoPrepareStatements() PREPARE_LOCALE_STMT(HOTFIX_SEL_PLAYER_CONDITION, "SELECT ID, FailureDescription_lang FROM player_condition_locale WHERE (`VerifiedBuild` > 0) = ?" " AND locale = ?", CONNECTION_SYNCH); + // PlayerDataElementAccount.db2 + PrepareStatement(HOTFIX_SEL_PLAYER_DATA_ELEMENT_ACCOUNT, "SELECT ID, StorageIndex, Type FROM player_data_element_account" + " WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); + PREPARE_MAX_ID_STMT(HOTFIX_SEL_PLAYER_DATA_ELEMENT_ACCOUNT, "SELECT MAX(ID) + 1 FROM player_data_element_account", CONNECTION_SYNCH); + + // PlayerDataElementCharacter.db2 + PrepareStatement(HOTFIX_SEL_PLAYER_DATA_ELEMENT_CHARACTER, "SELECT ID, StorageIndex, Type FROM player_data_element_character" + " WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); + PREPARE_MAX_ID_STMT(HOTFIX_SEL_PLAYER_DATA_ELEMENT_CHARACTER, "SELECT MAX(ID) + 1 FROM player_data_element_character", CONNECTION_SYNCH); + + // PlayerDataFlagAccount.db2 + PrepareStatement(HOTFIX_SEL_PLAYER_DATA_FLAG_ACCOUNT, "SELECT ID, StorageIndex, Unknown1107 FROM player_data_flag_account" + " WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); + PREPARE_MAX_ID_STMT(HOTFIX_SEL_PLAYER_DATA_FLAG_ACCOUNT, "SELECT MAX(ID) + 1 FROM player_data_flag_account", CONNECTION_SYNCH); + + // PlayerDataFlagCharacter.db2 + PrepareStatement(HOTFIX_SEL_PLAYER_DATA_FLAG_CHARACTER, "SELECT ID, StorageIndex, Unknown1107 FROM player_data_flag_character" + " WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); + PREPARE_MAX_ID_STMT(HOTFIX_SEL_PLAYER_DATA_FLAG_CHARACTER, "SELECT MAX(ID) + 1 FROM player_data_flag_character", CONNECTION_SYNCH); + // PowerDisplay.db2 PrepareStatement(HOTFIX_SEL_POWER_DISPLAY, "SELECT ID, GlobalStringBaseTag, ActualType, Red, Green, Blue FROM power_display" " WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h index 989e8512a41..3748de79cad 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.h +++ b/src/server/database/Database/Implementation/HotfixDatabase.h @@ -756,6 +756,18 @@ enum HotfixDatabaseStatements : uint32 HOTFIX_SEL_PLAYER_CONDITION_MAX_ID, HOTFIX_SEL_PLAYER_CONDITION_LOCALE, + HOTFIX_SEL_PLAYER_DATA_ELEMENT_ACCOUNT, + HOTFIX_SEL_PLAYER_DATA_ELEMENT_ACCOUNT_MAX_ID, + + HOTFIX_SEL_PLAYER_DATA_ELEMENT_CHARACTER, + HOTFIX_SEL_PLAYER_DATA_ELEMENT_CHARACTER_MAX_ID, + + HOTFIX_SEL_PLAYER_DATA_FLAG_ACCOUNT, + HOTFIX_SEL_PLAYER_DATA_FLAG_ACCOUNT_MAX_ID, + + HOTFIX_SEL_PLAYER_DATA_FLAG_CHARACTER, + HOTFIX_SEL_PLAYER_DATA_FLAG_CHARACTER_MAX_ID, + HOTFIX_SEL_POWER_DISPLAY, HOTFIX_SEL_POWER_DISPLAY_MAX_ID, diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp index 440fa2b998b..5ed330a6c48 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.cpp +++ b/src/server/database/Database/Implementation/LoginDatabase.cpp @@ -196,6 +196,13 @@ void LoginDatabaseConnection::DoPrepareStatements() "ON DUPLICATE KEY UPDATE isFavorite = VALUES(isFavorite)", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_BNET_WARBAND_SCENE, "UPDATE battlenet_account_warband_scenes SET isFavorite = ?, hasFanfare = ? WHERE battlenetAccountId = ? AND warbandSceneId = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_DEL_BNET_WARBAND_SCENE, "DELETE FROM battlenet_account_warband_scenes WHERE battlenetAccountId = ? AND warbandSceneId = ?", CONNECTION_ASYNC); + + PrepareStatement(LOGIN_SEL_BNET_PLAYER_DATA_ELEMENTS_ACCOUNT, "SELECT playerDataElementAccountId, floatValue, int64Value FROM battlenet_account_player_data_element WHERE battlenetAccountId = ?", CONNECTION_ASYNC); + PrepareStatement(LOGIN_DEL_BNET_PLAYER_DATA_ELEMENTS_ACCOUNT, "DELETE FROM battlenet_account_player_data_element WHERE battlenetAccountId = ? AND playerDataElementAccountId = ?", CONNECTION_ASYNC); + PrepareStatement(LOGIN_INS_BNET_PLAYER_DATA_ELEMENTS_ACCOUNT, "INSERT INTO battlenet_account_player_data_element (battlenetAccountId, playerDataElementAccountId, floatValue, int64Value) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(LOGIN_SEL_BNET_PLAYER_DATA_FLAGS_ACCOUNT, "SELECT storageIndex, mask FROM battlenet_account_player_data_flag WHERE battlenetAccountId = ?", CONNECTION_ASYNC); + PrepareStatement(LOGIN_DEL_BNET_PLAYER_DATA_FLAGS_ACCOUNT, "DELETE FROM battlenet_account_player_data_flag WHERE battlenetAccountId = ? AND storageIndex = ?", CONNECTION_ASYNC); + PrepareStatement(LOGIN_INS_BNET_PLAYER_DATA_FLAGS_ACCOUNT, "INSERT INTO battlenet_account_player_data_flag (battlenetAccountId, storageIndex, mask) VALUES (?, ?, ?)", CONNECTION_ASYNC); } LoginDatabaseConnection::LoginDatabaseConnection(MySQLConnectionInfo& connInfo, ConnectionFlags connectionFlags) : MySQLConnection(connInfo, connectionFlags) diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h index 7b70d063cb3..811931fd75a 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.h +++ b/src/server/database/Database/Implementation/LoginDatabase.h @@ -181,6 +181,13 @@ enum LoginDatabaseStatements : uint32 LOGIN_UPD_BNET_WARBAND_SCENE, LOGIN_DEL_BNET_WARBAND_SCENE, + LOGIN_SEL_BNET_PLAYER_DATA_ELEMENTS_ACCOUNT, + LOGIN_DEL_BNET_PLAYER_DATA_ELEMENTS_ACCOUNT, + LOGIN_INS_BNET_PLAYER_DATA_ELEMENTS_ACCOUNT, + LOGIN_SEL_BNET_PLAYER_DATA_FLAGS_ACCOUNT, + LOGIN_DEL_BNET_PLAYER_DATA_FLAGS_ACCOUNT, + LOGIN_INS_BNET_PLAYER_DATA_FLAGS_ACCOUNT, + MAX_LOGINDATABASE_STATEMENTS }; diff --git a/src/server/game/Achievements/CriteriaHandler.cpp b/src/server/game/Achievements/CriteriaHandler.cpp index c5406e0796f..c4896440d0f 100644 --- a/src/server/game/Achievements/CriteriaHandler.cpp +++ b/src/server/game/Achievements/CriteriaHandler.cpp @@ -3988,6 +3988,14 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 if (referencePlayer->GetPositionZ() >= reqValue) return false; break; + case ModifierTreeType::PlayerDataFlagAccountIsSet: // 378 + if (!referencePlayer->HasDataFlagAccount(reqValue)) + return false; + break; + case ModifierTreeType::PlayerDataFlagCharacterIsSet: // 379 + if (!referencePlayer->HasDataFlagCharacter(reqValue)) + return false; + break; case ModifierTreeType::PlayerIsOnMapWithExpansion: // 380 { MapEntry const* mapEntry = referencePlayer->GetMap()->GetEntry(); @@ -4022,6 +4030,12 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 return false; break; } + case ModifierTreeType::PlayerDataElementCharacterBetween: // 390 + return std::visit([&]<typename T>(T value) { return value >= T(secondaryAsset) && value <= T(tertiaryAsset); }, + referencePlayer->GetDataElementCharacter(reqValue)); + case ModifierTreeType::PlayerDataElementAccountBetween: // 391 + return std::visit([&]<typename T>(T value) { return value >= T(secondaryAsset) && value <= T(tertiaryAsset); }, + referencePlayer->GetDataElementAccount(reqValue)); case ModifierTreeType::PlayerHasCompletedQuestOrIsReadyToTurnIn: // 392 { QuestStatus status = referencePlayer->GetQuestStatus(reqValue); diff --git a/src/server/game/DataStores/DB2LoadInfo.h b/src/server/game/DataStores/DB2LoadInfo.h index f14f8a85f97..926c5095f0a 100644 --- a/src/server/game/DataStores/DB2LoadInfo.h +++ b/src/server/game/DataStores/DB2LoadInfo.h @@ -4363,6 +4363,54 @@ struct PlayerConditionLoadInfo static constexpr DB2LoadInfo Instance{ Fields, 162, &PlayerConditionMeta::Instance, HOTFIX_SEL_PLAYER_CONDITION }; }; +struct PlayerDataElementAccountLoadInfo +{ + static constexpr DB2FieldMeta Fields[3] = + { + { .IsSigned = false, .Type = FT_INT, .Name = "ID" }, + { .IsSigned = true, .Type = FT_INT, .Name = "StorageIndex" }, + { .IsSigned = true, .Type = FT_INT, .Name = "Type" }, + }; + + static constexpr DB2LoadInfo Instance{ Fields, 3, &PlayerDataElementAccountMeta::Instance, HOTFIX_SEL_PLAYER_DATA_ELEMENT_ACCOUNT }; +}; + +struct PlayerDataElementCharacterLoadInfo +{ + static constexpr DB2FieldMeta Fields[3] = + { + { .IsSigned = false, .Type = FT_INT, .Name = "ID" }, + { .IsSigned = true, .Type = FT_INT, .Name = "StorageIndex" }, + { .IsSigned = true, .Type = FT_INT, .Name = "Type" }, + }; + + static constexpr DB2LoadInfo Instance{ Fields, 3, &PlayerDataElementCharacterMeta::Instance, HOTFIX_SEL_PLAYER_DATA_ELEMENT_CHARACTER }; +}; + +struct PlayerDataFlagAccountLoadInfo +{ + static constexpr DB2FieldMeta Fields[3] = + { + { .IsSigned = false, .Type = FT_INT, .Name = "ID" }, + { .IsSigned = true, .Type = FT_INT, .Name = "StorageIndex" }, + { .IsSigned = true, .Type = FT_INT, .Name = "Unknown1107" }, + }; + + static constexpr DB2LoadInfo Instance{ Fields, 3, &PlayerDataFlagAccountMeta::Instance, HOTFIX_SEL_PLAYER_DATA_FLAG_ACCOUNT }; +}; + +struct PlayerDataFlagCharacterLoadInfo +{ + static constexpr DB2FieldMeta Fields[3] = + { + { .IsSigned = false, .Type = FT_INT, .Name = "ID" }, + { .IsSigned = true, .Type = FT_INT, .Name = "StorageIndex" }, + { .IsSigned = true, .Type = FT_INT, .Name = "Unknown1107" }, + }; + + static constexpr DB2LoadInfo Instance{ Fields, 3, &PlayerDataFlagCharacterMeta::Instance, HOTFIX_SEL_PLAYER_DATA_FLAG_CHARACTER }; +}; + struct PowerDisplayLoadInfo { static constexpr DB2FieldMeta Fields[6] = diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index 7195afe57c7..521fbcfb8cc 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -258,6 +258,10 @@ DB2Storage<PerksActivityEntry> sPerksActivityStore("PerksActivi DB2Storage<PhaseEntry> sPhaseStore("Phase.db2", &PhaseLoadInfo::Instance); DB2Storage<PhaseXPhaseGroupEntry> sPhaseXPhaseGroupStore("PhaseXPhaseGroup.db2", &PhaseXPhaseGroupLoadInfo::Instance); DB2Storage<PlayerConditionEntry> sPlayerConditionStore("PlayerCondition.db2", &PlayerConditionLoadInfo::Instance); +DB2Storage<PlayerDataElementAccountEntry> sPlayerDataElementAccountStore("PlayerDataElementAccount.db2", &PlayerDataElementAccountLoadInfo::Instance); +DB2Storage<PlayerDataElementCharacterEntry> sPlayerDataElementCharacterStore("PlayerDataElementCharacter.db2", &PlayerDataElementCharacterLoadInfo::Instance); +DB2Storage<PlayerDataFlagAccountEntry> sPlayerDataFlagAccountStore("PlayerDataFlagAccount.db2", &PlayerDataFlagAccountLoadInfo::Instance); +DB2Storage<PlayerDataFlagCharacterEntry> sPlayerDataFlagCharacterStore("PlayerDataFlagCharacter.db2", &PlayerDataFlagCharacterLoadInfo::Instance); DB2Storage<PowerDisplayEntry> sPowerDisplayStore("PowerDisplay.db2", &PowerDisplayLoadInfo::Instance); DB2Storage<PowerTypeEntry> sPowerTypeStore("PowerType.db2", &PowerTypeLoadInfo::Instance); DB2Storage<PrestigeLevelInfoEntry> sPrestigeLevelInfoStore("PrestigeLevelInfo.db2", &PrestigeLevelInfoLoadInfo::Instance); @@ -880,6 +884,10 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul LOAD_DB2(sPhaseStore); LOAD_DB2(sPhaseXPhaseGroupStore); LOAD_DB2(sPlayerConditionStore); + LOAD_DB2(sPlayerDataElementAccountStore); + LOAD_DB2(sPlayerDataElementCharacterStore); + LOAD_DB2(sPlayerDataFlagAccountStore); + LOAD_DB2(sPlayerDataFlagCharacterStore); LOAD_DB2(sPowerDisplayStore); LOAD_DB2(sPowerTypeStore); LOAD_DB2(sPrestigeLevelInfoStore); diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index 80c077822e1..b90d247c83a 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -199,6 +199,10 @@ TC_GAME_API extern DB2Storage<ParagonReputationEntry> sParagonRepu TC_GAME_API extern DB2Storage<PerksActivityEntry> sPerksActivityStore; TC_GAME_API extern DB2Storage<PhaseEntry> sPhaseStore; TC_GAME_API extern DB2Storage<PlayerConditionEntry> sPlayerConditionStore; +TC_GAME_API extern DB2Storage<PlayerDataElementAccountEntry> sPlayerDataElementAccountStore; +TC_GAME_API extern DB2Storage<PlayerDataElementCharacterEntry> sPlayerDataElementCharacterStore; +TC_GAME_API extern DB2Storage<PlayerDataFlagAccountEntry> sPlayerDataFlagAccountStore; +TC_GAME_API extern DB2Storage<PlayerDataFlagCharacterEntry> sPlayerDataFlagCharacterStore; TC_GAME_API extern DB2Storage<PowerDisplayEntry> sPowerDisplayStore; TC_GAME_API extern DB2Storage<PowerTypeEntry> sPowerTypeStore; TC_GAME_API extern DB2Storage<PVPStatEntry> sPVPStatStore; diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index bb9a5e55c43..3fe06f40106 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -3242,6 +3242,38 @@ struct PlayerConditionEntry std::array<uint16, 4> TraitNodeEntryMaxRank; }; +struct PlayerDataElementAccountEntry +{ + uint32 ID; + int32 StorageIndex; + int32 Type; + + PlayerDataElementType GetType() const { return static_cast<PlayerDataElementType>(Type); } +}; + +struct PlayerDataElementCharacterEntry +{ + uint32 ID; + int32 StorageIndex; + int32 Type; + + PlayerDataElementType GetType() const { return static_cast<PlayerDataElementType>(Type); } +}; + +struct PlayerDataFlagAccountEntry +{ + uint32 ID; + int32 StorageIndex; + int32 Unknown1107; +}; + +struct PlayerDataFlagCharacterEntry +{ + uint32 ID; + int32 StorageIndex; + int32 Unknown1107; +}; + struct PowerDisplayEntry { uint32 ID; diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index 951ca42004e..518f6ed81df 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -1829,8 +1829,8 @@ enum class ModifierTreeType : int32 PlayerWeaponHighWatermarkAboveOrEqual = 375, /*NYI*/ PlayerHeadHighWatermarkAboveOrEqual = 376, /*NYI*/ PlayerHasDisplayedCurrencyLessThan = 377, /*NYI*/ // Player has {CurrencyTypes} less than {#Amount} (value visible in ui is taken into account, not raw value) - PlayerDataFlagAccountIsSet = 378, /*NYI*/ // Player {PlayerDataFlagAccount} is set - PlayerDataFlagCharacterIsSet = 379, /*NYI*/ // Player {PlayerDataFlagCharacter} is set + PlayerDataFlagAccountIsSet = 378, // Player {PlayerDataFlagAccount} is set + PlayerDataFlagCharacterIsSet = 379, // Player {PlayerDataFlagCharacter} is set PlayerIsOnMapWithExpansion = 380, // Player is on map that has {ExpansionID} PlayerHasCompletedQuestOnAccount = 382, /*NYI*/ // Player has previously completed quest "{QuestV2}" on account @@ -1841,12 +1841,16 @@ enum class ModifierTreeType : int32 PlayerIsInSoloRBG = 387, /*NYI*/ // Player is in solo RBG (BG Blitz) PlayerHasCompletedCampaign = 388, /*NYI*/ // Player has completed campaign "{Campaign}" TargetCreatureClassificationEqual = 389, // Creature classification is {CreatureClassification} - PlayerDataElementCharacterEqual = 390, /*NYI*/ // Player {PlayerDataElementCharacter} is greater than {#Amount} - PlayerDataElementAccountEqual = 391, /*NYI*/ // Player {PlayerDataElementAccount} is greater than {#Amount} + PlayerDataElementCharacterBetween = 390, // Player {PlayerDataElementCharacter} is between {#Amount} and {#Amount2} + PlayerDataElementAccountBetween = 391, // Player {PlayerDataElementAccount} is between {#Amount} and {#Amount2} PlayerHasCompletedQuestOrIsReadyToTurnIn = 392, // Player has previously completed quest "{QuestV2}" or is ready to turn it in PlayerTitle = 393, // Player is currently using "{ChrTitles}" title + PlayerWeeklyCurrencyIsRelOpFromMax = 397, /*NYI*/ // Player weekly {CurrencyTypes} is {RelOp} {#Amount} from currency weekly limit + PlayerIsInGuild = 404, // Player is in a guild + + PlayerAvgItemLevelRelOp = 415, /*NYI*/ // Player average item level {AvgItemLevelCategory} is {RelOp} {#Amount} }; enum class ModifierTreeOperator : int8 @@ -1942,6 +1946,12 @@ enum class PlayerConditionLfgStatus : uint8 GearDiff = 8 }; +enum class PlayerDataElementType : int32 +{ + Int64 = 0, + Float = 1 +}; + enum class PlayerInteractionType : int32 { None = 0, diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 6b971d69a8c..f05a69123f1 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -105,6 +105,7 @@ #include "PlayerChoice.h" #include "QueryCallback.h" #include "QueryHolder.h" +#include "QueryResultStructured.h" #include "QuestDef.h" #include "QuestObjectiveCriteriaMgr.h" #include "QuestPackets.h" @@ -18618,6 +18619,8 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol _LoadCUFProfiles(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_CUF_PROFILES)); + _LoadPlayerData(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_DATA_ELEMENTS), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_DATA_FLAGS)); + std::unique_ptr<Garrison> garrison = std::make_unique<Garrison>(this); if (garrison->LoadFromDB(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GARRISON), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GARRISON_BLUEPRINTS), @@ -20165,6 +20168,87 @@ bool Player::_LoadHomeBind(PreparedQueryResult result) return true; } +void Player::_LoadPlayerData(PreparedQueryResult elementsResult, PreparedQueryResult flagsResult) +{ + if (elementsResult) + { + do + { + DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(PreparedResultSet, (playerDataElementCharacterId)(floatValue)(int64Value)) fields { *elementsResult }; + + PlayerDataElementCharacterEntry const* entry = sPlayerDataElementCharacterStore.LookupEntry(fields.playerDataElementCharacterId().GetUInt32()); + if (!entry) + continue; + + auto elementSetter = m_values.ModifyValue(&Player::m_activePlayerData) + .ModifyValue(&UF::ActivePlayerData::CharacterDataElements, entry->StorageIndex); + + SetUpdateFieldValue(elementSetter.ModifyValue(&UF::PlayerDataElement::Type), entry->Type); + + switch (entry->GetType()) + { + case PlayerDataElementType::Int64: + SetUpdateFieldValue(elementSetter.ModifyValue(&UF::PlayerDataElement::Int64Value), fields.int64Value().GetInt64()); + break; + case PlayerDataElementType::Float: + SetUpdateFieldValue(elementSetter.ModifyValue(&UF::PlayerDataElement::FloatValue), fields.floatValue().GetFloat()); + break; + default: + break; + } + } while (elementsResult->NextRow()); + } + + if (flagsResult) + { + auto bitVectorSetter = m_values.ModifyValue(&Player::m_activePlayerData) + .ModifyValue(&UF::ActivePlayerData::BitVectors) + .ModifyValue(&UF::BitVectors::Values, PLAYER_DATA_FLAG_CHARACTER_DATA_INDEX); + + do + { + DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(PreparedResultSet, (storageIndex)(mask)) fields { *flagsResult }; + + SetUpdateFieldValue(bitVectorSetter.ModifyValue(&UF::BitVector::Values, fields.storageIndex().GetUInt32()), fields.mask().GetUInt64()); + } while (flagsResult->NextRow()); + } + + WorldSession::PlayerDataAccount const& accountData = GetSession()->GetPlayerDataAccount(); + for (WorldSession::PlayerDataAccount::Element const& element : accountData.Elements) + { + PlayerDataElementAccountEntry const* entry = sPlayerDataElementAccountStore.LookupEntry(element.Id); + if (!entry) + continue; + + auto elementSetter = m_values.ModifyValue(&Player::m_activePlayerData) + .ModifyValue(&UF::ActivePlayerData::AccountDataElements, entry->StorageIndex); + + SetUpdateFieldValue(elementSetter.ModifyValue(&UF::PlayerDataElement::Type), entry->Type); + + switch (entry->GetType()) + { + case PlayerDataElementType::Int64: + SetUpdateFieldValue(elementSetter.ModifyValue(&UF::PlayerDataElement::Int64Value), element.Int64Value); + break; + case PlayerDataElementType::Float: + SetUpdateFieldValue(elementSetter.ModifyValue(&UF::PlayerDataElement::FloatValue), element.FloatValue); + break; + default: + break; + } + } + + if (!accountData.Flags.empty()) + { + auto bitVectorSetter = m_values.ModifyValue(&Player::m_activePlayerData) + .ModifyValue(&UF::ActivePlayerData::BitVectors) + .ModifyValue(&UF::BitVectors::Values, PLAYER_DATA_FLAG_ACCOUNT_DATA_INDEX); + + for (std::size_t i = 0; i < accountData.Flags.size(); ++i) + SetUpdateFieldValue(bitVectorSetter.ModifyValue(&UF::BitVector::Values, i), accountData.Flags[i].Value); + } +} + /*********************************************************/ /*** SAVE SYSTEM ***/ /*********************************************************/ @@ -20563,6 +20647,7 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba _SaveInstanceTimeRestrictions(trans); _SaveCurrency(trans); _SaveCUFProfiles(trans); + _SavePlayerData(trans); if (_garrison) _garrison->SaveToDB(trans); @@ -20579,6 +20664,7 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba GetSession()->GetCollectionMgr()->SaveAccountItemAppearances(loginTransaction); GetSession()->GetCollectionMgr()->SaveAccountTransmogIllusions(loginTransaction); GetSession()->GetCollectionMgr()->SaveAccountWarbandScenes(loginTransaction); + GetSession()->SavePlayerDataAccount(loginTransaction); Battlenet::RealmHandle currentRealmId = sRealmList->GetCurrentRealmId(); @@ -21465,6 +21551,70 @@ void Player::_SaveStats(CharacterDatabaseTransaction trans) const trans->Append(stmt); } +void Player::_SavePlayerData(CharacterDatabaseTransaction trans) +{ + ObjectGuid::LowType guid = GetGUID().GetCounter(); + CharacterDatabasePreparedStatement* stmt; + for (uint32 playerDataElementCharacterId : _playerDataElementsNeedSave) + { + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_DATA_ELEMENTS_CHARACTER); + stmt->setUInt64(0, guid); + stmt->setUInt32(1, playerDataElementCharacterId); + trans->Append(stmt); + + PlayerDataElementCharacterEntry const* entry = sPlayerDataElementCharacterStore.LookupEntry(playerDataElementCharacterId); + if (!entry) + continue; + + UF::PlayerDataElement const& element = m_activePlayerData->CharacterDataElements[entry->StorageIndex]; + switch (entry->GetType()) + { + case PlayerDataElementType::Int64: + if (!element.Int64Value) + continue; + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PLAYER_DATA_ELEMENTS_CHARACTER); + stmt->setUInt64(0, guid); + stmt->setUInt32(1, playerDataElementCharacterId); + stmt->setNull(2); + stmt->setInt64(3, element.Int64Value); + trans->Append(stmt); + break; + case PlayerDataElementType::Float: + if (!element.FloatValue) + continue; + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PLAYER_DATA_ELEMENTS_CHARACTER); + stmt->setUInt64(0, guid); + stmt->setUInt32(1, playerDataElementCharacterId); + stmt->setFloat(2, element.FloatValue); + stmt->setNull(3); + trans->Append(stmt); + break; + } + + } + + for (uint32 storageIndex : _playerDataFlagsNeedSave) + { + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_DATA_FLAGS_CHARACTER); + stmt->setUInt64(0, guid); + stmt->setUInt32(1, storageIndex); + trans->Append(stmt); + + uint64 value = m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_CHARACTER_DATA_INDEX].Values[storageIndex]; + if (!value) + continue; + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PLAYER_DATA_FLAGS_CHARACTER); + stmt->setUInt64(0, guid); + stmt->setUInt32(1, storageIndex); + stmt->setUInt64(2, value); + trans->Append(stmt); + } + + _playerDataElementsNeedSave.clear(); + _playerDataFlagsNeedSave.clear(); +} + void Player::outDebugValues() const { if (!sLog->ShouldLog("entities.unit", LOG_LEVEL_DEBUG)) @@ -29906,6 +30056,182 @@ bool Player::MeetPlayerCondition(uint32 conditionId) const return ConditionMgr::IsPlayerMeetingCondition(this, conditionId); } +std::variant<int64, float> Player::GetDataElementAccount(uint32 dataElementId) const +{ + PlayerDataElementAccountEntry const* entry = sPlayerDataElementAccountStore.LookupEntry(dataElementId); + if (!entry) + return int64(0); + + switch (entry->GetType()) + { + case PlayerDataElementType::Int64: + if (entry->StorageIndex < std::ssize(m_activePlayerData->AccountDataElements)) + return m_activePlayerData->AccountDataElements[entry->StorageIndex].Int64Value; + return SI64LIT(0); + case PlayerDataElementType::Float: + if (entry->StorageIndex < std::ssize(m_activePlayerData->AccountDataElements)) + return m_activePlayerData->AccountDataElements[entry->StorageIndex].FloatValue; + return 0.0f; + default: + break; + } + + return int64(0); +} + +void Player::SetDataElementAccount(uint32 dataElementId, std::variant<int64, float> value) +{ + PlayerDataElementAccountEntry const* entry = sPlayerDataElementAccountStore.LookupEntry(dataElementId); + if (!entry) + return; + + auto elementSetter = m_values.ModifyValue(&Player::m_activePlayerData) + .ModifyValue(&UF::ActivePlayerData::AccountDataElements, entry->StorageIndex); + SetUpdateFieldValue(elementSetter.ModifyValue(&UF::PlayerDataElement::Type), entry->Type); + + switch (entry->GetType()) + { + case PlayerDataElementType::Int64: + { + int64 int64Value = std::visit([](auto v) { return int64(v); }, value); + SetUpdateFieldValue(elementSetter.ModifyValue(&UF::PlayerDataElement::Int64Value), int64Value); + GetSession()->SetPlayerDataElementAccount(dataElementId, int64Value); + break; + } + case PlayerDataElementType::Float: + { + float floatValue = std::visit([](auto v) { return float(v); }, value); + SetUpdateFieldValue(elementSetter.ModifyValue(&UF::PlayerDataElement::FloatValue), floatValue); + GetSession()->SetPlayerDataElementAccount(dataElementId, floatValue); + break; + } + default: + break; + } +} + +std::variant<int64, float> Player::GetDataElementCharacter(uint32 dataElementId) const +{ + PlayerDataElementCharacterEntry const* entry = sPlayerDataElementCharacterStore.LookupEntry(dataElementId); + if (!entry) + return int64(0); + + switch (entry->Type) + { + case 0: + if (entry->StorageIndex < std::ssize(m_activePlayerData->CharacterDataElements)) + return m_activePlayerData->CharacterDataElements[entry->StorageIndex].Int64Value; + return SI64LIT(0); + case 1: + if (entry->StorageIndex < std::ssize(m_activePlayerData->CharacterDataElements)) + return m_activePlayerData->CharacterDataElements[entry->StorageIndex].FloatValue; + return 0.0f; + default: + break; + } + + return int64(0); +} + +void Player::SetDataElementCharacter(uint32 dataElementId, std::variant<int64, float> value) +{ + PlayerDataElementCharacterEntry const* entry = sPlayerDataElementCharacterStore.LookupEntry(dataElementId); + if (!entry) + return; + + auto elementSetter = m_values.ModifyValue(&Player::m_activePlayerData) + .ModifyValue(&UF::ActivePlayerData::CharacterDataElements, entry->StorageIndex); + SetUpdateFieldValue(elementSetter.ModifyValue(&UF::PlayerDataElement::Type), entry->Type); + + switch (entry->Type) + { + case 0: + SetUpdateFieldValue(elementSetter.ModifyValue(&UF::PlayerDataElement::Int64Value), std::visit([](auto v) { return int64(v); }, value)); + break; + case 1: + SetUpdateFieldValue(elementSetter.ModifyValue(&UF::PlayerDataElement::FloatValue), std::visit([](auto v) { return float(v); }, value)); + break; + default: + break; + } + + _playerDataElementsNeedSave.insert(dataElementId); +} + +bool Player::HasDataFlagAccount(uint32 dataFlagId) const +{ + PlayerDataFlagAccountEntry const* entry = sPlayerDataFlagAccountStore.LookupEntry(dataFlagId); + if (!entry) + return false; + + uint32 fieldOffset = entry->StorageIndex / PLAYER_DATA_FLAG_VALUE_BITS; + uint64 flag = UI64LIT(1) << (entry->StorageIndex % PLAYER_DATA_FLAG_VALUE_BITS); + + if (fieldOffset >= m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_ACCOUNT_DATA_INDEX].Values.size()) + return false; + + return (m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_ACCOUNT_DATA_INDEX].Values[fieldOffset] & flag) != 0; +} + +void Player::SetDataFlagAccount(uint32 dataFlagId, bool on) +{ + PlayerDataFlagAccountEntry const* entry = sPlayerDataFlagAccountStore.LookupEntry(dataFlagId); + if (!entry) + return; + + uint32 fieldOffset = entry->StorageIndex / PLAYER_DATA_FLAG_VALUE_BITS; + uint64 flag = UI64LIT(1) << (entry->StorageIndex % PLAYER_DATA_FLAG_VALUE_BITS); + + auto fieldSetter = m_values.ModifyValue(&Player::m_activePlayerData) + .ModifyValue(&UF::ActivePlayerData::BitVectors) + .ModifyValue(&UF::BitVectors::Values, PLAYER_DATA_FLAG_ACCOUNT_DATA_INDEX) + .ModifyValue(&UF::BitVector::Values, fieldOffset); + + if (on) + SetUpdateFieldFlagValue(fieldSetter, flag); + else + RemoveUpdateFieldFlagValue(fieldSetter, flag); + + GetSession()->SetPlayerDataFlagAccount(dataFlagId, on); +} + +bool Player::HasDataFlagCharacter(uint32 dataFlagId) const +{ + PlayerDataFlagCharacterEntry const* entry = sPlayerDataFlagCharacterStore.LookupEntry(dataFlagId); + if (!entry) + return false; + + uint32 fieldOffset = entry->StorageIndex / PLAYER_DATA_FLAG_VALUE_BITS; + uint64 flag = UI64LIT(1) << (entry->StorageIndex % PLAYER_DATA_FLAG_VALUE_BITS); + + if (fieldOffset >= m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_CHARACTER_DATA_INDEX].Values.size()) + return false; + + return (m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_CHARACTER_DATA_INDEX].Values[fieldOffset] & flag) != 0; +} + +void Player::SetDataFlagCharacter(uint32 dataFlagId, bool on) +{ + PlayerDataFlagCharacterEntry const* entry = sPlayerDataFlagCharacterStore.LookupEntry(dataFlagId); + if (!entry) + return; + + uint32 fieldOffset = entry->StorageIndex / PLAYER_DATA_FLAG_VALUE_BITS; + uint64 flag = UI64LIT(1) << (entry->StorageIndex % PLAYER_DATA_FLAG_VALUE_BITS); + + auto fieldSetter = m_values.ModifyValue(&Player::m_activePlayerData) + .ModifyValue(&UF::ActivePlayerData::BitVectors) + .ModifyValue(&UF::BitVectors::Values, PLAYER_DATA_FLAG_CHARACTER_DATA_INDEX) + .ModifyValue(&UF::BitVector::Values, fieldOffset); + + if (on) + SetUpdateFieldFlagValue(fieldSetter, flag); + else + RemoveUpdateFieldFlagValue(fieldSetter, flag); + + _playerDataFlagsNeedSave.insert(fieldOffset); +} + bool Player::IsInFriendlyArea() const { if (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(GetAreaId())) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 77c1e44f7dc..2d21399464d 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -33,6 +33,7 @@ #include "PlayerTaxi.h" #include "QuestDef.h" #include "SceneMgr.h" +#include <variant> struct AccessRequirement; struct AchievementEntry; @@ -147,7 +148,8 @@ enum PlayerSkillsConstants enum PlayerDataFlagConstants { - PLAYER_EXPLORED_ZONES_BITS = UF::size_of_value_type<decltype(UF::BitVector::Values)>() * 8, + PLAYER_DATA_FLAG_VALUE_BITS = UF::size_of_value_type<decltype(UF::BitVector::Values)>() * 8, + PLAYER_EXPLORED_ZONES_BITS = PLAYER_DATA_FLAG_VALUE_BITS, PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX = 1, PLAYER_DATA_FLAG_CHARACTER_DATA_INDEX = 2, @@ -949,6 +951,8 @@ enum PlayerLoginQueryIndex PLAYER_LOGIN_QUERY_LOAD_GARRISON_FOLLOWER_ABILITIES, PLAYER_LOGIN_QUERY_LOAD_TRAIT_ENTRIES, PLAYER_LOGIN_QUERY_LOAD_TRAIT_CONFIGS, + PLAYER_LOGIN_QUERY_LOAD_DATA_ELEMENTS, + PLAYER_LOGIN_QUERY_LOAD_DATA_FLAGS, MAX_PLAYER_LOGIN_QUERY }; @@ -2962,6 +2966,18 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player> void SetRequiredMountCapabilityFlag(uint8 flag) { SetUpdateFieldFlagValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::RequiredMountCapabilityFlags), flag); } void ReplaceAllRequiredMountCapabilityFlags(uint8 flags) { SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::RequiredMountCapabilityFlags), flags); } + std::variant<int64, float> GetDataElementAccount(uint32 dataElementId) const; + void SetDataElementAccount(uint32 dataElementId, std::variant<int64, float> value); + + std::variant<int64, float> GetDataElementCharacter(uint32 dataElementId) const; + void SetDataElementCharacter(uint32 dataElementId, std::variant<int64, float> value); + + bool HasDataFlagAccount(uint32 dataFlagId) const; + void SetDataFlagAccount(uint32 dataFlagId, bool on); + + bool HasDataFlagCharacter(uint32 dataFlagId) const; + void SetDataFlagCharacter(uint32 dataFlagId, bool on); + bool IsInFriendlyArea() const; bool IsFriendlyArea(AreaTableEntry const* inArea) const; @@ -3072,6 +3088,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player> void _LoadPetStable(uint32 summonedPetNumber, PreparedQueryResult result); void _LoadCurrency(PreparedQueryResult result); void _LoadCUFProfiles(PreparedQueryResult result); + void _LoadPlayerData(PreparedQueryResult elementsResult, PreparedQueryResult flagsResult); /*********************************************************/ /*** SAVE SYSTEM ***/ @@ -3100,6 +3117,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player> void _SaveInstanceTimeRestrictions(CharacterDatabaseTransaction trans); void _SaveCurrency(CharacterDatabaseTransaction trans); void _SaveCUFProfiles(CharacterDatabaseTransaction trans); + void _SavePlayerData(CharacterDatabaseTransaction trans); /*********************************************************/ /*** ENVIRONMENTAL SYSTEM ***/ @@ -3349,6 +3367,9 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player> void ExecutePendingSpellCastRequest(); bool ProcessItemCast(SpellCastRequest& castRequest, SpellCastTargets const& targets); bool CanExecutePendingSpellCastRequest(); + + Trinity::Containers::FlatSet<uint32> _playerDataElementsNeedSave; + Trinity::Containers::FlatSet<uint32> _playerDataFlagsNeedSave; }; TC_GAME_API void AddItemsSetItem(Player* player, Item const* item); diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index d107c300117..116390128d2 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -345,6 +345,14 @@ bool LoginQueryHolder::Initialize() stmt->setUInt64(0, lowGuid); res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_TRAIT_CONFIGS, stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PLAYER_DATA_ELEMENTS_CHARACTER); + stmt->setUInt64(0, lowGuid); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_DATA_ELEMENTS, stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PLAYER_DATA_FLAGS_CHARACTER); + stmt->setUInt64(0, lowGuid); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_DATA_FLAGS, stmt); + return res; } diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 3582aeec67a..8afe51bbdf9 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -1584,7 +1584,7 @@ enum SpellEffectName SPELL_EFFECT_CRAFT_ITEM = 288, // MiscValue[0] = CraftingDataID SPELL_EFFECT_MODIFY_AURA_STACKS = 289, // MiscValue[0] = 0 means add, = 1 means set SPELL_EFFECT_MODIFY_COOLDOWN = 290, - SPELL_EFFECT_MODIFY_COOLDOWNS = 291, // MiscValue[0] = SpellFamily, MiscValue[1] = maybe bit index for family flags? off by 1 for the only spell using this effect + SPELL_EFFECT_MODIFY_COOLDOWNS = 291, // MiscValue[0] = SpellFamily, MiscValue[1] = bit index for family flags SPELL_EFFECT_MODIFY_COOLDOWNS_BY_CATEGORY = 292, // MiscValue[0] = category SPELL_EFFECT_MODIFY_CHARGES = 293, // MiscValue[0] = charge category SPELL_EFFECT_CRAFT_LOOT = 294, // MiscValue[0] = CraftingDataID @@ -1628,10 +1628,10 @@ enum SpellEffectName SPELL_EFFECT_332 = 332, SPELL_EFFECT_333 = 333, SPELL_EFFECT_334 = 334, - SPELL_EFFECT_335 = 335, - SPELL_EFFECT_336 = 336, - SPELL_EFFECT_337 = 337, - SPELL_EFFECT_338 = 338, + SPELL_EFFECT_SET_PLAYER_DATA_ELEMENT_ACCOUNT = 335, // MiscValue[0] = PlayerDataElementAccount + SPELL_EFFECT_SET_PLAYER_DATA_ELEMENT_CHARACTER = 336, // MiscValue[0] = PlayerDataElementCharacter + SPELL_EFFECT_SET_PLAYER_DATA_FLAG_ACCOUNT = 337, // MiscValue[0] = PlayerDataFlagAccount + SPELL_EFFECT_SET_PLAYER_DATA_FLAG_CHARACTER = 338, // MiscValue[0] = PlayerDataFlagCharacter SPELL_EFFECT_UI_ACTION = 339, SPELL_EFFECT_340 = 340, SPELL_EFFECT_LEARN_WARBAND_SCENE = 341, diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 9c22c48a95d..88a6cc1d815 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -28,7 +28,9 @@ #include "CharacterPackets.h" #include "ChatPackets.h" #include "ClientConfigPackets.h" +#include "Containers.h" #include "DatabaseEnv.h" +#include "DB2Stores.h" #include "GameTime.h" #include "Group.h" #include "Guild.h" @@ -44,6 +46,7 @@ #include "PacketUtilities.h" #include "Player.h" #include "QueryHolder.h" +#include "QueryResultStructured.h" #include "Random.h" #include "RBAC.h" #include "RealmList.h" @@ -972,6 +975,159 @@ void WorldSession::SaveTutorialsData(CharacterDatabaseTransaction trans) _tutorialsChanged &= ~TUTORIALS_FLAG_CHANGED; } +void WorldSession::LoadPlayerDataAccount(PreparedQueryResult const& elementsResult, PreparedQueryResult const& flagsResult) +{ + if (elementsResult) + { + do + { + DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(PreparedResultSet, (playerDataElementAccountId)(floatValue)(int64Value)) fields { *elementsResult }; + + PlayerDataElementAccountEntry const* entry = sPlayerDataElementAccountStore.LookupEntry(fields.playerDataElementAccountId().GetUInt32()); + if (!entry) + continue; + + PlayerDataAccount::Element& element = _playerDataAccount.Elements.emplace_back(); + element.Id = entry->ID; + element.NeedSave = false; + + switch (entry->GetType()) + { + case PlayerDataElementType::Int64: + element.Int64Value = fields.int64Value().GetInt64(); + break; + case PlayerDataElementType::Float: + element.FloatValue = fields.floatValue().GetFloat(); + break; + default: + break; + } + } while (elementsResult->NextRow()); + } + + if (flagsResult) + { + do + { + DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(PreparedResultSet, (storageIndex)(mask)) fields { *flagsResult }; + + Trinity::Containers::EnsureWritableVectorIndex(_playerDataAccount.Flags, fields.storageIndex().GetUInt32()) = { .Value = fields.mask().GetUInt64(), .NeedSave = false }; + } while (flagsResult->NextRow()); + } +} + +void WorldSession::SavePlayerDataAccount(LoginDatabaseTransaction const& transaction) +{ + LoginDatabasePreparedStatement* stmt; + for (PlayerDataAccount::Element& element : _playerDataAccount.Elements) + { + if (!element.NeedSave) + continue; + + stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_BNET_PLAYER_DATA_ELEMENTS_ACCOUNT); + stmt->setUInt32(0, GetBattlenetAccountId()); + stmt->setUInt32(1, element.Id); + transaction->Append(stmt); + + element.NeedSave = false; + + PlayerDataElementAccountEntry const* entry = sPlayerDataElementAccountStore.LookupEntry(element.Id); + if (!entry) + continue; + + switch (entry->GetType()) + { + case PlayerDataElementType::Int64: + if (!element.Int64Value) + continue; + stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BNET_PLAYER_DATA_ELEMENTS_ACCOUNT); + stmt->setUInt32(0, GetBattlenetAccountId()); + stmt->setUInt32(1, element.Id); + stmt->setNull(2); + stmt->setInt64(3, element.Int64Value); + transaction->Append(stmt); + break; + case PlayerDataElementType::Float: + if (!element.FloatValue) + continue; + stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BNET_PLAYER_DATA_ELEMENTS_ACCOUNT); + stmt->setUInt32(0, GetBattlenetAccountId()); + stmt->setUInt32(1, element.Id); + stmt->setFloat(2, element.FloatValue); + stmt->setNull(3); + transaction->Append(stmt); + break; + } + } + + for (std::size_t i = 0; i < _playerDataAccount.Flags.size(); ++i) + { + PlayerDataAccount::Flag& flag = _playerDataAccount.Flags[i]; + if (!flag.NeedSave) + continue; + + stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_BNET_PLAYER_DATA_FLAGS_ACCOUNT); + stmt->setUInt32(0, GetBattlenetAccountId()); + stmt->setUInt32(1, i); + transaction->Append(stmt); + + flag.NeedSave = false; + + if (!flag.Value) + continue; + + stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BNET_PLAYER_DATA_FLAGS_ACCOUNT); + stmt->setUInt32(0, GetBattlenetAccountId()); + stmt->setUInt32(1, i); + stmt->setUInt64(2, flag.Value); + transaction->Append(stmt); + } +} + +void WorldSession::SetPlayerDataElementAccount(uint32 dataElementId, float value) +{ + auto elementItr = std::ranges::find(_playerDataAccount.Elements, dataElementId, &PlayerDataAccount::Element::Id); + if (elementItr == _playerDataAccount.Elements.end()) + { + elementItr = _playerDataAccount.Elements.emplace(_playerDataAccount.Elements.end()); + elementItr->Id = dataElementId; + } + + elementItr->NeedSave = true; + elementItr->FloatValue = value; +} + +void WorldSession::SetPlayerDataElementAccount(uint32 dataElementId, int64 value) +{ + auto elementItr = std::ranges::find(_playerDataAccount.Elements, dataElementId, &PlayerDataAccount::Element::Id); + if (elementItr == _playerDataAccount.Elements.end()) + { + elementItr = _playerDataAccount.Elements.emplace(_playerDataAccount.Elements.end()); + elementItr->Id = dataElementId; + } + + elementItr->NeedSave = true; + elementItr->Int64Value = value; +} + +void WorldSession::SetPlayerDataFlagAccount(uint32 dataFlagId, bool on) +{ + PlayerDataFlagAccountEntry const* entry = sPlayerDataFlagAccountStore.LookupEntry(dataFlagId); + if (!entry) + return; + + uint32 fieldOffset = entry->StorageIndex / PLAYER_DATA_FLAG_VALUE_BITS; + uint64 flagValue = UI64LIT(1) << (entry->StorageIndex % PLAYER_DATA_FLAG_VALUE_BITS); + + PlayerDataAccount::Flag& flag = Trinity::Containers::EnsureWritableVectorIndex(_playerDataAccount.Flags, fieldOffset); + if (on) + flag.Value |= flagValue; + else + flag.Value &= ~flagValue; + + flag.NeedSave = true; +} + bool WorldSession::IsAddonRegistered(std::string_view prefix) const { if (!_filterAddonMessages) // if we have hit the softcap (64) nothing should be filtered @@ -1118,6 +1274,8 @@ public: ITEM_FAVORITE_APPEARANCES, TRANSMOG_ILLUSIONS, WARBAND_SCENES, + PLAYER_DATA_ELEMENTS_ACCOUNT, + PLAYER_DATA_FLAGS_ACCOUNT, MAX_QUERIES }; @@ -1169,6 +1327,14 @@ public: stmt->setUInt32(0, battlenetAccountId); ok = SetPreparedQuery(WARBAND_SCENES, stmt) && ok; + stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_PLAYER_DATA_ELEMENTS_ACCOUNT); + stmt->setUInt32(0, battlenetAccountId); + ok = SetPreparedQuery(PLAYER_DATA_ELEMENTS_ACCOUNT, stmt) && ok; + + stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_PLAYER_DATA_FLAGS_ACCOUNT); + stmt->setUInt32(0, battlenetAccountId); + ok = SetPreparedQuery(PLAYER_DATA_FLAGS_ACCOUNT, stmt) && ok; + return ok; } }; @@ -1222,6 +1388,7 @@ void WorldSession::InitializeSessionCallback(LoginDatabaseQueryHolder const& hol _collectionMgr->LoadAccountItemAppearances(holder.GetPreparedResult(AccountInfoQueryHolder::ITEM_APPEARANCES), holder.GetPreparedResult(AccountInfoQueryHolder::ITEM_FAVORITE_APPEARANCES)); _collectionMgr->LoadAccountTransmogIllusions(holder.GetPreparedResult(AccountInfoQueryHolder::TRANSMOG_ILLUSIONS)); _collectionMgr->LoadAccountWarbandScenes(holder.GetPreparedResult(AccountInfoQueryHolder::WARBAND_SCENES)); + LoadPlayerDataAccount(holder.GetPreparedResult(AccountInfoQueryHolder::PLAYER_DATA_ELEMENTS_ACCOUNT), holder.GetPreparedResult(AccountInfoQueryHolder::PLAYER_DATA_FLAGS_ACCOUNT)); if (!m_inQueue) SendAuthResponse(ERROR_OK, false); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index a6c96948101..01299cc9f7c 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -1116,6 +1116,39 @@ class TC_GAME_API WorldSession _tutorialsChanged |= TUTORIALS_FLAG_CHANGED; } } + + struct PlayerDataAccount + { + struct Element + { + uint32 Id; + bool NeedSave; + union + { + float FloatValue; + int64 Int64Value; + }; + }; + + struct Flag + { + uint64 Value; + bool NeedSave; + }; + + std::vector<Element> Elements; + std::vector<Flag> Flags; + }; + + void LoadPlayerDataAccount(PreparedQueryResult const& elementsResult, PreparedQueryResult const& flagsResult); + void SavePlayerDataAccount(LoginDatabaseTransaction const& transaction); + + void SetPlayerDataElementAccount(uint32 dataElementId, float value); + void SetPlayerDataElementAccount(uint32 dataElementId, int64 value); + void SetPlayerDataFlagAccount(uint32 dataFlagId, bool on); + + PlayerDataAccount const& GetPlayerDataAccount() const { return _playerDataAccount; } + // Auction void SendAuctionHello(ObjectGuid guid, Unit const* unit); @@ -1991,6 +2024,7 @@ class TC_GAME_API WorldSession AccountData _accountData[NUM_ACCOUNT_DATA_TYPES]; std::array<uint32, MAX_ACCOUNT_TUTORIAL_VALUES> _tutorials; uint8 _tutorialsChanged; + PlayerDataAccount _playerDataAccount; std::vector<std::string> _registeredAddonPrefixes; bool _filterAddonMessages; uint32 recruiterId; diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index da18c6bf1d7..2fb3d3ae4c9 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -443,6 +443,10 @@ class TC_GAME_API Spell void EffectTeleportGraveyard(); void EffectUpdateInteractions(); void EffectLearnWarbandScene(); + void EffectSetPlayerDataElementAccount(); + void EffectSetPlayerDataElementCharacter(); + void EffectSetPlayerDataFlagAccount(); + void EffectSetPlayerDataFlagCharacter(); typedef std::unordered_set<Aura*> UsedSpellMods; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index eb983373d24..6baa049f831 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -423,10 +423,10 @@ NonDefaultConstructible<SpellEffectHandlerFn> SpellEffectHandlers[TOTAL_SPELL_EF &Spell::EffectNULL, //332 SPELL_EFFECT_332 &Spell::EffectNULL, //333 SPELL_EFFECT_333 &Spell::EffectNULL, //334 SPELL_EFFECT_334 - &Spell::EffectNULL, //335 SPELL_EFFECT_335 - &Spell::EffectNULL, //336 SPELL_EFFECT_336 - &Spell::EffectNULL, //337 SPELL_EFFECT_337 - &Spell::EffectNULL, //338 SPELL_EFFECT_338 + &Spell::EffectSetPlayerDataElementAccount, //335 SPELL_EFFECT_SET_PLAYER_DATA_ELEMENT_ACCOUNT + &Spell::EffectSetPlayerDataElementCharacter, //336 SPELL_EFFECT_SET_PLAYER_DATA_ELEMENT_CHARACTER + &Spell::EffectSetPlayerDataFlagAccount, //337 SPELL_EFFECT_SET_PLAYER_DATA_FLAG_ACCOUNT + &Spell::EffectSetPlayerDataFlagCharacter, //338 SPELL_EFFECT_SET_PLAYER_DATA_FLAG_CHARACTER &Spell::EffectNULL, //339 SPELL_EFFECT_UI_ACTION &Spell::EffectNULL, //340 SPELL_EFFECT_340 &Spell::EffectLearnWarbandScene, //341 SPELL_EFFECT_LEARN_WARBAND_SCENE @@ -6111,9 +6111,7 @@ void Spell::EffectModifyCooldowns() if (bitIndex < 0 || uint32(bitIndex) >= sizeof(flag128) * 8) return false; - flag128 reqFlag; - reqFlag[bitIndex / 32] = 1u << (bitIndex % 32); - return bool(spellOnCooldown->SpellFamilyFlags & reqFlag); + return (spellOnCooldown->SpellFamilyFlags[bitIndex / 32] & 1u << (bitIndex % 32)) != 0; }, Milliseconds(damage)); } @@ -6216,3 +6214,51 @@ void Spell::EffectLearnWarbandScene() target->GetSession()->GetCollectionMgr()->AddWarbandScene(effectInfo->MiscValue); } + +void Spell::EffectSetPlayerDataElementAccount() +{ + if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) + return; + + Player* target = Object::ToPlayer(unitTarget); + if (!target) + return; + + target->SetDataElementAccount(effectInfo->MiscValue, int64(damage)); +} + +void Spell::EffectSetPlayerDataElementCharacter() +{ + if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) + return; + + Player* target = Object::ToPlayer(unitTarget); + if (!target) + return; + + target->SetDataElementCharacter(effectInfo->MiscValue, int64(damage)); +} + +void Spell::EffectSetPlayerDataFlagAccount() +{ + if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) + return; + + Player* target = Object::ToPlayer(unitTarget); + if (!target) + return; + + target->SetDataFlagAccount(effectInfo->MiscValue, damage != 0); +} + +void Spell::EffectSetPlayerDataFlagCharacter() +{ + if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) + return; + + Player* target = Object::ToPlayer(unitTarget); + if (!target) + return; + + target->SetDataFlagCharacter(effectInfo->MiscValue, damage != 0); +} diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 7e81e5570f2..4cfd315baad 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -1249,10 +1249,10 @@ std::array<SpellEffectInfo::StaticData, TOTAL_SPELL_EFFECTS> SpellEffectInfo::_d {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 332 SPELL_EFFECT_332 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 333 SPELL_EFFECT_333 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 334 SPELL_EFFECT_334 - {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 335 SPELL_EFFECT_335 - {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 336 SPELL_EFFECT_336 - {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 337 SPELL_EFFECT_337 - {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 338 SPELL_EFFECT_338 + {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 335 SPELL_EFFECT_SET_PLAYER_DATA_ELEMENT_ACCOUNT + {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 336 SPELL_EFFECT_SET_PLAYER_DATA_ELEMENT_CHARACTER + {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 337 SPELL_EFFECT_SET_PLAYER_DATA_FLAG_ACCOUNT + {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 338 SPELL_EFFECT_SET_PLAYER_DATA_FLAG_CHARACTER {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 339 SPELL_EFFECT_UI_ACTION {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 340 SPELL_EFFECT_340 {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 341 SPELL_EFFECT_LEARN_WARBAND_SCENE |