aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/base/auth_database.sql52
-rw-r--r--sql/base/characters_database.sql52
-rw-r--r--sql/updates/auth/master/2025_06_27_00_auth.sql22
-rw-r--r--sql/updates/characters/master/2025_06_27_00_characters.sql22
-rw-r--r--sql/updates/hotfixes/master/2025_06_27_00_hotfixes.sql47
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.cpp7
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.h7
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.cpp20
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.h12
-rw-r--r--src/server/database/Database/Implementation/LoginDatabase.cpp7
-rw-r--r--src/server/database/Database/Implementation/LoginDatabase.h7
-rw-r--r--src/server/game/Achievements/CriteriaHandler.cpp14
-rw-r--r--src/server/game/DataStores/DB2LoadInfo.h48
-rw-r--r--src/server/game/DataStores/DB2Stores.cpp8
-rw-r--r--src/server/game/DataStores/DB2Stores.h4
-rw-r--r--src/server/game/DataStores/DB2Structure.h32
-rw-r--r--src/server/game/DataStores/DBCEnums.h18
-rw-r--r--src/server/game/Entities/Player/Player.cpp326
-rw-r--r--src/server/game/Entities/Player/Player.h23
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp8
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h10
-rw-r--r--src/server/game/Server/WorldSession.cpp167
-rw-r--r--src/server/game/Server/WorldSession.h34
-rw-r--r--src/server/game/Spells/Spell.h4
-rw-r--r--src/server/game/Spells/SpellEffects.cpp60
-rw-r--r--src/server/game/Spells/SpellInfo.cpp8
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