mirror of
https://github.com/mangosfour/server.git
synced 2025-12-31 13:37:07 +00:00
Adjusting field names and type, and send data accordingly to avoid client crash not using auto-generated id. Due to the nature of the primary keys, two tables are truncated. Be sure to make backup if you have data you do now want to loose. In addition, speed up load times of data from quest_poi_points, thanks Hunuza for helping out.
563 lines
20 KiB
C++
563 lines
20 KiB
C++
/*
|
|
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
|
|
*
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "Common.h"
|
|
#include "Language.h"
|
|
#include "Database/DatabaseEnv.h"
|
|
#include "Database/DatabaseImpl.h"
|
|
#include "WorldPacket.h"
|
|
#include "WorldSession.h"
|
|
#include "Opcodes.h"
|
|
#include "Log.h"
|
|
#include "World.h"
|
|
#include "ObjectMgr.h"
|
|
#include "ObjectGuid.h"
|
|
#include "Player.h"
|
|
#include "UpdateMask.h"
|
|
#include "NPCHandler.h"
|
|
#include "Pet.h"
|
|
#include "MapManager.h"
|
|
|
|
void WorldSession::SendNameQueryOpcode(Player *p)
|
|
{
|
|
if(!p)
|
|
return;
|
|
// guess size
|
|
WorldPacket data( SMSG_NAME_QUERY_RESPONSE, (8+1+1+1+1+1+10) );
|
|
data << p->GetPackGUID(); // player guid
|
|
data << uint8(0); // added in 3.1; if > 1, then end of packet
|
|
data << p->GetName(); // played name
|
|
data << uint8(0); // realm name for cross realm BG usage
|
|
data << uint8(p->getRace());
|
|
data << uint8(p->getGender());
|
|
data << uint8(p->getClass());
|
|
if(DeclinedName const* names = p->GetDeclinedNames())
|
|
{
|
|
data << uint8(1); // is declined
|
|
for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
|
|
data << names->name[i];
|
|
}
|
|
else
|
|
data << uint8(0); // is not declined
|
|
|
|
SendPacket(&data);
|
|
}
|
|
|
|
void WorldSession::SendNameQueryOpcodeFromDB(uint64 guid)
|
|
{
|
|
CharacterDatabase.AsyncPQuery(&WorldSession::SendNameQueryOpcodeFromDBCallBack, GetAccountId(),
|
|
!sWorld.getConfig(CONFIG_BOOL_DECLINED_NAMES_USED) ?
|
|
// ------- Query Without Declined Names --------
|
|
// 0 1 2 3 4
|
|
"SELECT guid, name, race, gender, class "
|
|
"FROM characters WHERE guid = '%u'"
|
|
:
|
|
// --------- Query With Declined Names ---------
|
|
// 0 1 2 3 4
|
|
"SELECT characters.guid, name, race, gender, class, "
|
|
// 5 6 7 8 9
|
|
"genitive, dative, accusative, instrumental, prepositional "
|
|
"FROM characters LEFT JOIN character_declinedname ON characters.guid = character_declinedname.guid WHERE characters.guid = '%u'",
|
|
GUID_LOPART(guid));
|
|
}
|
|
|
|
void WorldSession::SendNameQueryOpcodeFromDBCallBack(QueryResult *result, uint32 accountId)
|
|
{
|
|
if(!result)
|
|
return;
|
|
|
|
WorldSession * session = sWorld.FindSession(accountId);
|
|
if(!session)
|
|
{
|
|
delete result;
|
|
return;
|
|
}
|
|
|
|
Field *fields = result->Fetch();
|
|
uint32 guid = fields[0].GetUInt32();
|
|
std::string name = fields[1].GetCppString();
|
|
uint8 pRace = 0, pGender = 0, pClass = 0;
|
|
if(name == "")
|
|
name = session->GetMangosString(LANG_NON_EXIST_CHARACTER);
|
|
else
|
|
{
|
|
pRace = fields[2].GetUInt8();
|
|
pGender = fields[3].GetUInt8();
|
|
pClass = fields[4].GetUInt8();
|
|
}
|
|
// guess size
|
|
WorldPacket data( SMSG_NAME_QUERY_RESPONSE, (8+1+1+1+1+1+1+10) );
|
|
data.appendPackGUID(MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
|
|
data << uint8(0); // added in 3.1; if > 1, then end of packet
|
|
data << name;
|
|
data << uint8(0); // realm name for cross realm BG usage
|
|
data << uint8(pRace); // race
|
|
data << uint8(pGender); // gender
|
|
data << uint8(pClass); // class
|
|
|
|
// if the first declined name field (5) is empty, the rest must be too
|
|
if(sWorld.getConfig(CONFIG_BOOL_DECLINED_NAMES_USED) && fields[5].GetCppString() != "")
|
|
{
|
|
data << uint8(1); // is declined
|
|
for(int i = 5; i < MAX_DECLINED_NAME_CASES+5; ++i)
|
|
data << fields[i].GetCppString();
|
|
}
|
|
else
|
|
data << uint8(0); // is not declined
|
|
|
|
session->SendPacket( &data );
|
|
delete result;
|
|
}
|
|
|
|
void WorldSession::HandleNameQueryOpcode( WorldPacket & recv_data )
|
|
{
|
|
uint64 guid;
|
|
|
|
recv_data >> guid;
|
|
|
|
Player *pChar = sObjectMgr.GetPlayer(guid);
|
|
|
|
if (pChar)
|
|
SendNameQueryOpcode(pChar);
|
|
else
|
|
SendNameQueryOpcodeFromDB(guid);
|
|
}
|
|
|
|
void WorldSession::HandleQueryTimeOpcode( WorldPacket & /*recv_data*/ )
|
|
{
|
|
SendQueryTimeResponse();
|
|
}
|
|
|
|
/// Only _static_ data send in this packet !!!
|
|
void WorldSession::HandleCreatureQueryOpcode( WorldPacket & recv_data )
|
|
{
|
|
uint32 entry;
|
|
recv_data >> entry;
|
|
uint64 guid;
|
|
recv_data >> guid;
|
|
|
|
CreatureInfo const *ci = ObjectMgr::GetCreatureTemplate(entry);
|
|
if (ci)
|
|
{
|
|
std::string Name, SubName;
|
|
Name = ci->Name;
|
|
SubName = ci->SubName;
|
|
|
|
int loc_idx = GetSessionDbLocaleIndex();
|
|
if (loc_idx >= 0)
|
|
{
|
|
CreatureLocale const *cl = sObjectMgr.GetCreatureLocale(entry);
|
|
if (cl)
|
|
{
|
|
if (cl->Name.size() > size_t(loc_idx) && !cl->Name[loc_idx].empty())
|
|
Name = cl->Name[loc_idx];
|
|
if (cl->SubName.size() > size_t(loc_idx) && !cl->SubName[loc_idx].empty())
|
|
SubName = cl->SubName[loc_idx];
|
|
}
|
|
}
|
|
sLog.outDetail("WORLD: CMSG_CREATURE_QUERY '%s' - Entry: %u.", ci->Name, entry);
|
|
// guess size
|
|
WorldPacket data( SMSG_CREATURE_QUERY_RESPONSE, 100 );
|
|
data << uint32(entry); // creature entry
|
|
data << Name;
|
|
data << uint8(0) << uint8(0) << uint8(0); // name2, name3, name4, always empty
|
|
data << SubName;
|
|
data << ci->IconName; // "Directions" for guard, string for Icons 2.3.0
|
|
data << uint32(ci->type_flags); // flags
|
|
data << uint32(ci->type); // CreatureType.dbc
|
|
data << uint32(ci->family); // CreatureFamily.dbc
|
|
data << uint32(ci->rank); // Creature Rank (elite, boss, etc)
|
|
data << uint32(ci->KillCredit[0]); // new in 3.1, kill credit
|
|
data << uint32(ci->KillCredit[1]); // new in 3.1, kill credit
|
|
data << uint32(ci->DisplayID_A[0]); // modelid_male1
|
|
data << uint32(ci->DisplayID_H[0]); // modelid_female1 ?
|
|
data << uint32(ci->DisplayID_A[1]); // modelid_male2 ?
|
|
data << uint32(ci->DisplayID_H[1]); // modelid_femmale2 ?
|
|
data << float(ci->unk16); // unk
|
|
data << float(ci->unk17); // unk
|
|
data << uint8(ci->RacialLeader);
|
|
for(uint32 i = 0; i < 6; ++i)
|
|
data << uint32(ci->questItems[i]); // itemId[6], quest drop
|
|
data << uint32(ci->movementId); // CreatureMovementInfo.dbc
|
|
SendPacket( &data );
|
|
sLog.outDebug( "WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE" );
|
|
}
|
|
else
|
|
{
|
|
sLog.outDebug("WORLD: CMSG_CREATURE_QUERY - NO CREATURE INFO! (GUID: %u, ENTRY: %u)",
|
|
GUID_LOPART(guid), entry);
|
|
WorldPacket data( SMSG_CREATURE_QUERY_RESPONSE, 4 );
|
|
data << uint32(entry | 0x80000000);
|
|
SendPacket( &data );
|
|
sLog.outDebug( "WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE" );
|
|
}
|
|
}
|
|
|
|
/// Only _static_ data send in this packet !!!
|
|
void WorldSession::HandleGameObjectQueryOpcode( WorldPacket & recv_data )
|
|
{
|
|
uint32 entryID;
|
|
recv_data >> entryID;
|
|
uint64 guid;
|
|
recv_data >> guid;
|
|
|
|
const GameObjectInfo *info = ObjectMgr::GetGameObjectInfo(entryID);
|
|
if(info)
|
|
{
|
|
std::string Name;
|
|
std::string IconName;
|
|
std::string CastBarCaption;
|
|
|
|
Name = info->name;
|
|
IconName = info->IconName;
|
|
CastBarCaption = info->castBarCaption;
|
|
|
|
int loc_idx = GetSessionDbLocaleIndex();
|
|
if (loc_idx >= 0)
|
|
{
|
|
GameObjectLocale const *gl = sObjectMgr.GetGameObjectLocale(entryID);
|
|
if (gl)
|
|
{
|
|
if (gl->Name.size() > size_t(loc_idx) && !gl->Name[loc_idx].empty())
|
|
Name = gl->Name[loc_idx];
|
|
if (gl->CastBarCaption.size() > size_t(loc_idx) && !gl->CastBarCaption[loc_idx].empty())
|
|
CastBarCaption = gl->CastBarCaption[loc_idx];
|
|
}
|
|
}
|
|
sLog.outDetail("WORLD: CMSG_GAMEOBJECT_QUERY '%s' - Entry: %u. ", info->name, entryID);
|
|
WorldPacket data ( SMSG_GAMEOBJECT_QUERY_RESPONSE, 150 );
|
|
data << uint32(entryID);
|
|
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, 24);
|
|
data << float(info->size); // go size
|
|
for(uint32 i = 0; i < 6; ++i)
|
|
data << uint32(info->questItems[i]); // itemId[6], quest drop
|
|
SendPacket( &data );
|
|
sLog.outDebug( "WORLD: Sent SMSG_GAMEOBJECT_QUERY_RESPONSE" );
|
|
}
|
|
else
|
|
{
|
|
sLog.outDebug( "WORLD: CMSG_GAMEOBJECT_QUERY - Missing gameobject info for (GUID: %u, ENTRY: %u)",
|
|
GUID_LOPART(guid), entryID );
|
|
WorldPacket data ( SMSG_GAMEOBJECT_QUERY_RESPONSE, 4 );
|
|
data << uint32(entryID | 0x80000000);
|
|
SendPacket( &data );
|
|
sLog.outDebug( "WORLD: Sent SMSG_GAMEOBJECT_QUERY_RESPONSE" );
|
|
}
|
|
}
|
|
|
|
void WorldSession::HandleCorpseQueryOpcode(WorldPacket & /*recv_data*/)
|
|
{
|
|
sLog.outDetail("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;
|
|
}
|
|
|
|
int32 mapid = corpse->GetMapId();
|
|
float x = corpse->GetPositionX();
|
|
float y = corpse->GetPositionY();
|
|
float z = corpse->GetPositionZ();
|
|
int32 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->entrance_map >= 0)
|
|
{
|
|
// if corpse map have entrance
|
|
if(Map const* entranceMap = sMapMgr.CreateBaseMap(corpseMapEntry->entrance_map))
|
|
{
|
|
mapid = corpseMapEntry->entrance_map;
|
|
x = corpseMapEntry->entrance_x;
|
|
y = corpseMapEntry->entrance_y;
|
|
z = entranceMap->GetHeight(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( WorldPacket & recv_data )
|
|
{
|
|
uint32 textID;
|
|
uint64 guid;
|
|
|
|
recv_data >> textID;
|
|
sLog.outDetail("WORLD: CMSG_NPC_TEXT_QUERY ID '%u'", textID);
|
|
|
|
recv_data >> guid;
|
|
_player->SetTargetGUID(guid);
|
|
|
|
GossipText const* pGossip = sObjectMgr.GetGossipText(textID);
|
|
|
|
WorldPacket data( SMSG_NPC_TEXT_UPDATE, 100 ); // guess size
|
|
data << textID;
|
|
|
|
if (!pGossip)
|
|
{
|
|
for(uint32 i = 0; i < 8; ++i)
|
|
{
|
|
data << float(0);
|
|
data << "Greetings $N";
|
|
data << "Greetings $N";
|
|
data << uint32(0);
|
|
data << uint32(0);
|
|
data << uint32(0);
|
|
data << uint32(0);
|
|
data << uint32(0);
|
|
data << uint32(0);
|
|
data << uint32(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
std::string Text_0[8], Text_1[8];
|
|
for (int i = 0; i < 8; ++i)
|
|
{
|
|
Text_0[i]=pGossip->Options[i].Text_0;
|
|
Text_1[i]=pGossip->Options[i].Text_1;
|
|
}
|
|
|
|
int loc_idx = GetSessionDbLocaleIndex();
|
|
if (loc_idx >= 0)
|
|
{
|
|
NpcTextLocale const *nl = sObjectMgr.GetNpcTextLocale(textID);
|
|
if (nl)
|
|
{
|
|
for (int i = 0; i < 8; ++i)
|
|
{
|
|
if (nl->Text_0[i].size() > size_t(loc_idx) && !nl->Text_0[i][loc_idx].empty())
|
|
Text_0[i]=nl->Text_0[i][loc_idx];
|
|
if (nl->Text_1[i].size() > size_t(loc_idx) && !nl->Text_1[i][loc_idx].empty())
|
|
Text_1[i]=nl->Text_1[i][loc_idx];
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 8; ++i)
|
|
{
|
|
data << pGossip->Options[i].Probability;
|
|
|
|
if ( Text_0[i].empty() )
|
|
data << Text_1[i];
|
|
else
|
|
data << Text_0[i];
|
|
|
|
if ( Text_1[i].empty() )
|
|
data << Text_0[i];
|
|
else
|
|
data << Text_1[i];
|
|
|
|
data << pGossip->Options[i].Language;
|
|
|
|
for(int j = 0; j < 3; ++j)
|
|
{
|
|
data << pGossip->Options[i].Emotes[j]._Delay;
|
|
data << pGossip->Options[i].Emotes[j]._Emote;
|
|
}
|
|
}
|
|
}
|
|
|
|
SendPacket( &data );
|
|
|
|
sLog.outDebug( "WORLD: Sent SMSG_NPC_TEXT_UPDATE" );
|
|
}
|
|
|
|
void WorldSession::HandlePageTextQueryOpcode( WorldPacket & recv_data )
|
|
{
|
|
sLog.outDetail("WORLD: Received CMSG_PAGE_TEXT_QUERY");
|
|
recv_data.hexlike();
|
|
|
|
uint32 pageID;
|
|
recv_data >> pageID;
|
|
recv_data.read_skip<uint64>(); // guid
|
|
|
|
while (pageID)
|
|
{
|
|
PageText const *pPage = sPageTextStore.LookupEntry<PageText>( pageID );
|
|
// guess size
|
|
WorldPacket data( SMSG_PAGE_TEXT_QUERY_RESPONSE, 50 );
|
|
data << pageID;
|
|
|
|
if (!pPage)
|
|
{
|
|
data << "Item page missing.";
|
|
data << uint32(0);
|
|
pageID = 0;
|
|
}
|
|
else
|
|
{
|
|
std::string Text = pPage->Text;
|
|
|
|
int loc_idx = GetSessionDbLocaleIndex();
|
|
if (loc_idx >= 0)
|
|
{
|
|
PageTextLocale const *pl = sObjectMgr.GetPageTextLocale(pageID);
|
|
if (pl)
|
|
{
|
|
if (pl->Text.size() > size_t(loc_idx) && !pl->Text[loc_idx].empty())
|
|
Text = pl->Text[loc_idx];
|
|
}
|
|
}
|
|
|
|
data << Text;
|
|
data << uint32(pPage->Next_Page);
|
|
pageID = pPage->Next_Page;
|
|
}
|
|
SendPacket( &data );
|
|
|
|
sLog.outDebug( "WORLD: Sent SMSG_PAGE_TEXT_QUERY_RESPONSE" );
|
|
}
|
|
}
|
|
|
|
void WorldSession::HandleCorpseMapPositionQuery( WorldPacket & recv_data )
|
|
{
|
|
sLog.outDebug( "WORLD: Recv CMSG_CORPSE_MAP_POSITION_QUERY" );
|
|
|
|
uint32 unk;
|
|
recv_data >> unk;
|
|
|
|
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::HandleQueryQuestsCompleted( WorldPacket & /*recv_data */)
|
|
{
|
|
uint32 count = 0;
|
|
|
|
WorldPacket data(SMSG_QUERY_QUESTS_COMPLETED_RESPONSE, 4+4*count);
|
|
data << uint32(count);
|
|
|
|
for(QuestStatusMap::const_iterator itr = _player->getQuestStatusMap().begin(); itr != _player->getQuestStatusMap().end(); ++itr)
|
|
{
|
|
if(itr->second.m_rewarded)
|
|
{
|
|
data << uint32(itr->first);
|
|
count++;
|
|
}
|
|
}
|
|
data.put<uint32>(0, count);
|
|
SendPacket(&data);
|
|
}
|
|
|
|
void WorldSession::HandleQuestPOIQuery(WorldPacket& recv_data)
|
|
{
|
|
uint32 count;
|
|
recv_data >> count; // quest count, max=25
|
|
|
|
if(count > MAX_QUEST_LOG_SIZE)
|
|
{
|
|
recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam
|
|
return;
|
|
}
|
|
|
|
WorldPacket data(SMSG_QUEST_POI_QUERY_RESPONSE, 4+(4+4)*count);
|
|
data << uint32(count); // count
|
|
|
|
for(uint32 i = 0; i < count; ++i)
|
|
{
|
|
uint32 questId;
|
|
recv_data >> questId; // quest id
|
|
|
|
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->PoiId); // POI index
|
|
data << int32(itr->ObjectiveIndex); // objective index
|
|
data << uint32(itr->MapId); // mapid
|
|
data << uint32(itr->MapAreaId); // world map area id
|
|
data << uint32(itr->FloorId); // floor id
|
|
data << uint32(itr->Unk3); // unknown
|
|
data << uint32(itr->Unk4); // unknown
|
|
data << uint32(itr->points.size()); // POI points count
|
|
|
|
for(std::vector<QuestPOIPoint>::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::SendQueryTimeResponse()
|
|
{
|
|
WorldPacket data(SMSG_QUERY_TIME_RESPONSE, 4+4);
|
|
data << uint32(time(NULL));
|
|
data << uint32(sWorld.GetNextDailyQuestsResetTime() - time(NULL));
|
|
SendPacket(&data);
|
|
}
|