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
20 #include "Database/DatabaseEnv.h"
21 #include "WorldPacket.h"
22 #include "SharedDefines.h"
23 #include "WorldSession.h"
27 #include "ObjectMgr.h"
30 #include "UpdateMask.h"
32 #include "MapManager.h"
33 #include "ObjectAccessor.h"
35 #include "Database/DatabaseImpl.h"
36 #include "PlayerDump.h"
37 #include "SocialMgr.h"
41 class LoginQueryHolder
: public SqlQueryHolder
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
; }
54 bool LoginQueryHolder::Initialize()
56 SetSize(MAX_PLAYER_LOGIN_QUERY
);
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
));
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
92 void HandleCharEnumCallback(QueryResult
* result
, uint32 account
)
94 WorldSession
* session
= sWorld
.FindSession(account
);
100 session
->HandleCharEnum(result
);
102 void HandlePlayerLoginCallback(QueryResult
* /*dummy*/, SqlQueryHolder
* holder
)
105 WorldSession
*session
= sWorld
.FindSession(((LoginQueryHolder
*)holder
)->GetAccountId());
111 session
->HandlePlayerLogin((LoginQueryHolder
*)holder
);
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
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
);
140 while( result
->NextRow() );
146 data
.put
<uint8
>(0, num
);
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 --------
158 "SELECT characters.guid, characters.data, characters.name, characters.position_x, characters.position_y, characters.position_z, characters.map, characters.totaltime, characters.leveltime, "
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 ---------
166 "SELECT characters.guid, characters.data, characters.name, characters.position_x, characters.position_y, characters.position_z, characters.map, characters.totaltime, characters.leveltime, "
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",
175 void WorldSession::HandleCharCreateOpcode( WorldPacket
& recv_data
)
177 CHECK_PACKET_SIZE(recv_data
,1+1+1+1+1+1+1+1+1+1);
184 // recheck with known string size
185 CHECK_PACKET_SIZE(recv_data
,(name
.size()+1)+1+1+1+1+1+1+1+1+1);
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_
);
201 case ALLIANCE
: disabled
= mask
& (1<<0); break;
202 case HORDE
: disabled
= mask
& (1<<1); break;
207 data
<< (uint8
)CHAR_CREATE_DISABLED
;
214 ChrClassesEntry
const* classEntry
= sChrClassesStore
.LookupEntry(class_
);
215 ChrRacesEntry
const* raceEntry
= sChrRacesStore
.LookupEntry(race_
);
217 if( !classEntry
|| !raceEntry
)
219 data
<< (uint8
)CHAR_CREATE_FAILED
;
221 sLog
.outError("Class: %u or Race %u not found in DBC (Wrong DBC files?) or Cheater?", class_
, race_
);
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_
);
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_
);
244 // prevent character creating with invalid name
245 if(!normalizePlayerName(name
))
247 data
<< (uint8
)CHAR_NAME_INVALID_CHARACTER
;
249 sLog
.outError("Account:[%d] but tried to Create character with empty [name] ",GetAccountId());
253 // check name limitations
254 if(!ObjectMgr::IsValidName(name
,true))
256 data
<< (uint8
)CHAR_NAME_INVALID_CHARACTER
;
261 if(GetSecurity() == SEC_PLAYER
&& objmgr
.IsReservedName(name
))
263 data
<< (uint8
)CHAR_NAME_RESERVED
;
268 if(objmgr
.GetPlayerGUIDByName(name
))
270 data
<< (uint8
)CHAR_CREATE_NAME_IN_USE
;
275 QueryResult
*resultacct
= loginDatabase
.PQuery("SELECT SUM(numchars) FROM realmcharacters WHERE acctid = '%d'", GetAccountId());
278 Field
*fields
=resultacct
->Fetch();
279 uint32 acctcharcount
= fields
[0].GetUInt32();
282 if (acctcharcount
>= sWorld
.getConfig(CONFIG_CHARACTERS_PER_ACCOUNT
))
284 data
<< (uint8
)CHAR_CREATE_ACCOUNT_LIMIT
;
290 QueryResult
*result
= CharacterDatabase
.PQuery("SELECT COUNT(guid) FROM characters WHERE account = '%d'", GetAccountId());
294 Field
*fields
=result
->Fetch();
295 charcount
= fields
[0].GetUInt8();
298 if (charcount
>= sWorld
.getConfig(CONFIG_CHARACTERS_PER_REALM
))
300 data
<< (uint8
)CHAR_CREATE_SERVER_LIMIT
;
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");
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
)
326 team
= Player::TeamForRace(race
);
330 data
<< (uint8
)CHAR_CREATE_PVP_TEAMS_VIOLATION
;
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
;
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?)
362 data
<< (uint8
)CHAR_CREATE_ERROR
;
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();
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
;
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);
395 // can't delete loaded character
396 if(objmgr
.GetPlayer(guid
))
399 uint32 accountId
= 0;
403 if(objmgr
.GetGuildByLeader(guid
))
405 WorldPacket
data(SMSG_CHAR_DELETE
, 1);
406 data
<< (uint8
)CHAR_DELETE_FAILED_GUILD_LEADER
;
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
;
420 QueryResult
*result
= CharacterDatabase
.PQuery("SELECT account,name FROM characters WHERE guid='%u'", GUID_LOPART(guid
));
423 Field
*fields
= result
->Fetch();
424 accountId
= fields
[0].GetUInt32();
425 name
= fields
[1].GetCppString();
429 // prevent deleting other players' characters using cheating tools
430 if(accountId
!= GetAccountId())
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
;
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;
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;
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();
501 data
.Initialize( SMSG_ACCOUNT_DATA_TIMES
, 128 );
502 for(int i
= 0; i
< 32; i
++)
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
513 data
.Initialize(SMSG_MOTD
, 50); // new in 2.0.1
517 std::string str_motd
= sWorld
.GetMotd();
518 std::string::size_type pos
, nextpos
;
521 while ( (nextpos
= str_motd
.find('@',pos
)) != std::string::npos
)
525 data
<< str_motd
.substr(pos
,nextpos
-pos
);
531 if (pos
<str_motd
.length())
533 data
<< str_motd
.substr(pos
);
537 data
.put(0, linecount
);
540 DEBUG_LOG( "WORLD: Sent motd (SMSG_MOTD)" );
543 if(pCurrChar
->GetGuildId() != 0)
545 Guild
* guild
= objmgr
.GetGuildById(pCurrChar
->GetGuildId());
548 data
.Initialize(SMSG_GUILD_EVENT
, (2+guild
->GetMOTD().size()+1));
549 data
<< (uint8
)GE_MOTD
;
551 data
<< guild
->GetMOTD();
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
;
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();
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());
588 data
.Initialize( SMSG_TRIGGER_CINEMATIC
,4 );
589 data
<< uint32(rEntry
->startmovie
);
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
);
599 Field
*fields
= resultGuild
->Fetch();
600 pCurrChar
->SetInGuild(fields
[0].GetUInt32());
601 pCurrChar
->SetRank(fields
[1].GetUInt32());
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());
614 pCurrChar
->TeleportTo(at
->target_mapId
, at
->target_X
, at
->target_Y
, at
->target_Z
, pCurrChar
->GetOrientation());
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
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);
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
;
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())
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());
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
)
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;
759 void WorldSession::HandleSetFactionAtWar( WorldPacket
& recv_data
)
761 CHECK_PACKET_SIZE(recv_data
,4+1);
763 DEBUG_LOG( "WORLD: Received CMSG_SET_FACTION_ATWAR" );
768 recv_data
>> repListID
;
771 FactionStateList::iterator itr
= GetPlayer()->m_factions
.find(repListID
);
772 if (itr
== GetPlayer()->m_factions
.end())
775 // always invisible or hidden faction can't change war state
776 if(itr
->second
.Flags
& (FACTION_FLAG_INVISIBLE_FORCED
|FACTION_FLAG_HIDDEN
) )
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");
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);
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);
819 void WorldSession::HandleTutorialFlag( WorldPacket
& recv_data
)
821 CHECK_PACKET_SIZE(recv_data
,4);
826 uint32 wInt
= (iFlag
/ 32);
829 //sLog.outError("CHEATER? Account:[%d] Guid[%u] tried to send wrong CMSG_TUTORIAL_FLAG", GetAccountId(),GetGUID());
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");
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");
870 recv_data
>> replistid
>> inactive
;
872 FactionStateList::iterator itr
= _player
->m_factions
.find(replistid
);
873 if (itr
== _player
->m_factions
.end())
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);
899 CHECK_PACKET_SIZE(recv_data
, 8+1);
902 recv_data
>> newname
;
904 QueryResult
*result
= CharacterDatabase
.PQuery("SELECT at_login FROM characters WHERE guid ='%u'", GUID_LOPART(guid
));
907 uint32 at_loginFlags
;
908 Field
*fields
= result
->Fetch();
909 at_loginFlags
= fields
[0].GetUInt32();
912 if (!(at_loginFlags
& AT_LOGIN_RENAME
))
914 WorldPacket
data(SMSG_CHAR_RENAME
, 1);
915 data
<< (uint8
)CHAR_CREATE_ERROR
;
922 WorldPacket
data(SMSG_CHAR_RENAME
, 1);
923 data
<< (uint8
)CHAR_CREATE_ERROR
;
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
;
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
;
945 if(!ObjectMgr::IsValidName(newname
,true))
947 WorldPacket
data(SMSG_CHAR_RENAME
, 1);
948 data
<< (uint8
)CHAR_NAME_INVALID_CHARACTER
;
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
;
962 if(objmgr
.GetPlayerGUIDByName(newname
)) // character with this name already exist
964 WorldPacket
data(SMSG_CHAR_RENAME
, 1);
965 data
<< (uint8
)CHAR_CREATE_ERROR
;
970 if(newname
== oldname
) // checked by client
972 WorldPacket
data(SMSG_CHAR_RENAME
, 1);
973 data
<< (uint8
)CHAR_NAME_FAILURE
;
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
;
994 void WorldSession::HandleDeclinedPlayerNameOpcode(WorldPacket
& recv_data
)
998 CHECK_PACKET_SIZE(recv_data
, 8);
1001 // not accept declined names for unsupported languages
1003 if(!objmgr
.GetPlayerNameByGUID(guid
, name
))
1005 WorldPacket
data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT
, 4+8);
1007 data
<< uint64(guid
);
1013 if(!Utf8toWStr(name
, wname
))
1015 WorldPacket
data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT
, 4+8);
1017 data
<< uint64(guid
);
1022 if(!isCyrillicCharacter(wname
[0])) // name already stored as only single alphabet using
1024 WorldPacket
data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT
, 4+8);
1026 data
<< uint64(guid
);
1032 DeclinedName declinedname
;
1034 CHECK_PACKET_SIZE(recv_data
, recv_data
.rpos() + 1);
1037 if(name2
!= name
) // character have different name
1039 WorldPacket
data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT
, 4+8);
1041 data
<< uint64(guid
);
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);
1054 data
<< uint64(guid
);
1060 if(!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname
, 0), declinedname
))
1062 WorldPacket
data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT
, 4+8);
1064 data
<< uint64(guid
);
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
);