/*
* 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;
for (uint8 i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i)
if (creatureInfo->questItems[i])
stats.QuestItems.push_back(creatureInfo->questItems[i]);
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;
for (uint8 i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; i++)
if (gameObjectInfo->questItems[i])
stats.QuestItems.push_back(gameObjectInfo->questItems[i]);
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);
GossipText const* gossip = sObjectMgr->GetGossipText(packet.TextID);
WorldPackets::Query::QueryNPCTextResponse response;
response.TextID = packet.TextID;
bool hasText = false;
if (gossip)
{
for (uint8 i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; ++i)
{
response.Probabilities[i] = gossip->Options[i].Probability;
response.BroadcastTextID[i] = gossip->Options[i].BroadcastTextID;
if (!hasText && gossip->Options[i].BroadcastTextID)
hasText = true;
}
response.Allow = true;
}
if (hasText)
SendPacket(response.Write());
else
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");
}
/// 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(WorldPacket& recvData)
{
uint32 count;
recvData >> count; // quest count, max=25
if (count > MAX_QUEST_LOG_SIZE)
{
recvData.rfinish();
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
WorldPacket data(SMSG_QUEST_POI_QUERY_RESPONSE, 4 + (4 + 4)*questIds.size());
data << uint32(questIds.size()); // count
for (auto itr = questIds.begin(); itr != questIds.end(); ++itr)
{
uint32 questId = *itr;
bool questOk = false;
uint16 questSlot = _player->FindQuestSlot(questId);
if (questSlot != MAX_QUEST_LOG_SIZE)
questOk =_player->GetQuestSlotQuestId(questSlot) == questId;
if (questOk)
{
QuestPOIVector const* POI = sObjectMgr->GetQuestPOIVector(questId);
if (POI)
{
data << uint32(questId); // quest ID
data << uint32(POI->size()); // POI count
for (QuestPOIVector::const_iterator itr = POI->begin(); itr != POI->end(); ++itr)
{
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
}
}
}
else
{
data << uint32(questId); // quest ID
data << uint32(0); // POI count
}
}
else
{
data << uint32(questId); // quest ID
data << uint32(0); // POI count
}
}
SendPacket(&data);
}
void WorldSession::HandleDBQueryBulk(WorldPackets::Query::DBQueryBulk& packet)
{
DB2StorageBase const* store = sDB2Manager.GetStorage(packet.TableHash);
if (!store)
{
TC_LOG_ERROR("network", "CMSG_DB_QUERY_BULK: Received unknown hotfix type: %u", 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_ERROR("network", "CMSG_DB_QUERY_BULK: Entry %u does not exist in datastore: %u", rec.RecordID, packet.TableHash);
response.RecordID = -int32(rec.RecordID);
response.Timestamp = time(NULL);
}
SendPacket(response.Write());
}
}