/*
* Copyright (C) 2008-2015 TrinityCore
* Copyright (C) 2005-2009 MaNGOS
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
#include "Common.h"
#include "Language.h"
#include "DatabaseEnv.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Opcodes.h"
#include "Log.h"
#include "World.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "UpdateMask.h"
#include "NPCHandler.h"
#include "Pet.h"
#include "MapManager.h"
#include "CharacterPackets.h"
#include "QueryPackets.h"
void WorldSession::SendNameQueryOpcode(ObjectGuid guid)
{
Player* player = ObjectAccessor::FindConnectedPlayer(guid);
WorldPackets::Query::QueryPlayerNameResponse response;
response.Player = guid;
if (response.Data.Initialize(guid, player))
response.Result = RESPONSE_SUCCESS; // name known
else
response.Result = RESPONSE_FAILURE; // name unknown
SendPacket(response.Write());
}
void WorldSession::HandleNameQueryOpcode(WorldPackets::Query::QueryPlayerName& packet)
{
SendNameQueryOpcode(packet.Player);
}
void WorldSession::HandleQueryTimeOpcode(WorldPackets::Query::QueryTime& /*queryTime*/)
{
SendQueryTimeResponse();
}
void WorldSession::SendQueryTimeResponse()
{
WorldPackets::Query::QueryTimeResponse queryTimeResponse;
queryTimeResponse.CurrentTime = time(nullptr);
queryTimeResponse.TimeOutRequest = sWorld->GetNextDailyQuestsResetTime() - queryTimeResponse.CurrentTime;
SendPacket(queryTimeResponse.Write());
}
/// Only _static_ data is sent in this packet !!!
void WorldSession::HandleCreatureQuery(WorldPackets::Query::QueryCreature& packet)
{
WorldPackets::Query::QueryCreatureResponse response;
CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(packet.CreatureID);
response.CreatureID = packet.CreatureID;
if (creatureInfo)
{
response.Allow = true;
WorldPackets::Query::CreatureStats& stats = response.Stats;
stats.Title = creatureInfo->SubName;
stats.CursorName = creatureInfo->IconName;
stats.CreatureType = creatureInfo->type;
stats.CreatureFamily = creatureInfo->family;
stats.Classification = creatureInfo->rank;
stats.HpMulti = creatureInfo->ModHealth;
stats.EnergyMulti = creatureInfo->ModMana;
stats.Leader = creatureInfo->RacialLeader;
CreatureQuestItemList* items = sObjectMgr->GetCreatureQuestItemList(packet.CreatureID);
if (items)
for (uint32 item : *items)
stats.QuestItems.push_back(item);
stats.CreatureMovementInfoID = creatureInfo->movementId;
stats.RequiredExpansion = creatureInfo->expansionUnknown;
stats.Flags[0] = creatureInfo->type_flags;
stats.Flags[1] = creatureInfo->type_flags2;
for (uint32 i = 0; i < MAX_KILL_CREDIT; ++i)
stats.ProxyCreatureID[i] = creatureInfo->KillCredit[i];
stats.CreatureDisplayID[0] = creatureInfo->Modelid1;
stats.CreatureDisplayID[1] = creatureInfo->Modelid2;
stats.CreatureDisplayID[2] = creatureInfo->Modelid3;
stats.CreatureDisplayID[3] = creatureInfo->Modelid4;
stats.Name[0] = creatureInfo->Name;
stats.NameAlt[0] = creatureInfo->FemaleName;
}
else
response.Allow = false;
SendPacket(response.Write());
}
/// Only _static_ data is sent in this packet !!!
void WorldSession::HandleGameObjectQueryOpcode(WorldPackets::Query::QueryGameObject& packet)
{
WorldPackets::Query::QueryGameObjectResponse response;
response.GameObjectID = packet.GameObjectID;
if (GameObjectTemplate const* gameObjectInfo = sObjectMgr->GetGameObjectTemplate(packet.GameObjectID))
{
response.Allow = true;
WorldPackets::Query::GameObjectStats& stats = response.Stats;
stats.CastBarCaption = gameObjectInfo->castBarCaption;
stats.DisplayID = gameObjectInfo->displayId;
stats.IconName = gameObjectInfo->IconName;
stats.Name[0] = gameObjectInfo->name;
GameObjectQuestItemList* items = sObjectMgr->GetGameObjectQuestItemList(packet.GameObjectID);
if (items)
for (uint32 item : *items)
stats.QuestItems.push_back(item);
for (uint32 i = 0; i < MAX_GAMEOBJECT_DATA; i++)
stats.Data[i] = gameObjectInfo->raw.data[i];
stats.Size = gameObjectInfo->size;
stats.Type = gameObjectInfo->type;
stats.UnkString = gameObjectInfo->unk1;
stats.Expansion = 0;
}
else
response.Allow = false;
SendPacket(response.Write());
}
void WorldSession::HandleQueryCorpseLocation(WorldPackets::Query::QueryCorpseLocationFromClient& /*packet*/)
{
TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUERY_CORPSE_LOCATION_FROM_CLIENT");
Corpse* corpse = GetPlayer()->GetCorpse();
if (!corpse)
{
WorldPackets::Query::CorpseLocation packet;
packet.Valid = false; // corpse not found
SendPacket(packet.Write());
return;
}
uint32 mapID = corpse->GetMapId();
float x = corpse->GetPositionX();
float y = corpse->GetPositionY();
float z = corpse->GetPositionZ();
uint32 corpseMapID = mapID;
// if corpse at different map
if (mapID != _player->GetMapId())
{
// search entrance map for proper show entrance
if (MapEntry const* corpseMapEntry = sMapStore.LookupEntry(mapID))
{
if (corpseMapEntry->IsDungeon() && corpseMapEntry->CorpseMapID >= 0)
{
// if corpse map have entrance
if (Map const* entranceMap = sMapMgr->CreateBaseMap(corpseMapEntry->CorpseMapID))
{
mapID = corpseMapEntry->CorpseMapID;
x = corpseMapEntry->CorpsePos.X;
y = corpseMapEntry->CorpsePos.Y;
z = entranceMap->GetHeight(GetPlayer()->GetPhaseMask(), x, y, MAX_HEIGHT);
}
}
}
}
WorldPackets::Query::CorpseLocation packet;
packet.Valid = true;
packet.MapID = corpseMapID;
packet.ActualMapID = mapID;
packet.Position = G3D::Vector3(x, y, z);
packet.Transport = corpse->GetTransGUID();
SendPacket(packet.Write());
}
void WorldSession::HandleNpcTextQueryOpcode(WorldPackets::Query::QueryNPCText& packet)
{
TC_LOG_DEBUG("network", "WORLD: CMSG_NPC_TEXT_QUERY TextId: %u", packet.TextID);
NpcText const* npcText = sObjectMgr->GetNpcText(packet.TextID);
WorldPackets::Query::QueryNPCTextResponse response;
response.TextID = packet.TextID;
if (npcText)
{
for (uint8 i = 0; i < MAX_NPC_TEXT_OPTIONS; ++i)
{
response.Probabilities[i] = npcText->Data[i].Probability;
response.BroadcastTextID[i] = npcText->Data[i].BroadcastTextID;
if (!response.Allow && npcText->Data[i].BroadcastTextID)
response.Allow = true;
}
}
if (!response.Allow)
TC_LOG_ERROR("sql.sql", "HandleNpcTextQueryOpcode: no BroadcastTextID found for text %u in `npc_text table`", packet.TextID);
TC_LOG_DEBUG("network", "WORLD: Sent SMSG_NPC_TEXT_UPDATE");
SendPacket(response.Write());
}
/// Only _static_ data is sent in this packet !!!
void WorldSession::HandlePageTextQueryOpcode(WorldPackets::Query::QueryPageText& packet)
{
TC_LOG_DEBUG("network", "WORLD: Received CMSG_PAGE_TEXT_QUERY");
uint32 pageID = packet.PageTextID;
while (pageID)
{
PageText const* pageText = sObjectMgr->GetPageText(pageID);
WorldPackets::Query::QueryPageTextResponse response;
response.PageTextID = pageID;
if (!pageText)
{
response.Allow = false;
pageID = 0;
}
else
{
response.Allow = true;
response.Info.ID = pageID;
int loc_idx = GetSessionDbLocaleIndex();
if (loc_idx >= 0)
if (PageTextLocale const* player = sObjectMgr->GetPageTextLocale(pageID))
ObjectMgr::GetLocaleString(player->Text, loc_idx, response.Info.Text);
response.Info.NextPageID = pageText->NextPageID;
pageID = pageText->NextPageID;
}
SendPacket(response.Write());
TC_LOG_DEBUG("network", "WORLD: Sent SMSG_PAGE_TEXT_QUERY_RESPONSE");
}
}
void WorldSession::HandleQueryCorpseTransport(WorldPackets::Query::QueryCorpseTransport& packet)
{
TC_LOG_DEBUG("network", "WORLD: Recv CMSG_QUERY_CORPSE_TRANSPORT");
Corpse* corpse = _player->GetCorpse();
WorldPackets::Query::CorpseTransportQuery response;
if (!corpse || corpse->GetTransGUID().IsEmpty() || corpse->GetTransGUID() != packet.Transport)
{
response.Position = G3D::Vector3(0.0f, 0.0f, 0.0f);
response.Facing = 0.0f;
}
else
{
response.Position = G3D::Vector3(corpse->GetTransOffsetX(), corpse->GetTransOffsetY(), corpse->GetTransOffsetZ());
response.Facing = corpse->GetTransOffsetO();
}
SendPacket(response.Write());
}
void WorldSession::HandleQuestNPCQuery(WorldPacket& recvData)
{
uint32 count = recvData.ReadBits(24);
std::map> quests;
for (uint32 i = 0; i < count; ++i)
{
uint32 questId;
recvData >> questId;
if (!sObjectMgr->GetQuestTemplate(questId))
{
TC_LOG_DEBUG("network", "WORLD: Unknown quest %u in CMSG_QUERY_QUEST_COMPLETION_NPCS by %s", questId, _player->GetGUID().ToString().c_str());
continue;
}
auto creatures = sObjectMgr->GetCreatureQuestInvolvedRelationReverseBounds(questId);
for (auto it = creatures.first; it != creatures.second; ++it)
quests[questId].push_back(it->second);
auto gos = sObjectMgr->GetGOQuestInvolvedRelationReverseBounds(questId);
for (auto it = gos.first; it != gos.second; ++it)
quests[questId].push_back(it->second | 0x80000000); // GO mask
}
WorldPacket data(SMSG_QUEST_COMPLETION_NPC_RESPONSE, 3 + quests.size() * 14);
data.WriteBits(quests.size(), 23);
for (auto it = quests.begin(); it != quests.end(); ++it)
data.WriteBits(it->second.size(), 24);
for (auto it = quests.begin(); it != quests.end(); ++it)
{
data << uint32(it->first);
for (const auto& entry : it->second)
data << uint32(entry);
}
SendPacket(&data);
}
void WorldSession::HandleQuestPOIQuery(WorldPackets::Query::QuestPOIQuery& packet)
{
if (packet.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 (int32 i = 0; i < packet.MissingQuestCount; ++i)
questIds.insert(packet.MissingQuestPOIs[i]); // QuestID
WorldPackets::Query::QuestPOIQueryResponse response;
for (auto itr = questIds.begin(); itr != questIds.end(); ++itr)
{
int32 QuestID = *itr;
bool questOk = false;
uint16 questSlot = _player->FindQuestSlot(uint32(QuestID));
if (questSlot != MAX_QUEST_LOG_SIZE)
questOk = _player->GetQuestSlotQuestId(questSlot) == uint32(QuestID);
if (questOk)
{
QuestPOIVector const* poiData = sObjectMgr->GetQuestPOIVector(QuestID);
if (poiData)
{
WorldPackets::Query::QuestPOIData questPOIData;
questPOIData.QuestID = QuestID;
for (auto data = poiData->begin(); data != poiData->end(); ++data)
{
WorldPackets::Query::QuestPOIBlobData questPOIBlobData;
questPOIBlobData.BlobIndex = data->BlobIndex;
questPOIBlobData.ObjectiveIndex = data->ObjectiveIndex;
questPOIBlobData.QuestObjectiveID = data->QuestObjectiveID;
questPOIBlobData.QuestObjectID = data->QuestObjectID;
questPOIBlobData.MapID = data->MapID;
questPOIBlobData.WorldMapAreaID = data->WorldMapAreaID;
questPOIBlobData.Floor = data->Floor;
questPOIBlobData.Priority = data->Priority;
questPOIBlobData.Flags = data->Flags;
questPOIBlobData.WorldEffectID = data->WorldEffectID;
questPOIBlobData.PlayerConditionID = data->PlayerConditionID;
questPOIBlobData.UnkWoD1 = data->UnkWoD1;
for (auto points = data->points.begin(); points != data->points.end(); ++points)
{
WorldPackets::Query::QuestPOIBlobPoint questPOIBlobPoint;
questPOIBlobPoint.X = points->X;
questPOIBlobPoint.Y = points->Y;
TC_LOG_ERROR("misc", "Quest: %i BlobIndex: %i X/Y: %i/%i", QuestID, data->BlobIndex, points->X, points->Y);
questPOIBlobData.QuestPOIBlobPointStats.push_back(questPOIBlobPoint);
}
questPOIData.QuestPOIBlobDataStats.push_back(questPOIBlobData);
}
response.QuestPOIDataStats.push_back(questPOIData);
}
}
}
SendPacket(response.Write());
}
void WorldSession::HandleDBQueryBulk(WorldPackets::Query::DBQueryBulk& packet)
{
DB2StorageBase const* store = sDB2Manager.GetStorage(packet.TableHash);
if (!store)
{
TC_LOG_ERROR("network", "CMSG_DB_QUERY_BULK: %s requested unsupported unknown hotfix type: %u", GetPlayerInfo().c_str(), packet.TableHash);
return;
}
for (WorldPackets::Query::DBQueryBulk::DBQueryRecord const& rec : packet.Queries)
{
WorldPackets::Query::DBReply response;
response.TableHash = packet.TableHash;
if (store->HasRecord(rec.RecordID))
{
response.RecordID = rec.RecordID;
response.Timestamp = sDB2Manager.GetHotfixDate(rec.RecordID, packet.TableHash);
store->WriteRecord(rec.RecordID, GetSessionDbcLocale(), response.Data);
}
else
{
TC_LOG_TRACE("network", "CMSG_DB_QUERY_BULK: %s requested non-existing entry %u in datastore: %u", GetPlayerInfo().c_str(), rec.RecordID, packet.TableHash);
response.RecordID = -int32(rec.RecordID);
response.Timestamp = time(NULL);
}
SendPacket(response.Write());
}
}