/* * 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 "BattlenetAccountMgr.h" #include "CharacterPackets.h" #include "QueryPackets.h" void WorldSession::SendNameQueryOpcode(ObjectGuid guid) { Player* player = ObjectAccessor::FindConnectedPlayer(guid); CharacterInfo const* characterInfo = sWorld->GetCharacterInfo(guid); WorldPackets::Query::QueryPlayerNameResponse response; response.Player = guid; if (characterInfo) { uint32 accountId = player ? player->GetSession()->GetAccountId() : ObjectMgr::GetPlayerAccountIdByGUID(guid); uint32 bnetAccountId = player ? player->GetSession()->GetBattlenetAccountId() : Battlenet::AccountMgr::GetIdByGameAccount(accountId); response.Result = RESPONSE_SUCCESS; // name known response.Data.IsDeleted = characterInfo->IsDeleted; response.Data.AccountID = ObjectGuid::Create(accountId); response.Data.BnetAccountID = ObjectGuid::Create(bnetAccountId); response.Data.Name = characterInfo->Name; response.Data.VirtualRealmAddress = GetVirtualRealmAddress(); response.Data.Race = characterInfo->Race; response.Data.Sex = characterInfo->Sex; response.Data.ClassID = characterInfo->Class; response.Data.Level = characterInfo->Level; if (DeclinedName const* names = (player ? player->GetDeclinedNames() : nullptr)) for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i) response.Data.DeclinedNames.name[i] = names->name[i]; } else response.Result = RESPONSE_FAILURE; // name unknown SendPacket(response.Write()); } void WorldSession::HandleNameQueryOpcode(WorldPackets::Query::QueryPlayerName& packet) { SendNameQueryOpcode(packet.Player); } void WorldSession::HandleQueryTimeOpcode(WorldPacket & /*recvData*/) { SendQueryTimeResponse(); } void WorldSession::SendQueryTimeResponse() { WorldPacket data(SMSG_QUERY_TIME_RESPONSE, 4+4); data << uint32(time(NULL)); data << uint32(sWorld->GetNextDailyQuestsResetTime() - time(NULL)); SendPacket(&data); } /// 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::HandleCorpseQueryOpcode(WorldPacket& /*recvData*/) { TC_LOG_DEBUG("network", "WORLD: Received MSG_CORPSE_QUERY"); Corpse* corpse = GetPlayer()->GetCorpse(); if (!corpse) { WorldPacket data(MSG_CORPSE_QUERY, 1); data << uint8(0); // corpse not found SendPacket(&data); 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); } } } } WorldPacket data(MSG_CORPSE_QUERY, 1+(6*4)); data << uint8(1); // corpse found data << int32(mapid); data << float(x); data << float(y); data << float(z); data << int32(corpsemapid); data << uint32(0); // unknown SendPacket(&data); } 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; 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; } response.Allow = true; } SendPacket(response.Write()); 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::HandleCorpseMapPositionQuery(WorldPacket& recvData) { TC_LOG_DEBUG("network", "WORLD: Recv CMSG_CORPSE_MAP_POSITION_QUERY"); uint32 transportGuidLow; recvData >> transportGuidLow; WorldPacket data(SMSG_CORPSE_MAP_POSITION_QUERY_RESPONSE, 4+4+4+4); data << float(0); data << float(0); data << float(0); data << float(0); SendPacket(&data); } 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_QUEST_NPC_QUERY 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_NPC_QUERY_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.Locale = GetSessionDbcLocale(); response.Timestamp = sDB2Manager.GetHotfixDate(rec.RecordID, packet.TableHash); response.Data = store; } 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()); } }