Revert change from PQuery to Query for one accidentally included query.
[getmangos.git] / src / game / CharacterHandler.cpp
blob700f4f8588969a71a96bc617f8d1d5494dd5e17e
1 /*
2 * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "Common.h"
20 #include "Database/DatabaseEnv.h"
21 #include "WorldPacket.h"
22 #include "SharedDefines.h"
23 #include "WorldSession.h"
24 #include "Opcodes.h"
25 #include "Log.h"
26 #include "World.h"
27 #include "ObjectMgr.h"
28 #include "Player.h"
29 #include "Guild.h"
30 #include "UpdateMask.h"
31 #include "Auth/md5.h"
32 #include "MapManager.h"
33 #include "ObjectAccessor.h"
34 #include "Group.h"
35 #include "Database/DatabaseImpl.h"
36 #include "PlayerDump.h"
37 #include "SocialMgr.h"
38 #include "Util.h"
39 #include "Language.h"
41 class LoginQueryHolder : public SqlQueryHolder
43 private:
44 uint32 m_accountId;
45 uint64 m_guid;
46 public:
47 LoginQueryHolder(uint32 accountId, uint64 guid)
48 : m_accountId(accountId), m_guid(guid) { }
49 uint64 GetGuid() const { return m_guid; }
50 uint32 GetAccountId() const { return m_accountId; }
51 bool Initialize();
54 bool LoginQueryHolder::Initialize()
56 SetSize(MAX_PLAYER_LOGIN_QUERY);
58 bool res = true;
60 // NOTE: all fields in `characters` must be read to prevent lost character data at next save in case wrong DB structure.
61 // !!! NOTE: including unused `zone`,`online`
62 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADFROM, "SELECT guid, account, data, name, race, class, position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid));
63 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGROUP, "SELECT leaderGuid FROM group_member WHERE memberGuid ='%u'", GUID_LOPART(m_guid));
64 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES, "SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = '%u'", GUID_LOPART(m_guid));
65 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADAURAS, "SELECT caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'", GUID_LOPART(m_guid));
66 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLS, "SELECT spell,slot,active,disabled FROM character_spell WHERE guid = '%u'", GUID_LOPART(m_guid));
67 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS, "SELECT quest,status,rewarded,explored,timer,mobcount1,mobcount2,mobcount3,mobcount4,itemcount1,itemcount2,itemcount3,itemcount4 FROM character_queststatus WHERE guid = '%u'", GUID_LOPART(m_guid));
68 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS,"SELECT quest,time FROM character_queststatus_daily WHERE guid = '%u'", GUID_LOPART(m_guid));
69 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADTUTORIALS, "SELECT tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7 FROM character_tutorial WHERE account = '%u' AND realmid = '%u'", GetAccountId(), realmID);
70 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADREPUTATION, "SELECT faction,standing,flags FROM character_reputation WHERE guid = '%u'", GUID_LOPART(m_guid));
71 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADINVENTORY, "SELECT data,bag,slot,item,item_template FROM character_inventory JOIN item_instance ON character_inventory.item = item_instance.guid WHERE character_inventory.guid = '%u' ORDER BY bag,slot", GUID_LOPART(m_guid));
72 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACTIONS, "SELECT button,action,type,misc FROM character_action WHERE guid = '%u' ORDER BY button", GUID_LOPART(m_guid));
73 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMAILCOUNT, "SELECT COUNT(id) FROM mail WHERE receiver = '%u' AND (checked & 1)=0 AND deliver_time <= '" I64FMTD "'", GUID_LOPART(m_guid),(uint64)time(NULL));
74 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMAILDATE, "SELECT MIN(deliver_time) FROM mail WHERE receiver = '%u' AND (checked & 1)=0", GUID_LOPART(m_guid));
75 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSOCIALLIST, "SELECT friend,flags,note FROM character_social WHERE guid = '%u' LIMIT 255", GUID_LOPART(m_guid));
76 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADHOMEBIND, "SELECT map,zone,position_x,position_y,position_z FROM character_homebind WHERE guid = '%u'", GUID_LOPART(m_guid));
77 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS, "SELECT spell,item,time FROM character_spell_cooldown WHERE guid = '%u'", GUID_LOPART(m_guid));
78 if(sWorld.getConfig(CONFIG_DECLINED_NAMES_USED))
79 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_declinedname WHERE guid = '%u'",GUID_LOPART(m_guid));
80 // in other case still be dummy query
81 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGUILD, "SELECT guildid,rank FROM guild_member WHERE guid = '%u'", GUID_LOPART(m_guid));
83 return res;
86 // don't call WorldSession directly
87 // it may get deleted before the query callbacks get executed
88 // instead pass an account id to this handler
89 class CharacterHandler
91 public:
92 void HandleCharEnumCallback(QueryResult * result, uint32 account)
94 WorldSession * session = sWorld.FindSession(account);
95 if(!session)
97 delete result;
98 return;
100 session->HandleCharEnum(result);
102 void HandlePlayerLoginCallback(QueryResult * /*dummy*/, SqlQueryHolder * holder)
104 if (!holder) return;
105 WorldSession *session = sWorld.FindSession(((LoginQueryHolder*)holder)->GetAccountId());
106 if(!session)
108 delete holder;
109 return;
111 session->HandlePlayerLogin((LoginQueryHolder*)holder);
113 } chrHandler;
115 void WorldSession::HandleCharEnum(QueryResult * result)
117 // keys can be non cleared if player open realm list and close it by 'cancel'
118 loginDatabase.PExecute("UPDATE account SET v = '0', s = '0' WHERE id = '%u'", GetAccountId());
120 WorldPacket data(SMSG_CHAR_ENUM, 100); // we guess size
122 uint8 num = 0;
124 data << num;
126 if( result )
128 Player *plr = new Player(this);
131 uint32 guidlow = (*result)[0].GetUInt32();
132 sLog.outDetail("Loading char guid %u from account %u.",guidlow,GetAccountId());
134 if(plr->MinimalLoadFromDB( result, guidlow ))
136 plr->BuildEnumData( result, &data );
137 ++num;
140 while( result->NextRow() );
142 delete plr;
143 delete result;
146 data.put<uint8>(0, num);
148 SendPacket( &data );
151 void WorldSession::HandleCharEnumOpcode( WorldPacket & /*recv_data*/ )
153 /// get all the data necessary for loading all characters (along with their pets) on the account
154 CharacterDatabase.AsyncPQuery(&chrHandler, &CharacterHandler::HandleCharEnumCallback, GetAccountId(),
155 !sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) ?
156 // ------- Query Without Declined Names --------
157 // 0 1 2 3 4 5 6 7 8
158 "SELECT characters.guid, characters.data, characters.name, characters.position_x, characters.position_y, characters.position_z, characters.map, characters.totaltime, characters.leveltime, "
159 // 9 10 11 12
160 "characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level "
161 "FROM characters LEFT JOIN character_pet ON characters.guid=character_pet.owner AND character_pet.slot='0' "
162 "WHERE characters.account = '%u' ORDER BY characters.guid"
164 // --------- Query With Declined Names ---------
165 // 0 1 2 3 4 5 6 7 8
166 "SELECT characters.guid, characters.data, characters.name, characters.position_x, characters.position_y, characters.position_z, characters.map, characters.totaltime, characters.leveltime, "
167 // 9 10 11 12 13
168 "characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, genitive "
169 "FROM characters LEFT JOIN character_pet ON characters.guid = character_pet.owner AND character_pet.slot='0' "
170 "LEFT JOIN character_declinedname ON characters.guid = character_declinedname.guid "
171 "WHERE characters.account = '%u' ORDER BY characters.guid",
172 GetAccountId());
175 void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
177 CHECK_PACKET_SIZE(recv_data,1+1+1+1+1+1+1+1+1+1);
179 std::string name;
180 uint8 race_,class_;
182 recv_data >> name;
184 // recheck with known string size
185 CHECK_PACKET_SIZE(recv_data,(name.size()+1)+1+1+1+1+1+1+1+1+1);
187 recv_data >> race_;
188 recv_data >> class_;
190 WorldPacket data(SMSG_CHAR_CREATE, 1); // returned with diff.values in all cases
192 if(GetSecurity() == SEC_PLAYER)
194 if(uint32 mask = sWorld.getConfig(CONFIG_CHARACTERS_CREATING_DISABLED))
196 bool disabled = false;
198 uint32 team = Player::TeamForRace(race_);
199 switch(team)
201 case ALLIANCE: disabled = mask & (1<<0); break;
202 case HORDE: disabled = mask & (1<<1); break;
205 if(disabled)
207 data << (uint8)CHAR_CREATE_DISABLED;
208 SendPacket( &data );
209 return;
214 ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(class_);
215 ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(race_);
217 if( !classEntry || !raceEntry )
219 data << (uint8)CHAR_CREATE_FAILED;
220 SendPacket( &data );
221 sLog.outError("Class: %u or Race %u not found in DBC (Wrong DBC files?) or Cheater?", class_, race_);
222 return;
225 // prevent character creating Expansion race without Expansion account
226 if (raceEntry->addon > Expansion())
228 data << (uint8)CHAR_CREATE_EXPANSION;
229 sLog.outError("Not Expansion 1 account:[%d] but tried to Create character with expansion 1 race (%u)",GetAccountId(),race_);
230 SendPacket( &data );
231 return;
234 // prevent character creating Expansion class without Expansion account
235 // TODO: use possible addon field in ChrClassesEntry in next dbc version
236 if (Expansion() < 2 && class_ == CLASS_DEATH_KNIGHT)
238 data << (uint8)CHAR_CREATE_EXPANSION;
239 sLog.outError("Not Expansion 2 account:[%d] but tried to Create character with expansion 2 class (%u)",GetAccountId(),class_);
240 SendPacket( &data );
241 return;
244 // prevent character creating with invalid name
245 if(!normalizePlayerName(name))
247 data << (uint8)CHAR_NAME_INVALID_CHARACTER;
248 SendPacket( &data );
249 sLog.outError("Account:[%d] but tried to Create character with empty [name] ",GetAccountId());
250 return;
253 // check name limitations
254 if(!ObjectMgr::IsValidName(name,true))
256 data << (uint8)CHAR_NAME_INVALID_CHARACTER;
257 SendPacket( &data );
258 return;
261 if(GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(name))
263 data << (uint8)CHAR_NAME_RESERVED;
264 SendPacket( &data );
265 return;
268 if(objmgr.GetPlayerGUIDByName(name))
270 data << (uint8)CHAR_CREATE_NAME_IN_USE;
271 SendPacket( &data );
272 return;
275 QueryResult *resultacct = loginDatabase.PQuery("SELECT SUM(numchars) FROM realmcharacters WHERE acctid = '%d'", GetAccountId());
276 if ( resultacct )
278 Field *fields=resultacct->Fetch();
279 uint32 acctcharcount = fields[0].GetUInt32();
280 delete resultacct;
282 if (acctcharcount >= sWorld.getConfig(CONFIG_CHARACTERS_PER_ACCOUNT))
284 data << (uint8)CHAR_CREATE_ACCOUNT_LIMIT;
285 SendPacket( &data );
286 return;
290 QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(guid) FROM characters WHERE account = '%d'", GetAccountId());
291 uint8 charcount = 0;
292 if ( result )
294 Field *fields=result->Fetch();
295 charcount = fields[0].GetUInt8();
296 delete result;
298 if (charcount >= sWorld.getConfig(CONFIG_CHARACTERS_PER_REALM))
300 data << (uint8)CHAR_CREATE_SERVER_LIMIT;
301 SendPacket( &data );
302 return;
306 bool AllowTwoSideAccounts = !sWorld.IsPvPRealm() || sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || GetSecurity() > SEC_PLAYER;
307 uint32 skipCinematics = sWorld.getConfig(CONFIG_SKIP_CINEMATICS);
309 bool have_same_race = false;
310 if(!AllowTwoSideAccounts || skipCinematics == 1)
312 QueryResult *result2 = CharacterDatabase.PQuery("SELECT DISTINCT race FROM characters WHERE account = '%u' %s", GetAccountId(),skipCinematics == 1 ? "" : "LIMIT 1");
313 if(result2)
315 uint32 team_= Player::TeamForRace(race_);
317 Field* field = result2->Fetch();
318 uint8 race = field[0].GetUInt32();
320 // need to check team only for first character
321 // TODO: what to if account already has characters of both races?
322 if (!AllowTwoSideAccounts)
324 uint32 team=0;
325 if(race > 0)
326 team = Player::TeamForRace(race);
328 if(team != team_)
330 data << (uint8)CHAR_CREATE_PVP_TEAMS_VIOLATION;
331 SendPacket( &data );
332 delete result2;
333 return;
337 if (skipCinematics == 1)
339 // TODO: check if cinematic already shown? (already logged in?; cinematic field)
340 while (race_ != race && result2->NextRow())
342 field = result2->Fetch();
343 race = field[0].GetUInt32();
345 have_same_race = race_ == race;
347 delete result2;
351 // extract other data required for player creating
352 uint8 gender, skin, face, hairStyle, hairColor, facialHair, outfitId;
353 recv_data >> gender >> skin >> face;
354 recv_data >> hairStyle >> hairColor >> facialHair >> outfitId;
356 Player * pNewChar = new Player(this);
357 if(!pNewChar->Create( objmgr.GenerateLowGuid(HIGHGUID_PLAYER), name, race_, class_, gender, skin, face, hairStyle, hairColor, facialHair, outfitId ))
359 // Player not create (race/class problem?)
360 delete pNewChar;
362 data << (uint8)CHAR_CREATE_ERROR;
363 SendPacket( &data );
365 return;
368 if(have_same_race && skipCinematics == 1 || skipCinematics == 2)
369 pNewChar->setCinematic(1); // not show intro
371 // Player created, save it now
372 pNewChar->SaveToDB();
373 charcount+=1;
375 loginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid= '%d' AND realmid = '%d'", GetAccountId(), realmID);
376 loginDatabase.PExecute("INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (%u, %u, %u)", charcount, GetAccountId(), realmID);
378 delete pNewChar; // created only to call SaveToDB()
380 data << (uint8)CHAR_CREATE_SUCCESS;
381 SendPacket( &data );
383 std::string IP_str = GetRemoteAddress();
384 sLog.outBasic("Account: %d (IP: %s) Create Character:[%s]",GetAccountId(),IP_str.c_str(),name.c_str());
385 sLog.outChar("Account: %d (IP: %s) Create Character:[%s]",GetAccountId(),IP_str.c_str(),name.c_str());
388 void WorldSession::HandleCharDeleteOpcode( WorldPacket & recv_data )
390 CHECK_PACKET_SIZE(recv_data,8);
392 uint64 guid;
393 recv_data >> guid;
395 // can't delete loaded character
396 if(objmgr.GetPlayer(guid))
397 return;
399 uint32 accountId = 0;
400 std::string name;
402 // is guild leader
403 if(objmgr.GetGuildByLeader(guid))
405 WorldPacket data(SMSG_CHAR_DELETE, 1);
406 data << (uint8)CHAR_DELETE_FAILED_GUILD_LEADER;
407 SendPacket( &data );
408 return;
411 // is arena team captain
412 if(objmgr.GetArenaTeamByCapitan(guid))
414 WorldPacket data(SMSG_CHAR_DELETE, 1);
415 data << (uint8)CHAR_DELETE_FAILED_ARENA_CAPTAIN;
416 SendPacket( &data );
417 return;
420 QueryResult *result = CharacterDatabase.PQuery("SELECT account,name FROM characters WHERE guid='%u'", GUID_LOPART(guid));
421 if(result)
423 Field *fields = result->Fetch();
424 accountId = fields[0].GetUInt32();
425 name = fields[1].GetCppString();
426 delete result;
429 // prevent deleting other players' characters using cheating tools
430 if(accountId != GetAccountId())
431 return;
433 std::string IP_str = GetRemoteAddress();
434 sLog.outBasic("Account: %d (IP: %s) Delete Character:[%s] (guid:%u)",GetAccountId(),IP_str.c_str(),name.c_str(),GUID_LOPART(guid));
435 sLog.outChar("Account: %d (IP: %s) Delete Character:[%s] (guid: %u)",GetAccountId(),IP_str.c_str(),name.c_str(),GUID_LOPART(guid));
437 if(sLog.IsOutCharDump()) // optimize GetPlayerDump call
439 std::string dump = PlayerDumpWriter().GetDump(GUID_LOPART(guid));
440 sLog.outCharDump(dump.c_str(),GetAccountId(),GUID_LOPART(guid),name.c_str());
443 Player::DeleteFromDB(guid, GetAccountId());
445 WorldPacket data(SMSG_CHAR_DELETE, 1);
446 data << (uint8)CHAR_DELETE_SUCCESS;
447 SendPacket( &data );
450 void WorldSession::HandlePlayerLoginOpcode( WorldPacket & recv_data )
452 CHECK_PACKET_SIZE(recv_data,8);
454 m_playerLoading = true;
455 uint64 playerGuid = 0;
457 DEBUG_LOG( "WORLD: Recvd Player Logon Message" );
459 recv_data >> playerGuid;
461 LoginQueryHolder *holder = new LoginQueryHolder(GetAccountId(), playerGuid);
462 if(!holder->Initialize())
464 delete holder; // delete all unprocessed queries
465 m_playerLoading = false;
466 return;
469 CharacterDatabase.DelayQueryHolder(&chrHandler, &CharacterHandler::HandlePlayerLoginCallback, holder);
472 void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder)
474 uint64 playerGuid = holder->GetGuid();
476 Player* pCurrChar = new Player(this);
477 pCurrChar->GetMotionMaster()->Initialize();
479 // "GetAccountId()==db stored account id" checked in LoadFromDB (prevent login not own character using cheating tools)
480 if(!pCurrChar->LoadFromDB(GUID_LOPART(playerGuid), holder))
482 KickPlayer(); // disconnect client, player no set to session and it will not deleted or saved at kick
483 delete pCurrChar; // delete it manually
484 delete holder; // delete all unprocessed queries
485 m_playerLoading = false;
486 return;
489 SetPlayer(pCurrChar);
491 pCurrChar->SendDungeonDifficulty(false);
493 WorldPacket data( SMSG_LOGIN_VERIFY_WORLD, 20 );
494 data << pCurrChar->GetMapId();
495 data << pCurrChar->GetPositionX();
496 data << pCurrChar->GetPositionY();
497 data << pCurrChar->GetPositionZ();
498 data << pCurrChar->GetOrientation();
499 SendPacket(&data);
501 data.Initialize( SMSG_ACCOUNT_DATA_TIMES, 128 );
502 for(int i = 0; i < 32; i++)
503 data << uint32(0);
504 SendPacket(&data);
506 data.Initialize(SMSG_FEATURE_SYSTEM_STATUS, 2); // added in 2.2.0
507 data << uint8(2); // unknown value
508 data << uint8(0); // enable(1)/disable(0) voice chat interface in client
509 SendPacket(&data);
511 // Send MOTD
513 data.Initialize(SMSG_MOTD, 50); // new in 2.0.1
514 data << (uint32)0;
516 uint32 linecount=0;
517 std::string str_motd = sWorld.GetMotd();
518 std::string::size_type pos, nextpos;
520 pos = 0;
521 while ( (nextpos= str_motd.find('@',pos)) != std::string::npos )
523 if (nextpos != pos)
525 data << str_motd.substr(pos,nextpos-pos);
526 ++linecount;
528 pos = nextpos+1;
531 if (pos<str_motd.length())
533 data << str_motd.substr(pos);
534 ++linecount;
537 data.put(0, linecount);
539 SendPacket( &data );
540 DEBUG_LOG( "WORLD: Sent motd (SMSG_MOTD)" );
543 if(pCurrChar->GetGuildId() != 0)
545 Guild* guild = objmgr.GetGuildById(pCurrChar->GetGuildId());
546 if(guild)
548 data.Initialize(SMSG_GUILD_EVENT, (2+guild->GetMOTD().size()+1));
549 data << (uint8)GE_MOTD;
550 data << (uint8)1;
551 data << guild->GetMOTD();
552 SendPacket(&data);
553 DEBUG_LOG( "WORLD: Sent guild-motd (SMSG_GUILD_EVENT)" );
555 data.Initialize(SMSG_GUILD_EVENT, (5+10)); // we guess size
556 data<<(uint8)GE_SIGNED_ON;
557 data<<(uint8)1;
558 data<<pCurrChar->GetName();
559 data<<pCurrChar->GetGUID();
560 guild->BroadcastPacket(&data);
561 DEBUG_LOG( "WORLD: Sent guild-signed-on (SMSG_GUILD_EVENT)" );
563 // Increment online members of the guild
564 guild->IncOnlineMemberCount();
566 else
568 // remove wrong guild data
569 sLog.outError("Player %s (GUID: %u) marked as member not existed guild (id: %u), removing guild membership for player.",pCurrChar->GetName(),pCurrChar->GetGUIDLow(),pCurrChar->GetGuildId());
570 pCurrChar->SetUInt32Value(PLAYER_GUILDID,0);
571 pCurrChar->SetUInt32ValueInDB(PLAYER_GUILDID,0,pCurrChar->GetGUID());
575 if(!pCurrChar->isAlive())
576 pCurrChar->SendCorpseReclaimDelay(true);
578 pCurrChar->SendInitialPacketsBeforeAddToMap();
580 //Show cinematic at the first time that player login
581 if( !pCurrChar->getCinematic() )
583 pCurrChar->setCinematic(1);
585 ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(pCurrChar->getRace());
586 if(rEntry)
588 data.Initialize( SMSG_TRIGGER_CINEMATIC,4 );
589 data << uint32(rEntry->startmovie);
590 SendPacket( &data );
594 //QueryResult *result = CharacterDatabase.PQuery("SELECT guildid,rank FROM guild_member WHERE guid = '%u'",pCurrChar->GetGUIDLow());
595 QueryResult *resultGuild = holder->GetResult(PLAYER_LOGIN_QUERY_LOADGUILD);
597 if(resultGuild)
599 Field *fields = resultGuild->Fetch();
600 pCurrChar->SetInGuild(fields[0].GetUInt32());
601 pCurrChar->SetRank(fields[1].GetUInt32());
602 delete resultGuild;
604 else if(pCurrChar->GetGuildId()) // clear guild related fields in case wrong data about non existed membership
606 pCurrChar->SetInGuild(0);
607 pCurrChar->SetRank(0);
610 if (!MapManager::Instance().GetMap(pCurrChar->GetMapId(), pCurrChar)->Add(pCurrChar))
612 AreaTrigger const* at = objmgr.GetGoBackTrigger(pCurrChar->GetMapId());
613 if(at)
614 pCurrChar->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, pCurrChar->GetOrientation());
615 else
616 pCurrChar->TeleportTo(pCurrChar->m_homebindMapId, pCurrChar->m_homebindX, pCurrChar->m_homebindY, pCurrChar->m_homebindZ, pCurrChar->GetOrientation());
619 ObjectAccessor::Instance().AddObject(pCurrChar);
620 //sLog.outDebug("Player %s added to Map.",pCurrChar->GetName());
621 pCurrChar->GetSocial()->SendSocialList();
623 pCurrChar->SendInitialPacketsAfterAddToMap();
625 CharacterDatabase.PExecute("UPDATE characters SET online = 1 WHERE guid = '%u'", pCurrChar->GetGUIDLow());
626 loginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = '%u'", GetAccountId());
627 pCurrChar->SetInGameTime( getMSTime() );
629 // announce group about member online (must be after add to player list to receive announce to self)
630 if(Group *group = pCurrChar->GetGroup())
632 //pCurrChar->groupInfo.group->SendInit(this); // useless
633 group->SendUpdate();
636 // friend status
637 sSocialMgr.SendFriendStatus(pCurrChar, FRIEND_ONLINE, pCurrChar->GetGUIDLow(), true);
639 // Place character in world (and load zone) before some object loading
640 pCurrChar->LoadCorpse();
642 // setting Ghost+speed if dead
643 //if ( pCurrChar->m_deathState == DEAD )
644 if (pCurrChar->m_deathState != ALIVE)
646 // not blizz like, we must correctly save and load player instead...
647 if(pCurrChar->getRace() == RACE_NIGHTELF)
648 pCurrChar->CastSpell(pCurrChar, 20584, true, 0);// auras SPELL_AURA_INCREASE_SPEED(+speed in wisp form), SPELL_AURA_INCREASE_SWIM_SPEED(+swim speed in wisp form), SPELL_AURA_TRANSFORM (to wisp form)
649 pCurrChar->CastSpell(pCurrChar, 8326, true, 0); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?)
651 //pCurrChar->SetUInt32Value(UNIT_FIELD_AURA+41, 8326);
652 //pCurrChar->SetUInt32Value(UNIT_FIELD_AURA+42, 20584);
653 //pCurrChar->SetUInt32Value(UNIT_FIELD_AURAFLAGS+6, 238);
654 //pCurrChar->SetUInt32Value(UNIT_FIELD_AURALEVELS+11, 514);
655 //pCurrChar->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS+11, 65535);
656 //pCurrChar->SetUInt32Value(UNIT_FIELD_DISPLAYID, 1825);
657 //if (pCurrChar->getRace() == RACE_NIGHTELF)
659 // pCurrChar->SetSpeed(MOVE_RUN, 1.5f*1.2f, true);
660 // pCurrChar->SetSpeed(MOVE_SWIM, 1.5f*1.2f, true);
662 //else
664 // pCurrChar->SetSpeed(MOVE_RUN, 1.5f, true);
665 // pCurrChar->SetSpeed(MOVE_SWIM, 1.5f, true);
667 pCurrChar->SetMovement(MOVE_WATER_WALK);
670 if(uint32 sourceNode = pCurrChar->m_taxi.GetTaxiSource())
673 sLog.outDebug( "WORLD: Restart character %u taxi flight", pCurrChar->GetGUIDLow() );
675 uint32 MountId = objmgr.GetTaxiMount(sourceNode, pCurrChar->GetTeam());
676 uint32 path = pCurrChar->m_taxi.GetCurrentTaxiPath();
678 // search appropriate start path node
679 uint32 startNode = 0;
681 TaxiPathNodeList const& nodeList = sTaxiPathNodesByPath[path];
683 float distPrev = MAP_SIZE*MAP_SIZE;
684 float distNext =
685 (nodeList[0].x-pCurrChar->GetPositionX())*(nodeList[0].x-pCurrChar->GetPositionX())+
686 (nodeList[0].y-pCurrChar->GetPositionY())*(nodeList[0].y-pCurrChar->GetPositionY())+
687 (nodeList[0].z-pCurrChar->GetPositionZ())*(nodeList[0].z-pCurrChar->GetPositionZ());
689 for(uint32 i = 1; i < nodeList.size(); ++i)
691 TaxiPathNode const& node = nodeList[i];
692 TaxiPathNode const& prevNode = nodeList[i-1];
694 // skip nodes at another map
695 if(node.mapid != pCurrChar->GetMapId())
696 continue;
698 distPrev = distNext;
700 distNext =
701 (node.x-pCurrChar->GetPositionX())*(node.x-pCurrChar->GetPositionX())+
702 (node.y-pCurrChar->GetPositionY())*(node.y-pCurrChar->GetPositionY())+
703 (node.z-pCurrChar->GetPositionZ())*(node.z-pCurrChar->GetPositionZ());
705 float distNodes =
706 (node.x-prevNode.x)*(node.x-prevNode.x)+
707 (node.y-prevNode.y)*(node.y-prevNode.y)+
708 (node.z-prevNode.z)*(node.z-prevNode.z);
710 if(distNext + distPrev < distNodes)
712 startNode = i;
713 break;
717 SendDoFlight( MountId, path, startNode );
720 // Load pet if any and player is alive and not in taxi flight
721 if(pCurrChar->isAlive() && pCurrChar->m_taxi.GetTaxiSource()==0)
722 pCurrChar->LoadPet();
724 // Set FFA PvP for non GM in non-rest mode
725 if(sWorld.IsFFAPvPRealm() && !pCurrChar->isGameMaster() && !pCurrChar->HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_RESTING) )
726 pCurrChar->SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP);
728 if(pCurrChar->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP))
729 pCurrChar->SetContestedPvP();
731 // Apply at_login requests
732 if(pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_SPELLS))
734 pCurrChar->resetSpells();
735 SendNotification(LANG_RESET_SPELLS);
738 if(pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_TALENTS))
740 pCurrChar->resetTalents(true);
741 SendNotification(LANG_RESET_TALENTS);
744 // show time before shutdown if shutdown planned.
745 if(sWorld.IsShutdowning())
746 sWorld.ShutdownMsg(true,pCurrChar);
748 if(pCurrChar->isGameMaster())
749 SendNotification(LANG_GM_ON);
751 std::string IP_str = GetRemoteAddress();
752 sLog.outChar("Account: %d (IP: %s) Login Character:[%s] (guid:%u)",
753 GetAccountId(),IP_str.c_str(),pCurrChar->GetName() ,pCurrChar->GetGUIDLow());
755 m_playerLoading = false;
756 delete holder;
759 void WorldSession::HandleSetFactionAtWar( WorldPacket & recv_data )
761 CHECK_PACKET_SIZE(recv_data,4+1);
763 DEBUG_LOG( "WORLD: Received CMSG_SET_FACTION_ATWAR" );
765 uint32 repListID;
766 uint8 flag;
768 recv_data >> repListID;
769 recv_data >> flag;
771 FactionStateList::iterator itr = GetPlayer()->m_factions.find(repListID);
772 if (itr == GetPlayer()->m_factions.end())
773 return;
775 // always invisible or hidden faction can't change war state
776 if(itr->second.Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN) )
777 return;
779 GetPlayer()->SetFactionAtWar(&itr->second,flag);
782 //I think this function is never used :/ I dunno, but i guess this opcode not exists
783 void WorldSession::HandleSetFactionCheat( WorldPacket & /*recv_data*/ )
785 //CHECK_PACKET_SIZE(recv_data,4+4);
787 //sLog.outDebug("WORLD SESSION: HandleSetFactionCheat");
789 uint32 FactionID;
790 uint32 Standing;
792 recv_data >> FactionID;
793 recv_data >> Standing;
795 std::list<struct Factions>::iterator itr;
797 for(itr = GetPlayer()->factions.begin(); itr != GetPlayer()->factions.end(); ++itr)
799 if(itr->ReputationListID == FactionID)
801 itr->Standing += Standing;
802 itr->Flags = (itr->Flags | 1);
803 break;
807 GetPlayer()->UpdateReputation();
810 void WorldSession::HandleMeetingStoneInfo( WorldPacket & /*recv_data*/ )
812 DEBUG_LOG( "WORLD: Received CMSG_MEETING_STONE_INFO" );
814 WorldPacket data(SMSG_MEETINGSTONE_SETQUEUE, 5);
815 data << uint32(0) << uint8(6);
816 SendPacket(&data);
819 void WorldSession::HandleTutorialFlag( WorldPacket & recv_data )
821 CHECK_PACKET_SIZE(recv_data,4);
823 uint32 iFlag;
824 recv_data >> iFlag;
826 uint32 wInt = (iFlag / 32);
827 if (wInt >= 8)
829 //sLog.outError("CHEATER? Account:[%d] Guid[%u] tried to send wrong CMSG_TUTORIAL_FLAG", GetAccountId(),GetGUID());
830 return;
832 uint32 rInt = (iFlag % 32);
834 uint32 tutflag = GetPlayer()->GetTutorialInt( wInt );
835 tutflag |= (1 << rInt);
836 GetPlayer()->SetTutorialInt( wInt, tutflag );
838 //sLog.outDebug("Received Tutorial Flag Set {%u}.", iFlag);
841 void WorldSession::HandleTutorialClear( WorldPacket & /*recv_data*/ )
843 for ( uint32 iI = 0; iI < 8; iI++)
844 GetPlayer()->SetTutorialInt( iI, 0xFFFFFFFF );
847 void WorldSession::HandleTutorialReset( WorldPacket & /*recv_data*/ )
849 for ( uint32 iI = 0; iI < 8; iI++)
850 GetPlayer()->SetTutorialInt( iI, 0x00000000 );
853 void WorldSession::HandleSetWatchedFactionIndexOpcode(WorldPacket & recv_data)
855 CHECK_PACKET_SIZE(recv_data,4);
857 DEBUG_LOG("WORLD: Received CMSG_SET_WATCHED_FACTION");
858 uint32 fact;
859 recv_data >> fact;
860 GetPlayer()->SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fact);
863 void WorldSession::HandleSetWatchedFactionInactiveOpcode(WorldPacket & recv_data)
865 CHECK_PACKET_SIZE(recv_data,4+1);
867 DEBUG_LOG("WORLD: Received CMSG_SET_FACTION_INACTIVE");
868 uint32 replistid;
869 uint8 inactive;
870 recv_data >> replistid >> inactive;
872 FactionStateList::iterator itr = _player->m_factions.find(replistid);
873 if (itr == _player->m_factions.end())
874 return;
876 _player->SetFactionInactive(&itr->second, inactive);
879 void WorldSession::HandleToggleHelmOpcode( WorldPacket & /*recv_data*/ )
881 DEBUG_LOG("CMSG_TOGGLE_HELM for %s", _player->GetName());
882 _player->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM);
885 void WorldSession::HandleToggleCloakOpcode( WorldPacket & /*recv_data*/ )
887 DEBUG_LOG("CMSG_TOGGLE_CLOAK for %s", _player->GetName());
888 _player->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK);
891 void WorldSession::HandleChangePlayerNameOpcode(WorldPacket& recv_data)
893 CHECK_PACKET_SIZE(recv_data,8+1);
895 uint64 guid;
896 std::string newname;
897 std::string oldname;
899 CHECK_PACKET_SIZE(recv_data, 8+1);
901 recv_data >> guid;
902 recv_data >> newname;
904 QueryResult *result = CharacterDatabase.PQuery("SELECT at_login FROM characters WHERE guid ='%u'", GUID_LOPART(guid));
905 if (result)
907 uint32 at_loginFlags;
908 Field *fields = result->Fetch();
909 at_loginFlags = fields[0].GetUInt32();
910 delete result;
912 if (!(at_loginFlags & AT_LOGIN_RENAME))
914 WorldPacket data(SMSG_CHAR_RENAME, 1);
915 data << (uint8)CHAR_CREATE_ERROR;
916 SendPacket( &data );
917 return;
920 else
922 WorldPacket data(SMSG_CHAR_RENAME, 1);
923 data << (uint8)CHAR_CREATE_ERROR;
924 SendPacket( &data );
925 return;
928 if(!objmgr.GetPlayerNameByGUID(guid, oldname)) // character not exist, because we have no name for this guid
930 WorldPacket data(SMSG_CHAR_RENAME, 1);
931 data << (uint8)CHAR_LOGIN_NO_CHARACTER;
932 SendPacket( &data );
933 return;
936 // prevent character rename to invalid name
937 if(!normalizePlayerName(newname))
939 WorldPacket data(SMSG_CHAR_RENAME, 1);
940 data << (uint8)CHAR_NAME_NO_NAME;
941 SendPacket( &data );
942 return;
945 if(!ObjectMgr::IsValidName(newname,true))
947 WorldPacket data(SMSG_CHAR_RENAME, 1);
948 data << (uint8)CHAR_NAME_INVALID_CHARACTER;
949 SendPacket( &data );
950 return;
953 // check name limitations
954 if(GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname))
956 WorldPacket data(SMSG_CHAR_RENAME, 1);
957 data << (uint8)CHAR_NAME_RESERVED;
958 SendPacket( &data );
959 return;
962 if(objmgr.GetPlayerGUIDByName(newname)) // character with this name already exist
964 WorldPacket data(SMSG_CHAR_RENAME, 1);
965 data << (uint8)CHAR_CREATE_ERROR;
966 SendPacket( &data );
967 return;
970 if(newname == oldname) // checked by client
972 WorldPacket data(SMSG_CHAR_RENAME, 1);
973 data << (uint8)CHAR_NAME_FAILURE;
974 SendPacket( &data );
975 return;
978 // we have to check character at_login_flag & AT_LOGIN_RENAME also (fake packets hehe)
980 CharacterDatabase.escape_string(newname);
981 CharacterDatabase.PExecute("UPDATE characters set name = '%s', at_login = at_login & ~ %u WHERE guid ='%u'", newname.c_str(), uint32(AT_LOGIN_RENAME),GUID_LOPART(guid));
982 CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid ='%u'", GUID_LOPART(guid));
984 std::string IP_str = GetRemoteAddress();
985 sLog.outChar("Account: %d (IP: %s) Character:[%s] (guid:%u) Changed name to: %s",GetAccountId(),IP_str.c_str(),oldname.c_str(),GUID_LOPART(guid),newname.c_str());
987 WorldPacket data(SMSG_CHAR_RENAME,1+8+(newname.size()+1));
988 data << (uint8)RESPONSE_SUCCESS;
989 data << guid;
990 data << newname;
991 SendPacket(&data);
994 void WorldSession::HandleDeclinedPlayerNameOpcode(WorldPacket& recv_data)
996 uint64 guid;
998 CHECK_PACKET_SIZE(recv_data, 8);
999 recv_data >> guid;
1001 // not accept declined names for unsupported languages
1002 std::string name;
1003 if(!objmgr.GetPlayerNameByGUID(guid, name))
1005 WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8);
1006 data << uint32(1);
1007 data << uint64(guid);
1008 SendPacket(&data);
1009 return;
1012 std::wstring wname;
1013 if(!Utf8toWStr(name, wname))
1015 WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8);
1016 data << uint32(1);
1017 data << uint64(guid);
1018 SendPacket(&data);
1019 return;
1022 if(!isCyrillicCharacter(wname[0])) // name already stored as only single alphabet using
1024 WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8);
1025 data << uint32(1);
1026 data << uint64(guid);
1027 SendPacket(&data);
1028 return;
1031 std::string name2;
1032 DeclinedName declinedname;
1034 CHECK_PACKET_SIZE(recv_data, recv_data.rpos() + 1);
1035 recv_data >> name2;
1037 if(name2 != name) // character have different name
1039 WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8);
1040 data << uint32(1);
1041 data << uint64(guid);
1042 SendPacket(&data);
1043 return;
1046 for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
1048 CHECK_PACKET_SIZE(recv_data, recv_data.rpos() + 1);
1049 recv_data >> declinedname.name[i];
1050 if(!normalizePlayerName(declinedname.name[i]))
1052 WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8);
1053 data << uint32(1);
1054 data << uint64(guid);
1055 SendPacket(&data);
1056 return;
1060 if(!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname, 0), declinedname))
1062 WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8);
1063 data << uint32(1);
1064 data << uint64(guid);
1065 SendPacket(&data);
1066 return;
1069 for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
1070 CharacterDatabase.escape_string(declinedname.name[i]);
1072 CharacterDatabase.BeginTransaction();
1073 CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid = '%u'", GUID_LOPART(guid));
1074 CharacterDatabase.PExecute("INSERT INTO character_declinedname (guid, genitive, dative, accusative, instrumental, prepositional) VALUES ('%u','%s','%s','%s','%s','%s')",
1075 GUID_LOPART(guid), declinedname.name[0].c_str(), declinedname.name[1].c_str(), declinedname.name[2].c_str(), declinedname.name[3].c_str(), declinedname.name[4].c_str());
1076 CharacterDatabase.CommitTransaction();
1078 WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8);
1079 data << uint32(0); // OK
1080 data << uint64(guid);
1081 SendPacket(&data);