diff options
-rw-r--r-- | sql/updates/hotfixes/master/2020_12_20_00_hotfixes.sql | 15 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.cpp | 98 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.h | 10 | ||||
-rw-r--r-- | src/server/game/Handlers/HotfixHandler.cpp | 27 | ||||
-rw-r--r-- | src/server/game/World/World.cpp | 2 |
5 files changed, 141 insertions, 11 deletions
diff --git a/sql/updates/hotfixes/master/2020_12_20_00_hotfixes.sql b/sql/updates/hotfixes/master/2020_12_20_00_hotfixes.sql new file mode 100644 index 00000000000..dc939580db6 --- /dev/null +++ b/sql/updates/hotfixes/master/2020_12_20_00_hotfixes.sql @@ -0,0 +1,15 @@ +-- +-- Table structure for table `hotfix_optional_data` +-- +DROP TABLE IF EXISTS `hotfix_optional_data`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `hotfix_optional_data` ( + `TableHash` int(10) unsigned NOT NULL, + `RecordId` int(10) unsigned NOT NULL, + `locale` varchar(4) NOT NULL, + `Key` int(10) unsigned NOT NULL, + `Data` blob NOT NULL, + `VerifiedBuild` int(11) NOT NULL DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index d09920fb960..72b2967f832 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -361,6 +361,7 @@ typedef std::tuple<uint16, uint8, int32> WMOAreaTableKey; typedef std::map<WMOAreaTableKey, WMOAreaTableEntry const*> WMOAreaTableLookupContainer; typedef std::pair<uint32 /*tableHash*/, int32 /*recordId*/> HotfixBlobKey; typedef std::map<HotfixBlobKey, std::vector<uint8>> HotfixBlobMap; +using AllowedHotfixOptionalData = std::pair<uint32 /*optional data key*/, bool(*)(std::vector<uint8> const& data) /*validator*/>; namespace { @@ -375,6 +376,8 @@ namespace StorageMap _stores; DB2Manager::HotfixContainer _hotfixData; std::array<HotfixBlobMap, TOTAL_LOCALES> _hotfixBlob; + std::unordered_multimap<uint32 /*tableHash*/, AllowedHotfixOptionalData> _allowedHotfixOptionalData; + std::array<std::map<HotfixBlobKey, std::vector<DB2Manager::HotfixOptionalData>>, TOTAL_LOCALES> _hotfixOptionalData; AreaGroupMemberContainer _areaGroupMembers; ArtifactPowersContainer _artifactPowers; @@ -1513,7 +1516,7 @@ void DB2Manager::LoadHotfixBlob(uint32 localeMask) auto storeItr = _stores.find(tableHash); if (storeItr != _stores.end()) { - TC_LOG_ERROR("server.loading", "Table hash 0x%X points to a loaded DB2 store %s, fill related table instead of hotfix_blob", + TC_LOG_ERROR("sql.sql", "Table hash 0x%X points to a loaded DB2 store %s, fill related table instead of hotfix_blob", tableHash, storeItr->second->GetFileName().c_str()); continue; } @@ -1524,7 +1527,7 @@ void DB2Manager::LoadHotfixBlob(uint32 localeMask) if (!IsValidLocale(locale)) { - TC_LOG_ERROR("server.loading", "`hotfix_blob` contains invalid locale: %s at TableHash: 0x%X and RecordID: %d", localeName.c_str(), tableHash, recordId); + TC_LOG_ERROR("sql.sql", "`hotfix_blob` contains invalid locale: %s at TableHash: 0x%X and RecordID: %d", localeName.c_str(), tableHash, recordId); continue; } @@ -1538,6 +1541,88 @@ void DB2Manager::LoadHotfixBlob(uint32 localeMask) TC_LOG_INFO("server.loading", ">> Loaded %d hotfix blob records in %u ms", hotfixBlobCount, GetMSTimeDiffToNow(oldMSTime)); } +bool ValidateBroadcastTextTactKeyOptionalData(std::vector<uint8> const& data) +{ + return data.size() == 8 + 16; +} + +void DB2Manager::LoadHotfixOptionalData(uint32 localeMask) +{ + // Register allowed optional data keys + _allowedHotfixOptionalData.emplace(sBroadcastTextStore.GetTableHash(), std::make_pair(sTactKeyStore.GetTableHash(), &ValidateBroadcastTextTactKeyOptionalData)); + + uint32 oldMSTime = getMSTime(); + + QueryResult result = HotfixDatabase.Query("SELECT TableHash, RecordId, locale, `Key`, `Data` FROM hotfix_optional_data ORDER BY TableHash"); + + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 hotfix optional data records."); + return; + } + + std::bitset<TOTAL_LOCALES> availableDb2Locales = localeMask; + uint32 hotfixOptionalDataCount = 0; + do + { + Field* fields = result->Fetch(); + + uint32 tableHash = fields[0].GetUInt32(); + auto allowedHotfixes = Trinity::Containers::MapEqualRange(_allowedHotfixOptionalData, tableHash); + if (allowedHotfixes.begin() == allowedHotfixes.end()) + { + TC_LOG_ERROR("sql.sql", "Table `hotfix_optional_data` references DB2 store by hash 0x%X that is not allowed to have optional data", tableHash); + continue; + } + + uint32 recordId = fields[1].GetInt32(); + auto storeItr = _stores.find(tableHash); + if (storeItr == _stores.end()) + { + TC_LOG_ERROR("sql.sql", "Table `hotfix_optional_data` references unknown DB2 store by hash 0x%X with RecordID: %u", tableHash, recordId); + continue; + } + + std::string localeName = fields[2].GetString(); + LocaleConstant locale = GetLocaleByName(localeName); + + if (!IsValidLocale(locale)) + { + TC_LOG_ERROR("sql.sql", "`hotfix_optional_data` contains invalid locale: %s at TableHash: 0x%X and RecordID: %u", localeName.c_str(), tableHash, recordId); + continue; + } + + if (!availableDb2Locales[locale]) + continue; + + DB2Manager::HotfixOptionalData optionalData; + optionalData.Key = fields[3].GetUInt32(); + auto allowedHotfixItr = std::find_if(allowedHotfixes.begin(), allowedHotfixes.end(), [&](std::pair<uint32 const, AllowedHotfixOptionalData> const& v) + { + return v.second.first == optionalData.Key; + }); + if (allowedHotfixItr == allowedHotfixes.end()) + { + TC_LOG_ERROR("sql.sql", "Table `hotfix_optional_data` references non-allowed optional data key 0x%X for DB2 store by hash 0x%X and RecordID: %u", + optionalData.Key, tableHash, recordId); + continue; + } + + optionalData.Data = fields[4].GetBinary(); + if (!allowedHotfixItr->second.second(optionalData.Data)) + { + TC_LOG_ERROR("sql.sql", "Table `hotfix_optional_data` contains invalid data for DB2 store 0x%X, RecordID: %u and Key: 0x%X", + tableHash, recordId, optionalData.Key); + continue; + } + + _hotfixOptionalData[locale][std::make_pair(tableHash, recordId)].push_back(std::move(optionalData)); + hotfixOptionalDataCount++; + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %d hotfix optional data records in %u ms", hotfixOptionalDataCount, GetMSTimeDiffToNow(oldMSTime)); +} + uint32 DB2Manager::GetHotfixCount() const { return _hotfixData.size(); @@ -1548,13 +1633,20 @@ DB2Manager::HotfixContainer const& DB2Manager::GetHotfixData() const return _hotfixData; } -std::vector<uint8> const* DB2Manager::GetHotfixBlobData(uint32 tableHash, int32 recordId, LocaleConstant locale) +std::vector<uint8> const* DB2Manager::GetHotfixBlobData(uint32 tableHash, int32 recordId, LocaleConstant locale) const { ASSERT(IsValidLocale(locale), "Locale %u is invalid locale", uint32(locale)); return Trinity::Containers::MapGetValuePtr(_hotfixBlob[locale], std::make_pair(tableHash, recordId)); } +std::vector<DB2Manager::HotfixOptionalData> const* DB2Manager::GetHotfixOptionalData(uint32 tableHash, int32 recordId, LocaleConstant locale) const +{ + ASSERT(IsValidLocale(locale), "Locale %u is invalid locale", uint32(locale)); + + return Trinity::Containers::MapGetValuePtr(_hotfixOptionalData[locale], std::make_pair(tableHash, recordId)); +} + uint32 DB2Manager::GetEmptyAnimStateID() const { return sAnimationDataStore.GetNumRows(); diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index d20c58b9dd1..69acbe8ef2a 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -287,6 +287,12 @@ public: } }; + struct HotfixOptionalData + { + uint32 Key; + std::vector<uint8> Data; + }; + using HotfixContainer = std::set<HotfixRecord>; using ItemBonusList = std::vector<ItemBonusEntry const*>; @@ -301,9 +307,11 @@ public: void LoadHotfixData(); void LoadHotfixBlob(uint32 localeMask); + void LoadHotfixOptionalData(uint32 localeMask); uint32 GetHotfixCount() const; HotfixContainer const& GetHotfixData() const; - std::vector<uint8> const* GetHotfixBlobData(uint32 tableHash, int32 recordId, LocaleConstant locale); + std::vector<uint8> const* GetHotfixBlobData(uint32 tableHash, int32 recordId, LocaleConstant locale) const; + std::vector<HotfixOptionalData> const* GetHotfixOptionalData(uint32 tableHash, int32 recordId, LocaleConstant locale) const; uint32 GetEmptyAnimStateID() const; std::vector<uint32> GetAreasForGroup(uint32 areaGroupId) const; diff --git a/src/server/game/Handlers/HotfixHandler.cpp b/src/server/game/Handlers/HotfixHandler.cpp index 85880fd0786..c1a200b616e 100644 --- a/src/server/game/Handlers/HotfixHandler.cpp +++ b/src/server/game/Handlers/HotfixHandler.cpp @@ -28,23 +28,26 @@ void WorldSession::HandleDBQueryBulk(WorldPackets::Hotfix::DBQueryBulk& dbQuery) { DB2StorageBase const* store = sDB2Manager.GetStorage(dbQuery.TableHash); - if (!store) - { - TC_LOG_ERROR("network", "CMSG_DB_QUERY_BULK: %s requested unsupported unknown hotfix type: %u", GetPlayerInfo().c_str(), dbQuery.TableHash); - return; - } - for (WorldPackets::Hotfix::DBQueryBulk::DBQueryRecord const& record : dbQuery.Queries) { WorldPackets::Hotfix::DBReply dbReply; dbReply.TableHash = dbQuery.TableHash; dbReply.RecordID = record.RecordID; - if (store->HasRecord(record.RecordID)) + if (store && store->HasRecord(record.RecordID)) { dbReply.Status = 1; dbReply.Timestamp = GameTime::GetGameTime(); store->WriteRecord(record.RecordID, GetSessionDbcLocale(), dbReply.Data); + + if (std::vector<DB2Manager::HotfixOptionalData> const* optionalDataEntries = sDB2Manager.GetHotfixOptionalData(dbQuery.TableHash, record.RecordID, GetSessionDbcLocale())) + { + for (DB2Manager::HotfixOptionalData const& optionalData : *optionalDataEntries) + { + dbReply.Data << uint32(optionalData.Key); + dbReply.Data.append(optionalData.Data.data(), optionalData.Data.size()); + } + } } else { @@ -78,6 +81,16 @@ void WorldSession::HandleHotfixRequest(WorldPackets::Hotfix::HotfixRequest& hotf { std::size_t pos = hotfixQueryResponse.HotfixContent.size(); storage->WriteRecord(uint32(hotfixRecord.RecordID), GetSessionDbcLocale(), hotfixQueryResponse.HotfixContent); + + if (std::vector<DB2Manager::HotfixOptionalData> const* optionalDataEntries = sDB2Manager.GetHotfixOptionalData(hotfixRecord.TableHash, hotfixRecord.RecordID, GetSessionDbcLocale())) + { + for (DB2Manager::HotfixOptionalData const& optionalData : *optionalDataEntries) + { + hotfixQueryResponse.HotfixContent << uint32(optionalData.Key); + hotfixQueryResponse.HotfixContent.append(optionalData.Data.data(), optionalData.Data.size()); + } + } + hotfixData.Size = hotfixQueryResponse.HotfixContent.size() - pos; } else if (std::vector<uint8> const* blobData = sDB2Manager.GetHotfixBlobData(hotfixRecord.TableHash, hotfixRecord.RecordID, GetSessionDbcLocale())) diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index b182464c063..c5c47857409 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1667,6 +1667,8 @@ void World::SetInitialWorldSettings() sDB2Manager.LoadHotfixBlob(m_availableDbcLocaleMask); TC_LOG_INFO("misc", "Loading hotfix info..."); sDB2Manager.LoadHotfixData(); + TC_LOG_INFO("misc", "Loading hotfix optional data..."); + sDB2Manager.LoadHotfixOptionalData(m_availableDbcLocaleMask); ///- Close hotfix database - it is only used during DB2 loading HotfixDatabase.Close(); ///- Load M2 fly by cameras |