diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index f58eec190c..344fca4d78 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -42,6 +42,7 @@ #include "PhasingHandler.h" #include "Player.h" #include "PoolMgr.h" +#include "QueryPackets.h" #include "QuestDef.h" #include "ScriptedGossip.h" #include "SpellAuraEffects.h" @@ -164,6 +165,69 @@ uint32 CreatureTemplate::GetFirstVisibleModel() const return 17519; } +void CreatureTemplate::InitializeQueryData() +{ + WorldPacket queryTemp; + for (uint8 loc = LOCALE_enUS; loc < TOTAL_LOCALES; ++loc) + { + queryTemp = BuildQueryData(static_cast(loc)); + QueryData[loc] = queryTemp; + } +} + +WorldPacket CreatureTemplate::BuildQueryData(LocaleConstant loc) const +{ + WorldPackets::Query::QueryCreatureResponse queryTemp; + + std::string locName = Name; + std::string locNameAlt = FemaleName; + std::string locTitle = Title; + if (CreatureLocale const* cl = sObjectMgr->GetCreatureLocale(Entry)) + { + ObjectMgr::GetLocaleString(cl->Name, loc, locName); + ObjectMgr::GetLocaleString(cl->FemaleName, loc, locNameAlt); + ObjectMgr::GetLocaleString(cl->Title, loc, locTitle); + } + + queryTemp.CreatureID = Entry; + queryTemp.Allow = true; + + queryTemp.Stats.Name[0] = locName; + queryTemp.Stats.NameAlt[0] = locNameAlt; + queryTemp.Stats.Title = locTitle; + queryTemp.Stats.CursorName = IconName; + queryTemp.Stats.Flags[0] = type_flags; + queryTemp.Stats.Flags[1] = type_flags2; + queryTemp.Stats.CreatureType = type; + queryTemp.Stats.CreatureFamily = family; + queryTemp.Stats.Classification = rank; + + for (uint8 i = 0; i < MAX_KILL_CREDIT; ++i) + queryTemp.Stats.ProxyCreatureID[i] = KillCredit[i]; + + queryTemp.Stats.CreatureDisplayID[0] = Modelid1; + queryTemp.Stats.CreatureDisplayID[1] = Modelid2; + queryTemp.Stats.CreatureDisplayID[2] = Modelid3; + queryTemp.Stats.CreatureDisplayID[3] = Modelid4; + queryTemp.Stats.HpMulti = ModHealth; + queryTemp.Stats.EnergyMulti = ModMana; + queryTemp.Stats.Leader = RacialLeader; + + for (uint32 i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i) + queryTemp.Stats.QuestItems[i] = 0; + + if (std::vector const* items = sObjectMgr->GetCreatureQuestItemList(Entry)) + for (uint32 i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i) + if (i < items->size()) + queryTemp.Stats.QuestItems[i] = (*items)[i]; + + queryTemp.Stats.CreatureMovementInfoID = movementId; + queryTemp.Stats.RequiredExpansion = expansionUnknown; + queryTemp.Write(); + queryTemp.ShrinkToFit(); + return queryTemp.Move(); +} + bool AssistDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) { if (Unit* victim = ObjectAccessor::GetUnit(m_owner, m_victim)) diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index c5cffeedea..1a3b4284f7 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -25,6 +25,7 @@ #include "Duration.h" #include "Loot.h" #include "MapObject.h" + #include class CreatureAI; diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h index 3e0156d93d..d4a795a9e8 100644 --- a/src/server/game/Entities/Creature/CreatureData.h +++ b/src/server/game/Entities/Creature/CreatureData.h @@ -28,6 +28,7 @@ #include #include +class WorldPacket; struct ItemTemplate; enum class VisibilityDistanceType : uint8; @@ -195,6 +196,7 @@ struct TC_GAME_API CreatureTemplate uint32 SpellSchoolImmuneMask; uint32 flags_extra; uint32 ScriptID; + WorldPacket QueryData[TOTAL_LOCALES]; uint32 GetRandomValidModelId() const; uint32 GetFirstValidModelId() const; uint32 GetFirstInvisibleModel() const; @@ -226,6 +228,9 @@ struct TC_GAME_API CreatureTemplate // if can tame exotic then can tame any tameable return canTameExotic || !IsExotic(); } + + void InitializeQueryData(); + WorldPacket BuildQueryData(LocaleConstant loc) const; }; #pragma pack(push, 1) diff --git a/src/server/game/Entities/Creature/GossipDef.cpp b/src/server/game/Entities/Creature/GossipDef.cpp index 970ae18a2c..2818080172 100644 --- a/src/server/game/Entities/Creature/GossipDef.cpp +++ b/src/server/game/Entities/Creature/GossipDef.cpp @@ -246,7 +246,7 @@ void PlayerMenu::SendGossipMenu(uint32 titleTextId, ObjectGuid objectGUID) ObjectMgr::GetLocaleString(localeData->Title, localeConstant, title); if (questLevelInTitle) - AddQuestLevelToTitle(title, quest->GetQuestLevel()); + Quest::AddQuestLevelToTitle(title, quest->GetQuestLevel()); data << title; // max 0x200 } @@ -377,7 +377,7 @@ void PlayerMenu::SendQuestGiverQuestList(QEmote const& eEmote, const std::string ObjectMgr::GetLocaleString(questTemplateLocaleData->Title, localeConstant, title); if (questLevelInTitle) - AddQuestLevelToTitle(title, quest->GetQuestLevel()); + Quest::AddQuestLevelToTitle(title, quest->GetQuestLevel()); bool repeatable = quest->IsAutoComplete() && quest->IsRepeatable() && !quest->IsDailyOrWeekly() && !quest->IsMonthly(); questList.QuestDataText.emplace_back(questID, questMenuItem.QuestIcon, quest->GetQuestLevel(), quest->GetFlags(), repeatable, title); @@ -428,7 +428,7 @@ void PlayerMenu::SendQuestGiverQuestDetails(Quest const* quest, ObjectGuid npcGU } if (sWorld->getBoolConfig(CONFIG_UI_QUESTLEVELS_IN_DIALOGS)) - AddQuestLevelToTitle(packet.QuestTitle, quest->GetQuestLevel()); + Quest::AddQuestLevelToTitle(packet.QuestTitle, quest->GetQuestLevel()); packet.QuestGiverGUID = npcGUID; packet.InformUnit = _session->GetPlayer()->GetPlayerSharingQuest(); @@ -457,172 +457,14 @@ void PlayerMenu::SendQuestGiverQuestDetails(Quest const* quest, ObjectGuid npcGU void PlayerMenu::SendQuestQueryResponse(Quest const* quest) const { - std::string questTitle = quest->GetTitle(); - std::string questDetails = quest->GetDetails(); - std::string questObjectives = quest->GetObjectives(); - std::string questAreaDescription = quest->GetAreaDescription(); - std::string questCompletedText = quest->GetCompletedText(); - std::string questGiverTextWindow = quest->GetQuestGiverTextWindow(); - std::string questGiverTargetName = quest->GetQuestGiverTargetName(); - std::string questTurnTextWindow = quest->GetQuestTurnTextWindow(); - std::string questTurnTargetName = quest->GetQuestTurnTargetName(); - - std::string questObjectiveText[QUEST_OBJECTIVES_COUNT]; - for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - questObjectiveText[i] = quest->ObjectiveText[i]; - - LocaleConstant localeConstant = _session->GetSessionDbLocaleIndex(); - if (localeConstant != LOCALE_enUS) - { - if (QuestLocale const* localeData = sObjectMgr->GetQuestLocale(quest->GetQuestId())) - { - ObjectMgr::GetLocaleString(localeData->Title, localeConstant, questTitle); - ObjectMgr::GetLocaleString(localeData->Details, localeConstant, questDetails); - ObjectMgr::GetLocaleString(localeData->Objectives, localeConstant, questObjectives); - ObjectMgr::GetLocaleString(localeData->AreaDescription, localeConstant, questAreaDescription); - ObjectMgr::GetLocaleString(localeData->CompletedText, localeConstant, questCompletedText); - ObjectMgr::GetLocaleString(localeData->QuestGiverTextWindow, localeConstant, questGiverTextWindow); - ObjectMgr::GetLocaleString(localeData->QuestGiverTargetName, localeConstant, questGiverTargetName); - ObjectMgr::GetLocaleString(localeData->QuestTurnTextWindow, localeConstant, questTurnTextWindow); - ObjectMgr::GetLocaleString(localeData->QuestTurnTargetName, localeConstant, questTurnTargetName); - - for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - ObjectMgr::GetLocaleString(localeData->ObjectiveText[i], localeConstant, questObjectiveText[i]); - } - } - - WorldPacket data(SMSG_QUEST_QUERY_RESPONSE, 100); // guess size - - data << uint32(quest->GetQuestId()); // quest id - data << uint32(quest->GetQuestMethod()); // Accepted values: 0, 1 or 2. 0 == IsAutoComplete() (skip objectives/details) - data << uint32(quest->GetQuestLevel()); // may be -1, static data, in other cases must be used dynamic level: Player::GetQuestLevel (0 is not known, but assuming this is no longer valid for quest intended for client) - data << uint32(quest->GetMinLevel()); // min level - data << uint32(quest->GetZoneOrSort()); // zone or sort to display in quest log - - data << uint32(quest->GetType()); // quest type - data << uint32(quest->GetSuggestedPlayers()); // suggested players count - - data << uint32(quest->GetRepObjectiveFaction()); // shown in quest log as part of quest objective - data << uint32(quest->GetRepObjectiveValue()); // shown in quest log as part of quest objective - - data << uint32(quest->GetRepObjectiveFaction2()); // shown in quest log as part of quest objective OPPOSITE faction - data << uint32(quest->GetRepObjectiveValue2()); // shown in quest log as part of quest objective OPPOSITE faction - - data << uint32(quest->GetNextQuestInChain()); // client will request this quest from NPC, if not 0 - data << uint32(quest->GetXPId()); // used for calculating rewarded experience - - if (quest->HasFlag(QUEST_FLAGS_HIDDEN_REWARDS)) - data << uint32(0); // Hide money rewarded - else - data << uint32(quest->GetRewOrReqMoney(_session->GetPlayer())); // reward money (below max lvl) - - data << uint32(quest->GetRewMoneyMaxLevel()); // used in XP calculation at client - data << uint32(quest->GetRewSpell()); // reward spell, this spell will display (icon) (cast if RewSpellCast == 0) - data << int32(quest->GetRewSpellCast()); // cast spell - - // rewarded honor points - data << uint32(quest->GetRewHonorAddition()); - data << float(quest->GetRewHonorMultiplier()); - data << uint32(quest->GetSrcItemId()); // source item id - data << uint32(quest->GetFlags()); // quest flags - data << uint32(quest->GetMinimapTargetMark()); // minimap target mark (skull, etc. missing enum) - data << uint32(quest->GetCharTitleId()); // CharTitleId, new 2.4.0, player gets this title (id from CharTitles) - data << uint32(quest->GetPlayersSlain()); // players slain - data << uint32(quest->GetBonusTalents()); // bonus talents - data << uint32(quest->GetRewArenaPoints()); // bonus arena points FIXME: arena points were removed, right? - data << uint32(quest->GetRewardSkillId()); // reward skill id - data << uint32(quest->GetRewardSkillPoints()); // reward skill points - data << uint32(quest->GetRewardReputationMask()); // rep mask (unsure on what it does) - data << uint32(quest->GetQuestGiverPortrait()); // quest giver entry ? - data << uint32(quest->GetQuestTurnInPortrait()); // quest turnin entry ? - - if (quest->HasFlag(QUEST_FLAGS_HIDDEN_REWARDS)) - { - for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i) - data << uint32(0) << uint32(0); - for (uint8 i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i) - data << uint32(0) << uint32(0); - } + if (sWorld->getBoolConfig(CONFIG_CACHE_DATA_QUERIES)) + _session->SendPacket(&quest->QueryData[static_cast(_session->GetSessionDbLocaleIndex())]); else { - for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i) - { - data << uint32(quest->RewardItemId[i]); - data << uint32(quest->RewardItemIdCount[i]); - } - for (uint8 i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i) - { - data << uint32(quest->RewardChoiceItemId[i]); - data << uint32(quest->RewardChoiceItemCount[i]); - } + WorldPacket queryPacket = quest->BuildQueryData(_session->GetSessionDbLocaleIndex()); + _session->SendPacket(&queryPacket); } - for (uint8 i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) // reward factions ids - data << uint32(quest->RewardFactionId[i]); - - for (uint8 i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) // columnid+1 QuestFactionReward.dbc? - data << int32(quest->RewardFactionValueId[i]); - - for (uint8 i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) // unknown usage - data << int32(quest->RewardFactionValueIdOverride[i]); - - data << uint32(quest->GetPOIContinent()); - data << float(quest->GetPOIx()); - data << float(quest->GetPOIy()); - data << uint32(quest->GetPointOpt()); - - if (sWorld->getBoolConfig(CONFIG_UI_QUESTLEVELS_IN_DIALOGS)) - AddQuestLevelToTitle(questTitle, quest->GetQuestLevel()); - - data << questTitle; - data << questObjectives; - data << questDetails; - data << questAreaDescription; - data << questCompletedText; // display in quest objectives window once all objectives are completed - - for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - { - if (quest->RequiredNpcOrGo[i] < 0) - data << uint32((quest->RequiredNpcOrGo[i] * (-1)) | 0x80000000); // client expects gameobject template id in form (id|0x80000000) - else - data << uint32(quest->RequiredNpcOrGo[i]); - - data << uint32(quest->RequiredNpcOrGoCount[i]); - data << uint32(quest->ItemDrop[i]); - data << uint32(quest->ItemDropQuantity[i]); - } - - for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i) - { - data << uint32(quest->RequiredItemId[i]); - data << uint32(quest->RequiredItemCount[i]); - } - - data << uint32(quest->GetRequiredSpell()); // Is it required to be cast, learned or what? - - for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - data << questObjectiveText[i]; - - for (uint32 i = 0; i < QUEST_REWARD_CURRENCY_COUNT; ++i) - { - data << uint32(quest->RewardCurrencyId[i]); - data << uint32(quest->RewardCurrencyCount[i]); - } - - for (uint32 i = 0; i < QUEST_REQUIRED_CURRENCY_COUNT; ++i) - { - data << uint32(quest->RequiredCurrencyId[i]); - data << uint32(quest->RequiredCurrencyCount[i]); - } - - data << questGiverTextWindow; - data << questGiverTargetName; - data << questTurnTextWindow; - data << questTurnTargetName; - data << uint32(quest->GetSoundAccept()); - data << uint32(quest->GetSoundTurnIn()); - - _session->SendPacket(&data); TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUEST_QUERY_RESPONSE questid=%u", quest->GetQuestId()); } @@ -652,7 +494,7 @@ void PlayerMenu::SendQuestGiverOfferReward(Quest const* quest, ObjectGuid npcGUI } if (sWorld->getBoolConfig(CONFIG_UI_QUESTLEVELS_IN_DIALOGS)) - AddQuestLevelToTitle(packet.QuestTitle, quest->GetQuestLevel()); + Quest::AddQuestLevelToTitle(packet.QuestTitle, quest->GetQuestLevel()); WorldPackets::Quest::QuestGiverOfferReward& offer = packet.QuestData; @@ -698,7 +540,7 @@ void PlayerMenu::SendQuestGiverRequestItems(Quest const* quest, ObjectGuid npcGU } if (sWorld->getBoolConfig(CONFIG_UI_QUESTLEVELS_IN_DIALOGS)) - AddQuestLevelToTitle(questTitle, quest->GetQuestLevel()); + Quest::AddQuestLevelToTitle(questTitle, quest->GetQuestLevel()); WorldPackets::Quest::QuestGiverRequestItems packet; @@ -765,13 +607,3 @@ void PlayerMenu::SendQuestGiverRequestItems(Quest const* quest, ObjectGuid npcGU _session->SendPacket(packet.Write()); TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUEST_GIVER_REQUEST_ITEMS NPC=%s, questid=%u", npcGUID.ToString().c_str(), quest->GetQuestId()); } - -void PlayerMenu::AddQuestLevelToTitle(std::string &title, int32 level) -{ - // Adds the quest level to the front of the quest title - // example: [13] Westfall Stew - - std::stringstream questTitlePretty; - questTitlePretty << "[" << level << "] " << title; - title = questTitlePretty.str(); -} diff --git a/src/server/game/Entities/Creature/GossipDef.h b/src/server/game/Entities/Creature/GossipDef.h index 38b23f882b..bc09b2d01e 100644 --- a/src/server/game/Entities/Creature/GossipDef.h +++ b/src/server/game/Entities/Creature/GossipDef.h @@ -303,8 +303,6 @@ class TC_GAME_API PlayerMenu void SendQuestGiverOfferReward(Quest const* quest, ObjectGuid npcGUID, bool autoLaunched) const; void SendQuestGiverRequestItems(Quest const* quest, ObjectGuid npcGUID, bool canComplete, bool autoLaunched) const; - static void AddQuestLevelToTitle(std::string &title, int32 level); - private: GossipMenu _gossipMenu; QuestMenu _questMenu; diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index acd894bf9a..b411db0d58 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -37,6 +37,7 @@ #include "OutdoorPvPMgr.h" #include "PhasingHandler.h" #include "PoolMgr.h" +#include "QueryPackets.h" #include "ScriptMgr.h" #include "SpellMgr.h" #include "Transport.h" @@ -57,6 +58,60 @@ QuaternionData QuaternionData::fromEulerAnglesZYX(float Z, float Y, float X) return QuaternionData(quat.x, quat.y, quat.z, quat.w); } +void GameObjectTemplate::InitializeQueryData() +{ + WorldPacket queryTemp; + for (uint8 loc = LOCALE_enUS; loc < TOTAL_LOCALES; ++loc) + { + queryTemp = BuildQueryData(static_cast(loc)); + QueryData[loc] = queryTemp; + } +} + +WorldPacket GameObjectTemplate::BuildQueryData(LocaleConstant loc) const +{ + WorldPackets::Query::QueryGameObjectResponse queryTemp; + + std::string locName = name; + std::string locIconName = IconName; + std::string locCastBarCaption = castBarCaption; + + if (GameObjectLocale const* gameObjectLocale = sObjectMgr->GetGameObjectLocale(entry)) + { + ObjectMgr::GetLocaleString(gameObjectLocale->Name, loc, locName); + ObjectMgr::GetLocaleString(gameObjectLocale->CastBarCaption, loc, locCastBarCaption); + } + + queryTemp.GameObjectID = entry; + queryTemp.Allow = true; + + queryTemp.Stats.Type = type; + queryTemp.Stats.DisplayID = displayId; + queryTemp.Stats.Name[0] = locName; + queryTemp.Stats.IconName = locIconName; + queryTemp.Stats.CastBarCaption = locCastBarCaption; + queryTemp.Stats.UnkString = unk1; + + for (uint8 i = 0; i < MAX_GAMEOBJECT_DATA; ++i) + queryTemp.Stats.Data[i] = raw.data[i]; + + queryTemp.Stats.Size = size; + + for (uint32 i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; ++i) + queryTemp.Stats.QuestItems[i] = 0; + + if (std::vector const* items = sObjectMgr->GetCreatureQuestItemList(entry)) + for (uint32 i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; ++i) + if (i < items->size()) + queryTemp.Stats.QuestItems[i] = (*items)[i]; + + queryTemp.Stats.RequiredLevel = RequiredLevel; + + queryTemp.Write(); + queryTemp.ShrinkToFit(); + return queryTemp.Move(); +} + GameObject::GameObject() : WorldObject(false), MapObject(), m_model(nullptr), m_goValue(), m_AI(nullptr), m_respawnCompatibilityMode(false), _triggerJustAppeared(true) { diff --git a/src/server/game/Entities/GameObject/GameObjectData.h b/src/server/game/Entities/GameObject/GameObjectData.h index 907e21e483..65a6307428 100644 --- a/src/server/game/Entities/GameObject/GameObjectData.h +++ b/src/server/game/Entities/GameObject/GameObjectData.h @@ -29,6 +29,8 @@ #define MAX_GAMEOBJECT_QUEST_ITEMS 6 +class WorldPacket; + // from `gameobject_template` struct GameObjectTemplate { @@ -426,6 +428,7 @@ struct GameObjectTemplate std::string AIName; uint32 ScriptId; + WorldPacket QueryData[TOTAL_LOCALES]; // helpers bool IsDespawnAtAction() const @@ -594,6 +597,9 @@ struct GameObjectTemplate default: return false; } } + + void InitializeQueryData(); + WorldPacket BuildQueryData(LocaleConstant loc) const; }; // From `gameobject_template_addon` diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index b800b87828..83fa87b655 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -46,6 +46,8 @@ #include "PhasingHandler.h" #include "Player.h" #include "PoolMgr.h" +#include "QuestDef.h" +#include "QueryPackets.h" #include "Random.h" #include "ReputationMgr.h" #include "ScriptMgr.h" @@ -7741,7 +7743,6 @@ void ObjectMgr::LoadQuestPOI() // 0 1 2 3 4 5 6 7 QueryResult result = WorldDatabase.Query("SELECT QuestID, id, ObjectiveIndex, MapID, WorldMapAreaId, Floor, Priority, Flags FROM quest_poi order by QuestID"); - if (!result) { TC_LOG_ERROR("server.loading", ">> Loaded 0 quest POI definitions. DB table `quest_poi` is empty."); @@ -7751,7 +7752,7 @@ void ObjectMgr::LoadQuestPOI() // 0 1 2 3 QueryResult points = WorldDatabase.Query("SELECT QuestID, Idx1, X, Y FROM quest_poi_points ORDER BY QuestID DESC, Idx2"); - std::vector > > POIs; + std::vector>> POIs; if (points) { @@ -7772,7 +7773,10 @@ void ObjectMgr::LoadQuestPOI() if (POIs[questId].size() <= id + 1) POIs[questId].resize(id + 10); - QuestPOIPoint point(x, y); + QuestPOIBlobPoint point; + point.X = x; + point.Y = y; + POIs[questId][id].push_back(point); } while (points->NextRow()); } @@ -7790,11 +7794,30 @@ void ObjectMgr::LoadQuestPOI() uint32 unk3 = fields[6].GetUInt32(); uint32 unk4 = fields[7].GetUInt32(); - QuestPOI POI(id, objIndex, mapId, WorldMapAreaId, FloorId, unk3, unk4); + QuestPOIBlobData POI; + POI.BlobIndex = id; + POI.ObjectiveIndex = objIndex; + POI.MapID = mapId; + POI.WorldMapAreaID = WorldMapAreaId; + POI.Floor = FloorId; + POI.Unk3 = unk3; + POI.Unk4 = unk4; + if (questId < POIs.size() && id < POIs[questId].size()) { - POI.points = POIs[questId][id]; - _questPOIStore[questId].push_back(POI); + POI.QuestPOIBlobPointStats = POIs[questId][id]; + auto itr = _questPOIStore.find(questId); + if (itr == _questPOIStore.end()) + { + QuestPOIWrapper wrapper; + QuestPOIData data; + data.QuestID = questId; + wrapper.POIData = data; + + _questPOIStore.emplace(questId, std::move(wrapper)); + } + + _questPOIStore.at(questId).POIData.QuestPOIBlobDataStats.push_back(POI); } else TC_LOG_ERROR("sql.sql", "Table quest_poi references unknown quest points for quest %u POI id %u", questId, id); @@ -10098,3 +10121,62 @@ uint32 ObjectMgr::GetGameObjectTypeByEntry(uint32 entry) const return MAX_GAMEOBJECT_TYPE; } + +void ObjectMgr::InitializeQueriesData(QueryDataGroup mask) +{ + // cache disabled + if (!sWorld->getBoolConfig(CONFIG_CACHE_DATA_QUERIES)) + return; + + // Initialize Query data for creatures + if (mask & QUERY_DATA_CREATURES) + for (auto& creaturePair : _creatureTemplateStore) + creaturePair.second.InitializeQueryData(); + + // Initialize Query Data for gameobjects + if (mask & QUERY_DATA_GAMEOBJECTS) + for (auto& gameobjectPair : _gameObjectTemplateStore) + gameobjectPair.second.InitializeQueryData(); + + // Initialize Query Data for quests + if (mask & QUERY_DATA_QUESTS) + for (auto& questPair : _questTemplates) + questPair.second.InitializeQueryData(); + + // Initialize Quest POI data + if (mask & QUERY_DATA_POIS) + for (auto& poiPair : _questPOIStore) + poiPair.second.InitializeQueryData(); +} + +void QuestPOIWrapper::InitializeQueryData() +{ + QueryDataBuffer = BuildQueryData(); +} + +ByteBuffer QuestPOIWrapper::BuildQueryData() const +{ + ByteBuffer tempBuffer; + tempBuffer << uint32(POIData.QuestID); // quest ID + tempBuffer << uint32(POIData.QuestPOIBlobDataStats.size()); // POI count + + for (QuestPOIBlobData const& questPOIBlobData : POIData.QuestPOIBlobDataStats) + { + tempBuffer << uint32(questPOIBlobData.BlobIndex); // POI index + tempBuffer << int32(questPOIBlobData.ObjectiveIndex); // objective index + tempBuffer << uint32(questPOIBlobData.MapID); // mapid + tempBuffer << uint32(questPOIBlobData.WorldMapAreaID); // areaid + tempBuffer << uint32(questPOIBlobData.Floor); // floorid + tempBuffer << uint32(questPOIBlobData.Unk3); // unknown + tempBuffer << uint32(questPOIBlobData.Unk4); // unknown + tempBuffer << uint32(questPOIBlobData.QuestPOIBlobPointStats.size()); // POI points count + + for (QuestPOIBlobPoint const& questPOIBlobPoint : questPOIBlobData.QuestPOIBlobPointStats) + { + tempBuffer << int32(questPOIBlobPoint.X); // POI point x + tempBuffer << int32(questPOIBlobPoint.Y); // POI point y + } + } + + return tempBuffer; +} diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index cd501c0eab..c744e6e9df 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -809,32 +809,42 @@ typedef std::multimap GossipMenuItemsContainer; typedef std::pair GossipMenuItemsMapBounds; typedef std::pair GossipMenuItemsMapBoundsNonConst; -struct QuestPOIPoint +struct QuestPOIBlobPoint { - int32 x; - int32 y; - - QuestPOIPoint() : x(0), y(0) { } - QuestPOIPoint(int32 _x, int32 _y) : x(_x), y(_y) { } + int32 X = 0; + int32 Y = 0; }; -struct QuestPOI +struct QuestPOIBlobData { - uint32 Id; - int32 ObjectiveIndex; - uint32 MapId; - uint32 AreaId; - uint32 FloorId; - uint32 Unk3; - uint32 Unk4; - std::vector points; - - QuestPOI() : Id(0), ObjectiveIndex(0), MapId(0), AreaId(0), FloorId(0), Unk3(0), Unk4(0) { } - QuestPOI(uint32 id, int32 objIndex, uint32 mapId, uint32 areaId, uint32 floorId, uint32 unk3, uint32 unk4) : Id(id), ObjectiveIndex(objIndex), MapId(mapId), AreaId(areaId), FloorId(floorId), Unk3(unk3), Unk4(unk4) { } + uint32 BlobIndex = 0; + int32 ObjectiveIndex = 0; + uint32 MapID = 0; + uint32 WorldMapAreaID = 0; + uint32 Floor = 0; + uint32 Unk3 = 0; + uint32 Unk4 = 0; + std::vector QuestPOIBlobPointStats; }; -typedef std::vector QuestPOIVector; -typedef std::unordered_map QuestPOIContainer; +struct QuestPOIData +{ + uint32 QuestID = 0; + std::vector QuestPOIBlobDataStats; +}; + +struct QuestPOIWrapper +{ + QuestPOIData POIData; + ByteBuffer QueryDataBuffer; + + void InitializeQueryData(); + ByteBuffer BuildQueryData() const; + + QuestPOIWrapper() : QueryDataBuffer(0) { } +}; + +typedef std::unordered_map QuestPOIContainer; struct QuestGreeting { @@ -934,6 +944,16 @@ struct PhaseAreaInfo ConditionContainer Conditions; }; +enum QueryDataGroup +{ + QUERY_DATA_CREATURES = 0x01, + QUERY_DATA_GAMEOBJECTS = 0x02, + QUERY_DATA_QUESTS = 0x04, + QUERY_DATA_POIS = 0x08, + + QUERY_DATA_ALL = 0xFF +}; + class PlayerDumpReader; class TC_GAME_API ObjectMgr @@ -1103,7 +1123,7 @@ class TC_GAME_API ObjectMgr return nullptr; } - QuestPOIVector const* GetQuestPOIVector(uint32 questId) + QuestPOIWrapper const* GetQuestPOIWrapper(uint32 questId) { QuestPOIContainer::const_iterator itr = _questPOIStore.find(questId); if (itr != _questPOIStore.end()) @@ -1226,6 +1246,8 @@ class TC_GAME_API ObjectMgr void LoadTrainers(); void LoadCreatureDefaultTrainers(); + void InitializeQueriesData(QueryDataGroup mask); + void LoadPhases(); void UnloadPhaseConditions(); void LoadTerrainSwapDefaults(); diff --git a/src/server/game/Handlers/QueryHandler.cpp b/src/server/game/Handlers/QueryHandler.cpp index 5c62c632d6..e52fb741ff 100644 --- a/src/server/game/Handlers/QueryHandler.cpp +++ b/src/server/game/Handlers/QueryHandler.cpp @@ -90,152 +90,54 @@ void WorldSession::SendQueryTimeResponse() } /// Only _static_ data is sent in this packet !!! -void WorldSession::HandleCreatureQueryOpcode(WorldPacket& recvData) +void WorldSession::HandleCreatureQueryOpcode(WorldPackets::Query::QueryCreature& query) { - uint32 entry; - recvData >> entry; - ObjectGuid guid; - recvData >> guid; - - CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(entry); - if (creatureInfo) + if (CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(query.CreatureID)) { - std::string Name, FemaleName, Title; - Name = creatureInfo->Name; - FemaleName = creatureInfo->FemaleName; - Title = creatureInfo->Title; - - LocaleConstant locale = GetSessionDbLocaleIndex(); - if (locale != LOCALE_enUS) - { - if (CreatureLocale const* creatureLocale = sObjectMgr->GetCreatureLocale(entry)) - { - ObjectMgr::GetLocaleString(creatureLocale->Name, locale, Name); - ObjectMgr::GetLocaleString(creatureLocale->FemaleName, locale, FemaleName); - ObjectMgr::GetLocaleString(creatureLocale->Title, locale, Title); - } - } - - TC_LOG_DEBUG("network", "WORLD: CMSG_CREATURE_QUERY '%s' - Entry: %u.", creatureInfo->Name.c_str(), entry); - - WorldPacket data(SMSG_CREATURE_QUERY_RESPONSE, 100); // guess size - data << uint32(entry); // creature entry - data << Name; // Name - - for (uint8 i = 0; i < 3; i++) - data << uint8(0); // name2, ..., name3 - - data << FemaleName; // FemaleName - - for (uint8 i = 0; i < 3; i++) - data << uint8(0); // name5, ..., name8 - - data << Title; // Title - data << creatureInfo->IconName; // "Directions" for guard, string for Icons 2.3.0 - data << uint32(creatureInfo->type_flags); // flags - data << uint32(creatureInfo->type_flags2); // unknown meaning - data << uint32(creatureInfo->type); // CreatureType.dbc - data << uint32(creatureInfo->family); // CreatureFamily.dbc - data << uint32(creatureInfo->rank); // Creature Rank (elite, boss, etc) - data << uint32(creatureInfo->KillCredit[0]); // new in 3.1, kill credit - data << uint32(creatureInfo->KillCredit[1]); // new in 3.1, kill credit - data << uint32(creatureInfo->Modelid1); // Modelid1 - data << uint32(creatureInfo->Modelid2); // Modelid2 - data << uint32(creatureInfo->Modelid3); // Modelid3 - data << uint32(creatureInfo->Modelid4); // Modelid4 - data << float(creatureInfo->ModHealth); // dmg/hp modifier - data << float(creatureInfo->ModMana); // dmg/mana modifier - data << uint8(creatureInfo->RacialLeader); // RacialLeader - - std::vector const* items = sObjectMgr->GetCreatureQuestItemList(entry); - if (items) - for (uint32 i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i) - data << (i < items->size() ? uint32((*items)[i]) : uint32(0)); + TC_LOG_DEBUG("network", "WORLD: CMSG_CREATURE_QUERY '%s' - Entry: %u.", ci->Name.c_str(), query.CreatureID); + if (sWorld->getBoolConfig(CONFIG_CACHE_DATA_QUERIES)) + SendPacket(&ci->QueryData[static_cast(GetSessionDbLocaleIndex())]); else - for (uint32 i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i) - data << uint32(0); - - data << uint32(creatureInfo->movementId); // CreatureMovementInfo.dbc - data << uint32(creatureInfo->expansionUnknown); // unknown meaning - - SendPacket(&data); - + { + WorldPacket response = ci->BuildQueryData(GetSessionDbLocaleIndex()); + SendPacket(&response); + } TC_LOG_DEBUG("network", "WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE"); } else { TC_LOG_DEBUG("network", "WORLD: CMSG_CREATURE_QUERY - NO CREATURE INFO! (%s, ENTRY: %u)", - guid.ToString().c_str(), entry); - - WorldPacket data(SMSG_CREATURE_QUERY_RESPONSE, 4); - data << uint32(entry | 0x80000000); - SendPacket(&data); + query.GUID.ToString().c_str(), query.CreatureID); + WorldPackets::Query::QueryCreatureResponse response; + response.CreatureID = query.CreatureID; + SendPacket(response.Write()); TC_LOG_DEBUG("network", "WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE"); } } /// Only _static_ data is sent in this packet !!! -void WorldSession::HandleGameObjectQueryOpcode(WorldPacket& recvData) +void WorldSession::HandleGameObjectQueryOpcode(WorldPackets::Query::QueryGameObject& query) { - uint32 entry; - recvData >> entry; - ObjectGuid guid; - recvData >> guid; - - GameObjectTemplate const* info = sObjectMgr->GetGameObjectTemplate(entry); - if (info) + if (GameObjectTemplate const* info = sObjectMgr->GetGameObjectTemplate(query.GameObjectID)) { - std::string Name; - std::string IconName; - std::string CastBarCaption; - - Name = info->name; - IconName = info->IconName; - CastBarCaption = info->castBarCaption; - - LocaleConstant localeConstant = GetSessionDbLocaleIndex(); - if (localeConstant != LOCALE_enUS) - { - if (GameObjectLocale const* gameObjectLocale = sObjectMgr->GetGameObjectLocale(entry)) - { - ObjectMgr::GetLocaleString(gameObjectLocale->Name, localeConstant, Name); - ObjectMgr::GetLocaleString(gameObjectLocale->CastBarCaption, localeConstant, CastBarCaption); - } - } - - TC_LOG_DEBUG("network", "WORLD: CMSG_GAMEOBJECT_QUERY '%s' - Entry: %u. ", info->name.c_str(), entry); - WorldPacket data (SMSG_GAMEOBJECT_QUERY_RESPONSE, 150); - data << uint32(entry); - data << uint32(info->type); - data << uint32(info->displayId); - data << Name; - data << uint8(0) << uint8(0) << uint8(0); // name2, name3, name4 - data << IconName; // 2.0.3, string. Icon name to use instead of default icon for go's (ex: "Attack" makes sword) - data << CastBarCaption; // 2.0.3, string. Text will appear in Cast Bar when using GO (ex: "Collecting") - data << info->unk1; // 2.0.3, string - data.append(info->raw.data, MAX_GAMEOBJECT_DATA); - data << float(info->size); // go size - data << int32(info->RequiredLevel); // 4.x, Required level - - std::vector const* items = sObjectMgr->GetGameObjectQuestItemList(entry); - if (items) - for (size_t i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; ++i) - data << (i < items->size() ? uint32((*items)[i]) : uint32(0)); + if (sWorld->getBoolConfig(CONFIG_CACHE_DATA_QUERIES)) + SendPacket(&info->QueryData[static_cast(GetSessionDbLocaleIndex())]); else - for (size_t i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; ++i) - data << uint32(0); - - SendPacket(&data); + { + WorldPacket response = info->BuildQueryData(GetSessionDbLocaleIndex()); + SendPacket(&response); + } TC_LOG_DEBUG("network", "WORLD: Sent SMSG_GAMEOBJECT_QUERY_RESPONSE"); } else { TC_LOG_DEBUG("network", "WORLD: CMSG_GAMEOBJECT_QUERY - Missing gameobject info for (%s, ENTRY: %u)", - guid.ToString().c_str(), entry); - WorldPacket data (SMSG_GAMEOBJECT_QUERY_RESPONSE, 4); - data << uint32(entry | 0x80000000); - SendPacket(&data); + query.GUID.ToString().c_str(), query.GameObjectID); + + WorldPackets::Query::QueryGameObjectResponse response; + response.GameObjectID = query.GameObjectID; + SendPacket(response.Write()); TC_LOG_DEBUG("network", "WORLD: Sent SMSG_GAMEOBJECT_QUERY_RESPONSE"); } } @@ -469,61 +371,31 @@ void WorldSession::HandleQuestNPCQuery(WorldPacket& recvData) SendPacket(&data); } -void WorldSession::HandleQuestPOIQuery(WorldPacket& recvData) +void WorldSession::HandleQuestPOIQuery(WorldPackets::Query::QuestPOIQuery& query) { - uint32 count; - recvData >> count; // quest count, max=25 - - if (count > MAX_QUEST_LOG_SIZE) - { - recvData.rfinish(); + if (query.MissingQuestCount > MAX_QUEST_LOG_SIZE) return; - } // Read quest ids and add the in a unordered_set so we don't send POIs for the same quest multiple times std::unordered_set questIds; - for (uint32 i = 0; i < count; ++i) - questIds.insert(recvData.read()); // quest id + for (uint32 i = 0; i < query.MissingQuestCount; ++i) + questIds.insert(query.MissingQuestPOIs[i]); // quest id - WorldPacket data(SMSG_QUEST_POI_QUERY_RESPONSE, 4 + (4 + 4)*questIds.size()); + WorldPacket data(SMSG_QUEST_POI_QUERY_RESPONSE, 4 + (4 + 4 + 40) * questIds.size()); data << uint32(questIds.size()); // count - - for (auto itr = questIds.begin(); itr != questIds.end(); ++itr) + for (uint32 questId : questIds) { - uint32 questId = *itr; - - bool questOk = false; - - uint16 questSlot = _player->FindQuestSlot(questId); - - if (questSlot != MAX_QUEST_LOG_SIZE) - questOk =_player->GetQuestSlotQuestId(questSlot) == questId; - - if (questOk) + uint16 const questSlot = _player->FindQuestSlot(questId); + if (questSlot != MAX_QUEST_LOG_SIZE && _player->GetQuestSlotQuestId(questSlot) == questId) { - QuestPOIVector const* POI = sObjectMgr->GetQuestPOIVector(questId); - - if (POI) + if (QuestPOIWrapper const* poiWrapper = sObjectMgr->GetQuestPOIWrapper(questId)) { - data << uint32(questId); // quest ID - data << uint32(POI->size()); // POI count - - for (QuestPOIVector::const_iterator itr = POI->begin(); itr != POI->end(); ++itr) + if (sWorld->getBoolConfig(CONFIG_CACHE_DATA_QUERIES)) + data.append(poiWrapper->QueryDataBuffer); + else { - data << uint32(itr->Id); // POI index - data << int32(itr->ObjectiveIndex); // objective index - data << uint32(itr->MapId); // mapid - data << uint32(itr->AreaId); // areaid - data << uint32(itr->FloorId); // floorid - data << uint32(itr->Unk3); // unknown - data << uint32(itr->Unk4); // unknown - data << uint32(itr->points.size()); // POI points count - - for (std::vector::const_iterator itr2 = itr->points.begin(); itr2 != itr->points.end(); ++itr2) - { - data << int32(itr2->x); // POI point x - data << int32(itr2->y); // POI point y - } + ByteBuffer POIByteBuffer = poiWrapper->BuildQueryData(); + data.append(POIByteBuffer); } } else diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index d5042fe81a..f2fec14da4 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -31,6 +31,7 @@ #include "Player.h" #include "QuestDef.h" #include "QuestPackets.h" +#include "QueryPackets.h" #include "ScriptMgr.h" #include "World.h" #include "WorldPacket.h" @@ -238,14 +239,14 @@ void WorldSession::HandleQuestgiverQueryQuestOpcode(WorldPackets::Quest::QuestGi } } -void WorldSession::HandleQuestQueryOpcode(WorldPackets::Quest::QueryQuestInfo& packet) +void WorldSession::HandleQuestQueryOpcode(WorldPackets::Quest::QueryQuestInfo& query) { if (!_player) return; - TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUERY_QUEST_INFO QuestID = %u", packet.QuestID); + TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUEST_QUERY quest = %u", query.QuestID); - if (Quest const* quest = sObjectMgr->GetQuestTemplate(packet.QuestID)) + if (Quest const* quest = sObjectMgr->GetQuestTemplate(query.QuestID)) _player->PlayerTalkClass->SendQuestQueryResponse(quest); } diff --git a/src/server/game/Quests/QuestDef.cpp b/src/server/game/Quests/QuestDef.cpp index f9a3b1eede..c43594f485 100644 --- a/src/server/game/Quests/QuestDef.cpp +++ b/src/server/game/Quests/QuestDef.cpp @@ -20,6 +20,7 @@ #include "Field.h" #include "Log.h" #include "ObjectMgr.h" +#include "Opcodes.h" #include "Player.h" #include "QuestPackets.h" #include "QuestPools.h" @@ -418,3 +419,174 @@ uint32 Quest::RoundXPValue(uint32 xp) else return 50 * ((xp + 25) / 50); } + +void Quest::InitializeQueryData() +{ + WorldPacket queryTemp; + for (uint8 loc = LOCALE_enUS; loc < TOTAL_LOCALES; ++loc) + { + queryTemp = BuildQueryData(static_cast(loc)); + QueryData[loc] = queryTemp; + } +} + +WorldPacket Quest::BuildQueryData(LocaleConstant loc) const +{ + WorldPackets::Quest::QueryQuestInfoResponse response; + + response.Info.QuestID = GetQuestId(); + + std::string locQuestTitle = GetTitle(); + std::string locQuestDetails = GetDetails(); + std::string locQuestObjectives = GetObjectives(); + std::string locQuestAreaDescription = GetAreaDescription(); + std::string locQuestCompletedText = GetCompletedText(); + std::string locQuestGiverTextWindow = GetQuestGiverTextWindow(); + std::string locQuestGiverTargetName = GetQuestGiverTargetName(); + std::string locQuestTurnTextWindow = GetQuestTurnTextWindow(); + std::string locQuestTurnTargetName = GetQuestTurnTargetName(); + + std::string locQuestObjectiveText[QUEST_OBJECTIVES_COUNT]; + for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) + locQuestObjectiveText[i] = ObjectiveText[i]; + + if (QuestLocale const* localeData = sObjectMgr->GetQuestLocale(GetQuestId())) + { + ObjectMgr::GetLocaleString(localeData->Title, loc, locQuestTitle); + ObjectMgr::GetLocaleString(localeData->Details, loc, locQuestDetails); + ObjectMgr::GetLocaleString(localeData->Objectives, loc, locQuestObjectives); + ObjectMgr::GetLocaleString(localeData->AreaDescription, loc, locQuestAreaDescription); + ObjectMgr::GetLocaleString(localeData->CompletedText, loc, locQuestCompletedText); + ObjectMgr::GetLocaleString(localeData->QuestGiverTextWindow, loc, locQuestGiverTextWindow); + ObjectMgr::GetLocaleString(localeData->QuestGiverTargetName, loc, locQuestGiverTargetName); + ObjectMgr::GetLocaleString(localeData->QuestTurnTextWindow, loc, locQuestTurnTextWindow); + ObjectMgr::GetLocaleString(localeData->QuestTurnTargetName, loc, locQuestTurnTargetName); + + for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) + ObjectMgr::GetLocaleString(localeData->ObjectiveText[i], loc, locQuestObjectiveText[i]); + } + + response.Info.QuestType = GetQuestMethod(); + response.Info.QuestLevel = GetQuestLevel(); + response.Info.QuestMinLevel = GetMinLevel(); + response.Info.QuestSortID = GetZoneOrSort(); + response.Info.QuestInfoID = GetType(); + + response.Info.SuggestedGroupNum = GetSuggestedPlayers(); + + response.Info.RequiredFactionId[0] = GetRepObjectiveFaction(); + response.Info.RequiredFactionValue[0] = GetRepObjectiveValue(); + + response.Info.RequiredFactionId[1] = GetRepObjectiveFaction2(); + response.Info.RequiredFactionValue[1] = GetRepObjectiveValue2(); + + response.Info.RewardNextQuest = GetNextQuestInChain(); + response.Info.RewardXPDifficulty = GetXPId(); + + response.Info.RewardMoney = GetRewOrReqMoney(); + response.Info.RewardBonusMoney = GetRewMoneyMaxLevel(); + response.Info.RewardDisplaySpell = GetRewSpell(); + response.Info.RewardSpell = GetRewSpellCast(); + + response.Info.RewardHonor = GetRewHonorAddition(); + response.Info.RewardKillHonor = GetRewHonorMultiplier(); + + response.Info.StartItem = GetSrcItemId(); + response.Info.Flags = GetFlags(); + response.Info.MinimapTargetMark = GetMinimapTargetMark(); + + response.Info.RewardTitle = GetCharTitleId(); + response.Info.RequiredPlayerKills = GetPlayersSlain(); + response.Info.RewardTalents = GetBonusTalents(); + response.Info.RewardArenaPoints = GetRewArenaPoints(); + response.Info.PortraitGiver = GetQuestGiverPortrait(); + response.Info.PortraitTurnIn = GetQuestTurnInPortrait(); + + for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i) + { + response.Info.RewardItems[i] = RewardItemId[i]; + response.Info.RewardAmount[i] = RewardItemIdCount[i]; + } + + for (uint8 i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i) + { + response.Info.UnfilteredChoiceItems[i].ItemID = RewardChoiceItemId[i]; + response.Info.UnfilteredChoiceItems[i].Quantity = RewardChoiceItemCount[i]; + } + + for (uint8 i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) // reward factions ids + response.Info.RewardFactionID[i] = RewardFactionId[i]; + + for (uint8 i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) // columnid+1 QuestFactionReward.dbc? + response.Info.RewardFactionValue[i] = RewardFactionValueId[i]; + + for (uint8 i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) // unknown usage + response.Info.RewardFactionValueOverride[i] = RewardFactionValueIdOverride[i]; + + response.Info.POIContinent = GetPOIContinent(); + response.Info.POIx = GetPOIx(); + response.Info.POIy = GetPOIy(); + response.Info.POIPriority = GetPointOpt(); + + if (sWorld->getBoolConfig(CONFIG_UI_QUESTLEVELS_IN_DIALOGS)) + Quest::AddQuestLevelToTitle(locQuestTitle, GetQuestLevel()); + + response.Info.LogTitle = locQuestTitle; + response.Info.LogDescription = locQuestObjectives; + response.Info.QuestDescription = locQuestDetails; + response.Info.AreaDescription = locQuestAreaDescription; + response.Info.QuestCompletionLog = locQuestCompletedText; + + for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) + { + response.Info.RequiredNpcOrGo[i] = RequiredNpcOrGo[i]; + response.Info.RequiredNpcOrGoCount[i] = RequiredNpcOrGoCount[i]; + response.Info.ItemDrop[i] = ItemDrop[i]; + response.Info.ItemDropQuantity[i] = ItemDropQuantity[i]; + } + + for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i) + { + response.Info.RequiredItemId[i] = RequiredItemId[i]; + response.Info.RequiredItemCount[i] = RequiredItemCount[i]; + } + + response.Info.RequiredSpell = GetRequiredSpell(); + + for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) + response.Info.ObjectiveText[i] = locQuestObjectiveText[i]; + + for (uint8 i = 0; i < QUEST_REWARD_CURRENCY_COUNT; ++i) + { + response.Info.RewardCurrencyID[i] = RewardCurrencyId[i]; + response.Info.RewardCurrencyQty[i] = RewardCurrencyCount[i]; + } + + for (uint8 i = 0; i < QUEST_REQUIRED_CURRENCY_COUNT; ++i) + { + response.Info.RequiredCurrencyID[i] = RequiredCurrencyId[i]; + response.Info.RequiredCurrencyQty[i] = RequiredCurrencyCount[i]; + } + + response.Info.PortraitGiverText = locQuestGiverTextWindow; + response.Info.PortraitGiverName = locQuestGiverTargetName; + response.Info.PortraitTurnintext = locQuestTurnTextWindow; + response.Info.PortraitTurnInName = locQuestTurnTargetName; + + response.Info.AcceptedSoundKitID = GetSoundAccept(); + response.Info.CompleteSoundKitID = GetSoundTurnIn(); + + response.Write(); + response.ShrinkToFit(); + return response.Move(); +} + +void Quest::AddQuestLevelToTitle(std::string& title, int32 level) +{ + // Adds the quest level to the front of the quest title + // example: [13] Westfall Stew + + std::stringstream questTitlePretty; + questTitlePretty << "[" << level << "] " << title; + title = questTitlePretty.str(); +} diff --git a/src/server/game/Quests/QuestDef.h b/src/server/game/Quests/QuestDef.h index 7c977259df..d0491a7baf 100644 --- a/src/server/game/Quests/QuestDef.h +++ b/src/server/game/Quests/QuestDef.h @@ -26,6 +26,7 @@ #include class Player; +class WorldPacket; namespace WorldPackets { @@ -371,8 +372,13 @@ class TC_GAME_API Quest void BuildQuestRewards(WorldPackets::Quest::QuestRewards& rewards, Player* player) const; + static void AddQuestLevelToTitle(std::string& title, int32 level); + void InitializeQueryData(); + WorldPacket BuildQueryData(LocaleConstant loc) const; + std::vector DependentPreviousQuests; std::vector DependentBreadcrumbQuests; + WorldPacket QueryData[TOTAL_LOCALES]; // cached data private: diff --git a/src/server/game/Server/Packets/QueryPackets.cpp b/src/server/game/Server/Packets/QueryPackets.cpp index d9c39e56b5..1a779567ad 100644 --- a/src/server/game/Server/Packets/QueryPackets.cpp +++ b/src/server/game/Server/Packets/QueryPackets.cpp @@ -19,6 +19,88 @@ #include "Player.h" #include "QueryPackets.h" +void WorldPackets::Query::QueryCreature::Read() +{ + _worldPacket >> CreatureID; + _worldPacket >> GUID; +} + +WorldPacket const* WorldPackets::Query::QueryCreatureResponse::Write() +{ + _worldPacket << uint32(CreatureID | (Allow ? 0x00000000 : 0x80000000)); // creature entry + + if (Allow) + { + for (std::size_t i = 0; i < Stats.Name.size(); ++i) + _worldPacket << Stats.Name[i]; + + for (std::size_t i = 0; i < Stats.NameAlt.size(); ++i) + _worldPacket << Stats.NameAlt[i]; + + _worldPacket << Stats.Title; + + _worldPacket << Stats.CursorName; // "Directions" for guard, string for Icons 2.3.0 + _worldPacket.append(Stats.Flags.data(), Stats.Flags.size()); // flags + _worldPacket << uint32(Stats.CreatureType); // CreatureType.dbc + _worldPacket << uint32(Stats.CreatureFamily); // CreatureFamily.dbc + _worldPacket << uint32(Stats.Classification); // Creature Rank (elite, boss, etc) + _worldPacket.append(Stats.ProxyCreatureID.data(), Stats.ProxyCreatureID.size()); + _worldPacket.append(Stats.CreatureDisplayID.data(), Stats.CreatureDisplayID.size()); // Modelid + _worldPacket << float(Stats.HpMulti); // dmg/hp modifier + _worldPacket << float(Stats.EnergyMulti); // dmg/mana modifier + _worldPacket << uint8(Stats.Leader); + _worldPacket.append(Stats.QuestItems.data(), Stats.QuestItems.size()); + _worldPacket << uint32(Stats.CreatureMovementInfoID); // CreatureMovementInfo.dbc + _worldPacket << uint32(Stats.RequiredExpansion); + } + + return &_worldPacket; +} + +void WorldPackets::Query::QueryGameObject::Read() +{ + _worldPacket >> GameObjectID; + _worldPacket >> GUID; +} + +WorldPacket const* WorldPackets::Query::QueryGameObjectResponse::Write() +{ + _worldPacket << uint32(GameObjectID | (Allow ? 0x00000000 : 0x80000000)); + + if (Allow) + { + _worldPacket << uint32(Stats.Type); + _worldPacket << uint32(Stats.DisplayID); + + for (std::size_t i = 0; i < Stats.Name.size(); ++i) + _worldPacket << Stats.Name[i]; + + _worldPacket << Stats.IconName; // 2.0.3, string. Icon name to use instead of default icon for go's (ex: "Attack" makes sword) + _worldPacket << Stats.CastBarCaption; // 2.0.3, string. Text will appear in Cast Bar when using GO (ex: "Collecting") + _worldPacket << Stats.UnkString; // 2.0.3, string + _worldPacket.append(Stats.Data.data(), Stats.Data.size()); + _worldPacket << float(Stats.Size); // go size + _worldPacket.append(Stats.QuestItems.data(), Stats.QuestItems.size()); + _worldPacket << int32(Stats.RequiredLevel); + } + + return &_worldPacket; +} + +void WorldPackets::Query::QuestPOIQuery::Read() +{ + _worldPacket >> MissingQuestCount; + + MissingQuestPOIs.fill(0); + if (MissingQuestCount <= MAX_QUEST_LOG_SIZE) + { + for (std::size_t i = 0; i < MissingQuestCount; ++i) + _worldPacket >> MissingQuestPOIs[i]; + } + else + _worldPacket.rfinish(); +} + void WorldPackets::Query::DBQueryBulk::Read() { _worldPacket >> TableHash; diff --git a/src/server/game/Server/Packets/QueryPackets.h b/src/server/game/Server/Packets/QueryPackets.h index 2f1f2edfb5..9fd81f6b21 100644 --- a/src/server/game/Server/Packets/QueryPackets.h +++ b/src/server/game/Server/Packets/QueryPackets.h @@ -19,8 +19,11 @@ #define QueryPackets_h__ #include "Packet.h" +#include "CreatureData.h" +#include "GameObjectData.h" #include "DB2Stores.h" #include "ObjectGuid.h" +#include "QuestDef.h" #include "SharedDefines.h" class Player; @@ -29,6 +32,97 @@ namespace WorldPackets { namespace Query { + class QueryCreature final : public ClientPacket + { + public: + QueryCreature(WorldPacket&& packet) : ClientPacket(CMSG_CREATURE_QUERY, std::move(packet)) { } + + void Read() override; + + uint32 CreatureID = 0; + ObjectGuid GUID; + }; + + struct CreatureStats + { + std::array Name; + std::array NameAlt; + std::string Title; + std::string CursorName; + std::array Flags; + uint32 CreatureType = 0; + uint32 CreatureFamily = 0; + uint32 Classification = 0; + std::array ProxyCreatureID; + std::array CreatureDisplayID; + float HpMulti = 0.0f; + float EnergyMulti = 0.0f; + bool Leader = false; + std::array QuestItems; + uint32 CreatureMovementInfoID = 0; + uint32 RequiredExpansion = 0; + }; + + class QueryCreatureResponse final : public ServerPacket + { + public: + QueryCreatureResponse() : ServerPacket(SMSG_CREATURE_QUERY_RESPONSE, 100) { } + + WorldPacket const* Write() override; + + bool Allow = false; + CreatureStats Stats; + uint32 CreatureID = 0; + }; + + class QueryGameObject final : public ClientPacket + { + public: + QueryGameObject(WorldPacket&& packet) : ClientPacket(CMSG_GAMEOBJECT_QUERY, std::move(packet)) { } + + void Read() override; + + uint32 GameObjectID = 0; + ObjectGuid GUID; + }; + + struct GameObjectStats + { + std::array Name; + std::string IconName; + std::string CastBarCaption; + std::string UnkString; + uint32 Type = 0; + uint32 DisplayID = 0; + std::array Data; + float Size = 0.0f; + std::array QuestItems; + int32 RequiredLevel = 0; + }; + + class QueryGameObjectResponse final : public ServerPacket + { + public: + QueryGameObjectResponse() : ServerPacket(SMSG_GAMEOBJECT_QUERY_RESPONSE, 150) { } + + WorldPacket const* Write() override; + + uint32 GameObjectID = 0; + bool Allow = false; + GameObjectStats Stats; + }; + + class QuestPOIQuery final : public ClientPacket + { + public: + QuestPOIQuery(WorldPacket&& packet) : ClientPacket(CMSG_QUEST_POI_QUERY, std::move(packet)) { } + + void Read() override; + + uint32 MissingQuestCount = 0; + std::array MissingQuestPOIs; + }; + struct DBQueryRecord { ObjectGuid GUID; diff --git a/src/server/game/Server/Packets/QuestPackets.cpp b/src/server/game/Server/Packets/QuestPackets.cpp index a66dc5fc64..89decb649f 100644 --- a/src/server/game/Server/Packets/QuestPackets.cpp +++ b/src/server/game/Server/Packets/QuestPackets.cpp @@ -297,6 +297,138 @@ WorldPacket const* WorldPackets::Quest::QuestGiverOfferRewardMessage::Write() return &_worldPacket; } +WorldPacket const* WorldPackets::Quest::QueryQuestInfoResponse::Write() +{ + _worldPacket << uint32(Info.QuestID); + _worldPacket << int32(Info.QuestType); + _worldPacket << int32(Info.QuestLevel); + _worldPacket << int32(Info.QuestMinLevel); + _worldPacket << int32(Info.QuestSortID); + _worldPacket << int32(Info.QuestInfoID); + _worldPacket << uint32(Info.SuggestedGroupNum); + + for (uint8 i = 0; i < BG_TEAMS_COUNT; ++i) + { + _worldPacket << uint32(Info.RequiredFactionId[i]); + _worldPacket << int32(Info.RequiredFactionValue[i]); + } + + _worldPacket << int32(Info.RewardNextQuest); + _worldPacket << uint32(Info.RewardXPDifficulty); + + if ((Info.Flags & QUEST_FLAGS_HIDDEN_REWARDS) != 0) + _worldPacket << int32(0); + else + _worldPacket << int32(Info.RewardMoney); + + _worldPacket << uint32(Info.RewardBonusMoney); + _worldPacket << int32(Info.RewardDisplaySpell); + _worldPacket << int32(Info.RewardSpell); + _worldPacket << int32(Info.RewardHonor); + _worldPacket << float(Info.RewardKillHonor); + _worldPacket << uint32(Info.StartItem); + _worldPacket << uint32(Info.Flags); + _worldPacket << uint32(Info.MinimapTargetMark); + _worldPacket << uint32(Info.RewardTitle); + _worldPacket << uint32(Info.RequiredPlayerKills); + _worldPacket << uint32(Info.RewardTalents); + _worldPacket << uint32(Info.RewardArenaPoints); + _worldPacket << uint32(Info.RewardSkillLineID); + _worldPacket << uint32(Info.RewardNumSkillUps); + _worldPacket << uint32(Info.RewardReputationMask); + _worldPacket << uint32(Info.PortraitGiver); + _worldPacket << uint32(Info.PortraitTurnIn); + + if ((Info.Flags & QUEST_FLAGS_HIDDEN_REWARDS) != 0) + { + for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i) + _worldPacket << int32(0) << uint32(0); + for (uint8 i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i) + _worldPacket << int32(0) << uint32(0); + } + else + { + for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i) + { + _worldPacket << int32(Info.RewardItems[i]); + _worldPacket << uint32(Info.RewardAmount[i]); + } + + for (uint8 i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i) + { + _worldPacket << int32(Info.UnfilteredChoiceItems[i].ItemID); + _worldPacket << uint32(Info.UnfilteredChoiceItems[i].Quantity); + } + } + + for (uint8 i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) + _worldPacket << uint32(Info.RewardFactionID[i]); + + for (uint8 i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) + _worldPacket << int32(Info.RewardFactionValue[i]); + + for (uint8 i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) + _worldPacket << uint32(Info.RewardFactionValueOverride[i]); + + _worldPacket << uint32(Info.POIContinent); + _worldPacket << float(Info.POIx); + _worldPacket << float(Info.POIy); + _worldPacket << uint32(Info.POIPriority); + + _worldPacket << Info.LogTitle; + _worldPacket << Info.LogDescription; + _worldPacket << Info.QuestDescription; + _worldPacket << Info.AreaDescription; + _worldPacket << Info.QuestCompletionLog; + + for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) + { + if (Info.RequiredNpcOrGo[i] < 0) + _worldPacket << uint32((Info.RequiredNpcOrGo[i] * (-1)) | 0x80000000); // client expects gameobject template id in form (id|0x80000000) + else + _worldPacket << uint32(Info.RequiredNpcOrGo[i]); + + _worldPacket << uint32(Info.RequiredNpcOrGoCount[i]); + + _worldPacket << int32(Info.ItemDrop[i]); + _worldPacket << uint32(Info.ItemDropQuantity[i]); + } + + for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i) + { + _worldPacket << int32(Info.RequiredItemId[i]); + _worldPacket << uint32(Info.RequiredItemCount[i]); + } + + _worldPacket << uint32(Info.RequiredSpell); + + for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) + _worldPacket << Info.ObjectiveText[i]; + + for (uint32 i = 0; i < QUEST_REWARD_CURRENCY_COUNT; ++i) + { + _worldPacket << uint32(Info.RewardCurrencyID[i]); + _worldPacket << uint32(Info.RewardCurrencyQty[i]); + } + + for (uint32 i = 0; i < QUEST_REQUIRED_CURRENCY_COUNT; ++i) + { + _worldPacket << uint32(Info.RequiredCurrencyID[i]); + _worldPacket << uint32(Info.RequiredCurrencyQty[i]); + } + + _worldPacket << Info.PortraitGiverText; + _worldPacket << Info.PortraitGiverName; + _worldPacket << Info.PortraitTurnintext; + _worldPacket << Info.PortraitTurnInName; + + + _worldPacket << uint32(Info.AcceptedSoundKitID); + _worldPacket << uint32(Info.CompleteSoundKitID); + + return &_worldPacket; +} + ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Quest::QuestRewards const& questRewards) { data << uint32(questRewards.ChoiceItemCount); diff --git a/src/server/game/Server/Packets/QuestPackets.h b/src/server/game/Server/Packets/QuestPackets.h index ccd5f23b90..3a6f3fed56 100644 --- a/src/server/game/Server/Packets/QuestPackets.h +++ b/src/server/game/Server/Packets/QuestPackets.h @@ -182,6 +182,102 @@ namespace WorldPackets int32 QuestID = 0; }; + struct QuestInfoChoiceItem + { + int32 ItemID = 0; + int32 Quantity = 0; + int32 DisplayID = 0; + }; + + struct QuestInfo + { + uint32 QuestID = 0; + int32 QuestType = 0; // Accepted values: 0, 1 or 2. 0 == IsAutoComplete() (skip objectives/details) + int32 QuestLevel = 0; // may be -1, static data, in other cases must be used dynamic level: Player::GetQuestLevel (0 is not known, but assuming this is no longer valid for quest intended for client) + int32 QuestMinLevel = 0; + int32 QuestSortID = 0; // zone or sort to display in quest log + int32 QuestInfoID = 0; + uint32 SuggestedGroupNum = 0; + int32 AllowableRaces = -1; + + std::array RequiredFactionId; // shown in quest log as part of quest objective (same/opposite faction) + std::array RequiredFactionValue; // shown in quest log as part of quest objective (same/opposite faction) + + int32 RewardNextQuest = 0; // client will request this quest from NPC, if not 0 + uint32 RewardXPDifficulty = 0; // used for calculating rewarded experience + int32 RewardMoney = 0; // reward money (below max lvl) + uint32 RewardBonusMoney = 0; // used in XP calculation at client + int32 RewardDisplaySpell = 0; // reward spell, this spell will be displayed (icon) (cast if RewSpellCast == 0) + int32 RewardSpell = 0; + int32 RewardHonor = 0; + float RewardKillHonor = 0.0f; + uint32 StartItem = 0; + uint32 Flags = 0; + uint32 MinimapTargetMark = 0; + uint32 RewardTitle = 0; // new 2.4.0, player gets this title (id from CharTitles) + uint32 RequiredPlayerKills = 0; + uint32 RewardTalents = 0; + int32 RewardArenaPoints = 0; + uint32 RewardSkillLineID = 0; + uint32 RewardNumSkillUps = 0; + uint32 RewardReputationMask = 0; + uint32 PortraitGiver = 0; + uint32 PortraitTurnIn = 0; + + std::array RewardItems; + std::array RewardAmount; + std::array UnfilteredChoiceItems; + std::array RewardFactionID; + std::array RewardFactionValue; + std::array RewardFactionValueOverride; + + uint32 POIContinent = 0; + float POIx = 0.0f; + float POIy = 0.0f; + uint32 POIPriority = 0; + std::string LogTitle; + std::string LogDescription; + std::string QuestDescription; + std::string AreaDescription; + std::string QuestCompletionLog; // display in quest objectives window once all objectives are completed + + std::array RequiredNpcOrGo; // >0 Creature <0 Gameobject + std::array RequiredNpcOrGoCount; + + std::array ItemDrop; + std::array ItemDropQuantity; + + std::array RequiredItemId; + std::array RequiredItemCount; + + uint32 RequiredSpell = 0; + + std::array ObjectiveText; + + std::array RewardCurrencyID; + std::array RewardCurrencyQty; + std::array RequiredCurrencyID; + std::array RequiredCurrencyQty; + + std::string PortraitGiverText; + std::string PortraitGiverName; + std::string PortraitTurnintext; + std::string PortraitTurnInName; + + uint32 AcceptedSoundKitID = 0; + uint32 CompleteSoundKitID = 0; + }; + + class QueryQuestInfoResponse final : public ServerPacket + { + public: + QueryQuestInfoResponse() : ServerPacket(SMSG_QUEST_QUERY_RESPONSE, 2000) { } + + WorldPacket const* Write() override; + + QuestInfo Info; + }; + struct GossipText { GossipText(uint32 questID, uint32 questType, int32 questLevel, uint32 questFlags, bool repeatable, std::string questTitle) : @@ -229,13 +325,6 @@ namespace WorldPackets QuestGiverInfo QuestGiver; }; - struct QuestInfoChoiceItem - { - int32 ItemID = 0; - int32 Quantity = 0; - int32 DisplayID = 0; - }; - struct QuestRewards { int32 ChoiceItemCount = 0; diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index aa7ddc8730..d729ac765e 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -171,6 +171,9 @@ namespace WorldPackets namespace Query { + class QueryCreature; + class QueryGameObject; + class QuestPOIQuery; class DBQueryBulk; } @@ -713,9 +716,9 @@ class TC_GAME_API WorldSession void HandleQueryTimeOpcode(WorldPacket& recvPacket); - void HandleCreatureQueryOpcode(WorldPacket& recvPacket); + void HandleCreatureQueryOpcode(WorldPackets::Query::QueryCreature& query); - void HandleGameObjectQueryOpcode(WorldPacket& recvPacket); + void HandleGameObjectQueryOpcode(WorldPackets::Query::QueryGameObject& query); void HandleMoveWorldportAckOpcode(WorldPacket& recvPacket); void HandleMoveWorldportAck(); // for server-side calls @@ -1177,7 +1180,7 @@ class TC_GAME_API WorldSession void HandleReadyForAccountDataTimes(WorldPacket& recvData); void HandleQueryQuestsCompleted(WorldPacket& recvData); void HandleQuestNPCQuery(WorldPacket& recvData); - void HandleQuestPOIQuery(WorldPacket& recvData); + void HandleQuestPOIQuery(WorldPackets::Query::QuestPOIQuery& query); void HandleEjectPassenger(WorldPacket& data); void HandleEnterPlayerVehicle(WorldPacket& data); void HandleUpdateProjectilePosition(WorldPacket& recvPacket); diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 04ce85b74a..3ffcfd272e 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1598,6 +1598,9 @@ void World::LoadConfigSettings(bool reload) // Whether to use LoS from game objects m_bool_configs[CONFIG_CHECK_GOBJECT_LOS] = sConfigMgr->GetBoolDefault("CheckGameObjectLoS", true); + // Allow to cache data queries + m_bool_configs[CONFIG_CACHE_DATA_QUERIES] = sConfigMgr->GetBoolDefault("CacheDataQueries", true); + // call ScriptMgr if we're reloading the configuration if (reload) sScriptMgr->OnConfigLoad(reload); @@ -2181,6 +2184,9 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Item loot..."); sLootItemStorage->LoadStorageFromDB(); + TC_LOG_INFO("server.loading", "Initialize query data..."); + sObjectMgr->InitializeQueriesData(QUERY_DATA_ALL); + ///- Initialize game time and timers TC_LOG_INFO("server.loading", "Initialize game time and timers"); GameTime::UpdateGameTimers(); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index ba4a18d7ab..464e000312 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -185,6 +185,7 @@ enum WorldBoolConfigs CONFIG_HOTSWAP_PREFIX_CORRECTION_ENABLED, CONFIG_CHECK_GOBJECT_LOS, CONFIG_RESPAWN_DYNAMIC_ESCORTNPC, + CONFIG_CACHE_DATA_QUERIES, BOOL_CONFIG_VALUE_COUNT }; diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index fcf4922537..ef43753bc8 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -453,6 +453,7 @@ public: sObjectMgr->CheckCreatureTemplate(cInfo); } + sObjectMgr->InitializeQueriesData(QUERY_DATA_CREATURES); handler->SendGlobalGMSysMessage("Creature template reloaded."); return true; } @@ -543,6 +544,7 @@ public: { TC_LOG_INFO("misc", "Re-Loading Quest Templates..."); sObjectMgr->LoadQuests(); + sObjectMgr->InitializeQueriesData(QUERY_DATA_QUESTS); handler->SendGlobalGMSysMessage("DB table `quest_template` (quest definitions) reloaded."); /// dependent also from `gameobject` but this table not reloaded anyway @@ -734,6 +736,7 @@ public: { TC_LOG_INFO("misc", "Re-Loading Quest POI ..." ); sObjectMgr->LoadQuestPOI(); + sObjectMgr->InitializeQueriesData(QUERY_DATA_POIS); handler->SendGlobalGMSysMessage("DB Table `quest_poi` and `quest_poi_points` reloaded."); return true; } diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 04b3756a71..6cfb38a244 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -1277,6 +1277,17 @@ Account.PasswordChangeSecurity = 0 BirthdayTime = 1222964635 +# +# CacheDataQueries +# Description: Server caches data queries at startup. +# Can be disabled if not enough memory is available. +# Default: 1 - (Enabled) +# 0 - (Disabled) +# +# + +CacheDataQueries = 1 + # ###################################################################################################