2 * Copyright (C) 2005-2010 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"
28 #include "ObjectDefines.h"
31 #include "UpdateMask.h"
33 #include "ObjectAccessor.h"
35 #include "Database/DatabaseImpl.h"
36 #include "PlayerDump.h"
37 #include "SocialMgr.h"
39 #include "ArenaTeam.h"
42 // config option SkipCinematics supported values
43 enum CinematicsSkipMode
45 CINEMATICS_SKIP_NONE
= 0,
46 CINEMATICS_SKIP_SAME_RACE
= 1,
47 CINEMATICS_SKIP_ALL
= 2,
50 class LoginQueryHolder
: public SqlQueryHolder
56 LoginQueryHolder(uint32 accountId
, uint64 guid
)
57 : m_accountId(accountId
), m_guid(guid
) { }
58 uint64
GetGuid() const { return m_guid
; }
59 uint32
GetAccountId() const { return m_accountId
; }
63 bool LoginQueryHolder::Initialize()
65 SetSize(MAX_PLAYER_LOGIN_QUERY
);
69 // NOTE: all fields in `characters` must be read to prevent lost character data at next save in case wrong DB structure.
70 // !!! NOTE: including unused `zone`,`online`
71 res
&= SetPQuery(PLAYER_LOGIN_QUERY_LOADFROM
, "SELECT guid, account, data, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags, 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, arena_pending_points FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid
));
72 res
&= SetPQuery(PLAYER_LOGIN_QUERY_LOADGROUP
, "SELECT leaderGuid FROM group_member WHERE memberGuid ='%u'", GUID_LOPART(m_guid
));
73 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
));
74 res
&= SetPQuery(PLAYER_LOGIN_QUERY_LOADAURAS
, "SELECT caster_guid,spell,effect_index,stackcount,amount,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'", GUID_LOPART(m_guid
));
75 res
&= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLS
, "SELECT spell,active,disabled FROM character_spell WHERE guid = '%u'", GUID_LOPART(m_guid
));
76 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
));
77 res
&= SetPQuery(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS
,"SELECT quest,time FROM character_queststatus_daily WHERE guid = '%u'", GUID_LOPART(m_guid
));
78 res
&= SetPQuery(PLAYER_LOGIN_QUERY_LOADREPUTATION
, "SELECT faction,standing,flags FROM character_reputation WHERE guid = '%u'", GUID_LOPART(m_guid
));
79 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
));
80 res
&= SetPQuery(PLAYER_LOGIN_QUERY_LOADACTIONS
, "SELECT button,action,type FROM character_action WHERE guid = '%u' ORDER BY button", GUID_LOPART(m_guid
));
81 res
&= SetPQuery(PLAYER_LOGIN_QUERY_LOADMAILCOUNT
, "SELECT COUNT(id) FROM mail WHERE receiver = '%u' AND (checked & 1)=0 AND deliver_time <= '" UI64FMTD
"'", GUID_LOPART(m_guid
), (uint64
)time(NULL
));
82 res
&= SetPQuery(PLAYER_LOGIN_QUERY_LOADMAILDATE
, "SELECT MIN(deliver_time) FROM mail WHERE receiver = '%u' AND (checked & 1)=0", GUID_LOPART(m_guid
));
83 res
&= SetPQuery(PLAYER_LOGIN_QUERY_LOADSOCIALLIST
, "SELECT friend,flags,note FROM character_social WHERE guid = '%u' LIMIT 255", GUID_LOPART(m_guid
));
84 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
));
85 res
&= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS
, "SELECT spell,item,time FROM character_spell_cooldown WHERE guid = '%u'", GUID_LOPART(m_guid
));
86 if(sWorld
.getConfig(CONFIG_DECLINED_NAMES_USED
))
87 res
&= SetPQuery(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES
, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_declinedname WHERE guid = '%u'", GUID_LOPART(m_guid
));
88 // in other case still be dummy query
89 res
&= SetPQuery(PLAYER_LOGIN_QUERY_LOADGUILD
, "SELECT guildid,rank FROM guild_member WHERE guid = '%u'", GUID_LOPART(m_guid
));
90 res
&= SetPQuery(PLAYER_LOGIN_QUERY_LOADARENAINFO
, "SELECT arenateamid, played_week, played_season, wons_season, personal_rating FROM arena_team_member WHERE guid='%u'", GUID_LOPART(m_guid
));
91 res
&= SetPQuery(PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS
, "SELECT achievement, date FROM character_achievement WHERE guid = '%u'", GUID_LOPART(m_guid
));
92 res
&= SetPQuery(PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS
,"SELECT criteria, counter, date FROM character_achievement_progress WHERE guid = '%u'", GUID_LOPART(m_guid
));
93 res
&= SetPQuery(PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS
, "SELECT setguid, setindex, name, iconname, item0, item1, item2, item3, item4, item5, item6, item7, item8, item9, item10, item11, item12, item13, item14, item15, item16, item17, item18 FROM character_equipmentsets WHERE guid = '%u' ORDER BY setindex", GUID_LOPART(m_guid
));
94 res
&= SetPQuery(PLAYER_LOGIN_QUERY_LOADBGDATA
, "SELECT instance_id, team, join_x, join_y, join_z, join_o, join_map, taxi_start, taxi_end, mount_spell FROM character_battleground_data WHERE guid = '%u'", GUID_LOPART(m_guid
));
95 res
&= SetPQuery(PLAYER_LOGIN_QUERY_LOADACCOUNTDATA
, "SELECT type, time, data FROM character_account_data WHERE guid='%u'", GUID_LOPART(m_guid
));
96 res
&= SetPQuery(PLAYER_LOGIN_QUERY_LOADSKILLS
, "SELECT skill, value, max FROM character_skills WHERE guid = '%u'", GUID_LOPART(m_guid
));
101 // don't call WorldSession directly
102 // it may get deleted before the query callbacks get executed
103 // instead pass an account id to this handler
104 class CharacterHandler
107 void HandleCharEnumCallback(QueryResult
* result
, uint32 account
)
109 WorldSession
* session
= sWorld
.FindSession(account
);
115 session
->HandleCharEnum(result
);
117 void HandlePlayerLoginCallback(QueryResult
* /*dummy*/, SqlQueryHolder
* holder
)
120 WorldSession
*session
= sWorld
.FindSession(((LoginQueryHolder
*)holder
)->GetAccountId());
126 session
->HandlePlayerLogin((LoginQueryHolder
*)holder
);
130 void WorldSession::HandleCharEnum(QueryResult
* result
)
132 WorldPacket
data(SMSG_CHAR_ENUM
, 100); // we guess size
142 uint32 guidlow
= (*result
)[0].GetUInt32();
143 sLog
.outDetail("Loading char guid %u from account %u.", guidlow
, GetAccountId());
144 if(Player::BuildEnumData(result
, &data
))
147 while( result
->NextRow() );
152 data
.put
<uint8
>(0, num
);
157 void WorldSession::HandleCharEnumOpcode( WorldPacket
& /*recv_data*/ )
159 /// get all the data necessary for loading all characters (along with their pets) on the account
160 CharacterDatabase
.AsyncPQuery(&chrHandler
, &CharacterHandler::HandleCharEnumCallback
, GetAccountId(),
161 !sWorld
.getConfig(CONFIG_DECLINED_NAMES_USED
) ?
162 // ------- Query Without Declined Names --------
164 "SELECT characters.guid, characters.name, characters.race, characters.class, characters.gender, characters.playerBytes, characters.playerBytes2, characters.level, "
165 // 8 9 10 11 12 13 14
166 "characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z, guild_member.guildid, characters.playerFlags, "
168 "characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.data "
169 "FROM characters LEFT JOIN character_pet ON characters.guid=character_pet.owner AND character_pet.slot='%u' "
170 "LEFT JOIN guild_member ON characters.guid = guild_member.guid "
171 "WHERE characters.account = '%u' ORDER BY characters.guid"
173 // --------- Query With Declined Names ---------
175 "SELECT characters.guid, characters.name, characters.race, characters.class, characters.gender, characters.playerBytes, characters.playerBytes2, characters.level, "
176 // 8 9 10 11 12 13 14
177 "characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z, guild_member.guildid, characters.playerFlags, "
179 "characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.data, character_declinedname.genitive "
180 "FROM characters LEFT JOIN character_pet ON characters.guid = character_pet.owner AND character_pet.slot='%u' "
181 "LEFT JOIN character_declinedname ON characters.guid = character_declinedname.guid "
182 "LEFT JOIN guild_member ON characters.guid = guild_member.guid "
183 "WHERE characters.account = '%u' ORDER BY characters.guid",
184 PET_SAVE_AS_CURRENT
, GetAccountId());
187 void WorldSession::HandleCharCreateOpcode( WorldPacket
& recv_data
)
197 WorldPacket
data(SMSG_CHAR_CREATE
, 1); // returned with diff.values in all cases
199 if(GetSecurity() == SEC_PLAYER
)
201 if(uint32 mask
= sWorld
.getConfig(CONFIG_CHARACTERS_CREATING_DISABLED
))
203 bool disabled
= false;
205 uint32 team
= Player::TeamForRace(race_
);
208 case ALLIANCE
: disabled
= mask
& (1 << 0); break;
209 case HORDE
: disabled
= mask
& (1 << 1); break;
214 data
<< (uint8
)CHAR_CREATE_DISABLED
;
221 ChrClassesEntry
const* classEntry
= sChrClassesStore
.LookupEntry(class_
);
222 ChrRacesEntry
const* raceEntry
= sChrRacesStore
.LookupEntry(race_
);
224 if( !classEntry
|| !raceEntry
)
226 data
<< (uint8
)CHAR_CREATE_FAILED
;
228 sLog
.outError("Class: %u or Race %u not found in DBC (Wrong DBC files?) or Cheater?", class_
, race_
);
232 // prevent character creating Expansion race without Expansion account
233 if (raceEntry
->expansion
> Expansion())
235 data
<< (uint8
)CHAR_CREATE_EXPANSION
;
236 sLog
.outError("Expansion %u account:[%d] tried to Create character with expansion %u race (%u)", Expansion(), GetAccountId(), raceEntry
->expansion
, race_
);
241 // prevent character creating Expansion class without Expansion account
242 if (classEntry
->expansion
> Expansion())
244 data
<< (uint8
)CHAR_CREATE_EXPANSION_CLASS
;
245 sLog
.outError("Expansion %u account:[%d] tried to Create character with expansion %u class (%u)", Expansion(), GetAccountId(), classEntry
->expansion
, class_
);
250 // prevent character creating with invalid name
251 if (!normalizePlayerName(name
))
253 data
<< (uint8
)CHAR_NAME_NO_NAME
;
255 sLog
.outError("Account:[%d] but tried to Create character with empty [name]", GetAccountId());
259 // check name limitations
260 uint8 res
= ObjectMgr::CheckPlayerName(name
, true);
261 if (res
!= CHAR_NAME_SUCCESS
)
268 if (GetSecurity() == SEC_PLAYER
&& sObjectMgr
.IsReservedName(name
))
270 data
<< (uint8
)CHAR_NAME_RESERVED
;
275 if (sObjectMgr
.GetPlayerGUIDByName(name
))
277 data
<< (uint8
)CHAR_CREATE_NAME_IN_USE
;
282 QueryResult
*resultacct
= loginDatabase
.PQuery("SELECT SUM(numchars) FROM realmcharacters WHERE acctid = '%d'", GetAccountId());
285 Field
*fields
=resultacct
->Fetch();
286 uint32 acctcharcount
= fields
[0].GetUInt32();
289 if (acctcharcount
>= sWorld
.getConfig(CONFIG_CHARACTERS_PER_ACCOUNT
))
291 data
<< (uint8
)CHAR_CREATE_ACCOUNT_LIMIT
;
297 QueryResult
*result
= CharacterDatabase
.PQuery("SELECT COUNT(guid) FROM characters WHERE account = '%d'", GetAccountId());
301 Field
*fields
= result
->Fetch();
302 charcount
= fields
[0].GetUInt8();
305 if (charcount
>= sWorld
.getConfig(CONFIG_CHARACTERS_PER_REALM
))
307 data
<< (uint8
)CHAR_CREATE_SERVER_LIMIT
;
313 // speedup check for heroic class disabled case
314 uint32 heroic_free_slots
= sWorld
.getConfig(CONFIG_HEROIC_CHARACTERS_PER_REALM
);
315 if(heroic_free_slots
== 0 && GetSecurity() == SEC_PLAYER
&& class_
== CLASS_DEATH_KNIGHT
)
317 data
<< (uint8
)CHAR_CREATE_UNIQUE_CLASS_LIMIT
;
322 // speedup check for heroic class disabled case
323 uint32 req_level_for_heroic
= sWorld
.getConfig(CONFIG_MIN_LEVEL_FOR_HEROIC_CHARACTER_CREATING
);
324 if(GetSecurity() == SEC_PLAYER
&& class_
== CLASS_DEATH_KNIGHT
&& req_level_for_heroic
> sWorld
.getConfig(CONFIG_MAX_PLAYER_LEVEL
))
326 data
<< (uint8
)CHAR_CREATE_LEVEL_REQUIREMENT
;
331 bool AllowTwoSideAccounts
= sWorld
.getConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS
) || GetSecurity() > SEC_PLAYER
;
332 CinematicsSkipMode skipCinematics
= CinematicsSkipMode(sWorld
.getConfig(CONFIG_SKIP_CINEMATICS
));
334 bool have_same_race
= false;
336 // if 0 then allowed creating without any characters
337 bool have_req_level_for_heroic
= (req_level_for_heroic
==0);
339 if(!AllowTwoSideAccounts
|| skipCinematics
== CINEMATICS_SKIP_SAME_RACE
|| class_
== CLASS_DEATH_KNIGHT
)
341 QueryResult
*result2
= CharacterDatabase
.PQuery("SELECT level,race,class FROM characters WHERE account = '%u' %s",
342 GetAccountId(), (skipCinematics
== CINEMATICS_SKIP_SAME_RACE
|| class_
== CLASS_DEATH_KNIGHT
) ? "" : "LIMIT 1");
345 uint32 team_
= Player::TeamForRace(race_
);
347 Field
* field
= result2
->Fetch();
348 uint8 acc_race
= field
[1].GetUInt32();
350 if(GetSecurity() == SEC_PLAYER
&& class_
== CLASS_DEATH_KNIGHT
)
352 uint8 acc_class
= field
[2].GetUInt32();
353 if(acc_class
== CLASS_DEATH_KNIGHT
)
355 if(heroic_free_slots
> 0)
358 if(heroic_free_slots
== 0)
360 data
<< (uint8
)CHAR_CREATE_UNIQUE_CLASS_LIMIT
;
366 if(!have_req_level_for_heroic
)
368 uint32 acc_level
= field
[0].GetUInt32();
369 if(acc_level
>= req_level_for_heroic
)
370 have_req_level_for_heroic
= true;
374 // need to check team only for first character
375 // TODO: what to if account already has characters of both races?
376 if (!AllowTwoSideAccounts
)
380 acc_team
= Player::TeamForRace(acc_race
);
382 if(acc_team
!= team_
)
384 data
<< (uint8
)CHAR_CREATE_PVP_TEAMS_VIOLATION
;
391 // search same race for cinematic or same class if need
392 // TODO: check if cinematic already shown? (already logged in?; cinematic field)
393 while ((skipCinematics
== CINEMATICS_SKIP_SAME_RACE
&& !have_same_race
) || class_
== CLASS_DEATH_KNIGHT
)
395 if(!result2
->NextRow())
398 field
= result2
->Fetch();
399 acc_race
= field
[1].GetUInt32();
402 have_same_race
= race_
== acc_race
;
404 if(GetSecurity() == SEC_PLAYER
&& class_
== CLASS_DEATH_KNIGHT
)
406 uint8 acc_class
= field
[2].GetUInt32();
407 if(acc_class
== CLASS_DEATH_KNIGHT
)
409 if(heroic_free_slots
> 0)
412 if(heroic_free_slots
== 0)
414 data
<< (uint8
)CHAR_CREATE_UNIQUE_CLASS_LIMIT
;
420 if(!have_req_level_for_heroic
)
422 uint32 acc_level
= field
[0].GetUInt32();
423 if(acc_level
>= req_level_for_heroic
)
424 have_req_level_for_heroic
= true;
432 if(GetSecurity() == SEC_PLAYER
&& class_
== CLASS_DEATH_KNIGHT
&& !have_req_level_for_heroic
)
434 data
<< (uint8
)CHAR_CREATE_LEVEL_REQUIREMENT
;
439 // extract other data required for player creating
440 uint8 gender
, skin
, face
, hairStyle
, hairColor
, facialHair
, outfitId
;
441 recv_data
>> gender
>> skin
>> face
;
442 recv_data
>> hairStyle
>> hairColor
>> facialHair
>> outfitId
;
444 Player
*pNewChar
= new Player(this);
445 if(!pNewChar
->Create( sObjectMgr
.GenerateLowGuid(HIGHGUID_PLAYER
), name
, race_
, class_
, gender
, skin
, face
, hairStyle
, hairColor
, facialHair
, outfitId
))
447 // Player not create (race/class problem?)
450 data
<< (uint8
)CHAR_CREATE_ERROR
;
456 if ((have_same_race
&& skipCinematics
== CINEMATICS_SKIP_SAME_RACE
) || skipCinematics
== CINEMATICS_SKIP_ALL
)
457 pNewChar
->setCinematic(1); // not show intro
459 // Player created, save it now
460 pNewChar
->SaveToDB();
463 loginDatabase
.PExecute("DELETE FROM realmcharacters WHERE acctid= '%d' AND realmid = '%d'", GetAccountId(), realmID
);
464 loginDatabase
.PExecute("INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (%u, %u, %u)", charcount
, GetAccountId(), realmID
);
466 delete pNewChar
; // created only to call SaveToDB()
468 data
<< (uint8
)CHAR_CREATE_SUCCESS
;
471 std::string IP_str
= GetRemoteAddress();
472 sLog
.outBasic("Account: %d (IP: %s) Create Character:[%s]", GetAccountId(), IP_str
.c_str(), name
.c_str());
473 sLog
.outChar("Account: %d (IP: %s) Create Character:[%s]", GetAccountId(), IP_str
.c_str(), name
.c_str());
476 void WorldSession::HandleCharDeleteOpcode( WorldPacket
& recv_data
)
481 // can't delete loaded character
482 if(sObjectMgr
.GetPlayer(guid
))
485 uint32 accountId
= 0;
489 if(sObjectMgr
.GetGuildByLeader(guid
))
491 WorldPacket
data(SMSG_CHAR_DELETE
, 1);
492 data
<< (uint8
)CHAR_DELETE_FAILED_GUILD_LEADER
;
497 // is arena team captain
498 if(sObjectMgr
.GetArenaTeamByCaptain(guid
))
500 WorldPacket
data(SMSG_CHAR_DELETE
, 1);
501 data
<< (uint8
)CHAR_DELETE_FAILED_ARENA_CAPTAIN
;
506 QueryResult
*result
= CharacterDatabase
.PQuery("SELECT account,name FROM characters WHERE guid='%u'", GUID_LOPART(guid
));
509 Field
*fields
= result
->Fetch();
510 accountId
= fields
[0].GetUInt32();
511 name
= fields
[1].GetCppString();
515 // prevent deleting other players' characters using cheating tools
516 if(accountId
!= GetAccountId())
519 std::string IP_str
= GetRemoteAddress();
520 sLog
.outBasic("Account: %d (IP: %s) Delete Character:[%s] (guid: %u)", GetAccountId(), IP_str
.c_str(), name
.c_str(), GUID_LOPART(guid
));
521 sLog
.outChar("Account: %d (IP: %s) Delete Character:[%s] (guid: %u)", GetAccountId(), IP_str
.c_str(), name
.c_str(), GUID_LOPART(guid
));
523 if(sLog
.IsOutCharDump()) // optimize GetPlayerDump call
525 std::string dump
= PlayerDumpWriter().GetDump(GUID_LOPART(guid
));
526 sLog
.outCharDump(dump
.c_str(), GetAccountId(), GUID_LOPART(guid
), name
.c_str());
529 Player::DeleteFromDB(guid
, GetAccountId());
531 WorldPacket
data(SMSG_CHAR_DELETE
, 1);
532 data
<< (uint8
)CHAR_DELETE_SUCCESS
;
536 void WorldSession::HandlePlayerLoginOpcode( WorldPacket
& recv_data
)
538 if(PlayerLoading() || GetPlayer() != NULL
)
540 sLog
.outError("Player tryes to login again, AccountId = %d", GetAccountId());
544 m_playerLoading
= true;
545 uint64 playerGuid
= 0;
547 DEBUG_LOG( "WORLD: Recvd Player Logon Message" );
549 recv_data
>> playerGuid
;
551 LoginQueryHolder
*holder
= new LoginQueryHolder(GetAccountId(), playerGuid
);
552 if(!holder
->Initialize())
554 delete holder
; // delete all unprocessed queries
555 m_playerLoading
= false;
559 CharacterDatabase
.DelayQueryHolder(&chrHandler
, &CharacterHandler::HandlePlayerLoginCallback
, holder
);
562 void WorldSession::HandlePlayerLogin(LoginQueryHolder
*holder
)
564 uint64 playerGuid
= holder
->GetGuid();
566 Player
*pCurrChar
= new Player(this);
567 pCurrChar
->GetMotionMaster()->Initialize();
569 // "GetAccountId()==db stored account id" checked in LoadFromDB (prevent login not own character using cheating tools)
570 if(!pCurrChar
->LoadFromDB(GUID_LOPART(playerGuid
), holder
))
572 KickPlayer(); // disconnect client, player no set to session and it will not deleted or saved at kick
573 delete pCurrChar
; // delete it manually
574 delete holder
; // delete all unprocessed queries
575 m_playerLoading
= false;
579 SetPlayer(pCurrChar
);
581 pCurrChar
->SendDungeonDifficulty(false);
583 WorldPacket
data( SMSG_LOGIN_VERIFY_WORLD
, 20 );
584 data
<< pCurrChar
->GetMapId();
585 data
<< pCurrChar
->GetPositionX();
586 data
<< pCurrChar
->GetPositionY();
587 data
<< pCurrChar
->GetPositionZ();
588 data
<< pCurrChar
->GetOrientation();
591 // load player specific part before send times
592 LoadAccountData(holder
->GetResult(PLAYER_LOGIN_QUERY_LOADACCOUNTDATA
),PER_CHARACTER_CACHE_MASK
);
593 SendAccountDataTimes(PER_CHARACTER_CACHE_MASK
);
595 data
.Initialize(SMSG_FEATURE_SYSTEM_STATUS
, 2); // added in 2.2.0
596 data
<< uint8(2); // unknown value
597 data
<< uint8(0); // enable(1)/disable(0) voice chat interface in client
602 data
.Initialize(SMSG_MOTD
, 50); // new in 2.0.1
606 std::string str_motd
= sWorld
.GetMotd();
607 std::string::size_type pos
, nextpos
;
610 while ( (nextpos
= str_motd
.find('@',pos
)) != std::string::npos
)
614 data
<< str_motd
.substr(pos
, nextpos
-pos
);
620 if (pos
< str_motd
.length())
622 data
<< str_motd
.substr(pos
);
626 data
.put(0, linecount
);
629 DEBUG_LOG( "WORLD: Sent motd (SMSG_MOTD)" );
632 //QueryResult *result = CharacterDatabase.PQuery("SELECT guildid,rank FROM guild_member WHERE guid = '%u'",pCurrChar->GetGUIDLow());
633 QueryResult
*resultGuild
= holder
->GetResult(PLAYER_LOGIN_QUERY_LOADGUILD
);
637 Field
*fields
= resultGuild
->Fetch();
638 pCurrChar
->SetInGuild(fields
[0].GetUInt32());
639 pCurrChar
->SetRank(fields
[1].GetUInt32());
642 else if(pCurrChar
->GetGuildId()) // clear guild related fields in case wrong data about non existed membership
644 pCurrChar
->SetInGuild(0);
645 pCurrChar
->SetRank(0);
648 if(pCurrChar
->GetGuildId() != 0)
650 Guild
* guild
= sObjectMgr
.GetGuildById(pCurrChar
->GetGuildId());
653 data
.Initialize(SMSG_GUILD_EVENT
, (2+guild
->GetMOTD().size()+1));
654 data
<< uint8(GE_MOTD
);
656 data
<< guild
->GetMOTD();
658 DEBUG_LOG( "WORLD: Sent guild-motd (SMSG_GUILD_EVENT)" );
660 guild
->DisplayGuildBankTabsInfo(this);
662 data
.Initialize(SMSG_GUILD_EVENT
, (5+10)); // we guess size
663 data
<< uint8(GE_SIGNED_ON
);
665 data
<< pCurrChar
->GetName();
666 data
<< pCurrChar
->GetGUID();
667 guild
->BroadcastPacket(&data
);
668 DEBUG_LOG( "WORLD: Sent guild-signed-on (SMSG_GUILD_EVENT)" );
672 // remove wrong guild data
673 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());
674 pCurrChar
->SetInGuild(0);
678 data
.Initialize(SMSG_LEARNED_DANCE_MOVES
, 4+4);
683 pCurrChar
->SendInitialPacketsBeforeAddToMap();
685 //Show cinematic at the first time that player login
686 if( !pCurrChar
->getCinematic() )
688 pCurrChar
->setCinematic(1);
690 if(ChrClassesEntry
const* cEntry
= sChrClassesStore
.LookupEntry(pCurrChar
->getClass()))
692 if (cEntry
->CinematicSequence
)
693 pCurrChar
->SendCinematicStart(cEntry
->CinematicSequence
);
694 else if (ChrRacesEntry
const* rEntry
= sChrRacesStore
.LookupEntry(pCurrChar
->getRace()))
695 pCurrChar
->SendCinematicStart(rEntry
->CinematicSequence
);
699 if (!pCurrChar
->GetMap()->Add(pCurrChar
))
701 AreaTrigger
const* at
= sObjectMgr
.GetGoBackTrigger(pCurrChar
->GetMapId());
703 pCurrChar
->TeleportTo(at
->target_mapId
, at
->target_X
, at
->target_Y
, at
->target_Z
, pCurrChar
->GetOrientation());
705 pCurrChar
->TeleportToHomebind();
708 sObjectAccessor
.AddObject(pCurrChar
);
709 //sLog.outDebug("Player %s added to Map.",pCurrChar->GetName());
711 pCurrChar
->SendInitialPacketsAfterAddToMap();
713 CharacterDatabase
.PExecute("UPDATE characters SET online = 1 WHERE guid = '%u'", pCurrChar
->GetGUIDLow());
714 loginDatabase
.PExecute("UPDATE account SET active_realm_id = %d WHERE id = '%u'", realmID
, GetAccountId());
715 pCurrChar
->SetInGameTime( getMSTime() );
717 // announce group about member online (must be after add to player list to receive announce to self)
718 if(Group
*group
= pCurrChar
->GetGroup())
720 //pCurrChar->groupInfo.group->SendInit(this); // useless
725 sSocialMgr
.SendFriendStatus(pCurrChar
, FRIEND_ONLINE
, pCurrChar
->GetGUIDLow(), true);
727 // Place character in world (and load zone) before some object loading
728 pCurrChar
->LoadCorpse();
730 // setting Ghost+speed if dead
731 if (pCurrChar
->m_deathState
!= ALIVE
)
733 // not blizz like, we must correctly save and load player instead...
734 if(pCurrChar
->getRace() == RACE_NIGHTELF
)
735 pCurrChar
->CastSpell(pCurrChar
, 20584, true); // 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)
736 pCurrChar
->CastSpell(pCurrChar
, 8326, true); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?)
738 pCurrChar
->SetMovement(MOVE_WATER_WALK
);
741 pCurrChar
->ContinueTaxiFlight();
743 // reset for all pets before pet loading
744 if(pCurrChar
->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS
))
745 Pet::resetTalentsForAllPetsOf(pCurrChar
);
747 // Load pet if any (if player not alive and in taxi flight or another then pet will remember as temporary unsummoned)
748 pCurrChar
->LoadPet();
750 // Set FFA PvP for non GM in non-rest mode
751 if(sWorld
.IsFFAPvPRealm() && !pCurrChar
->isGameMaster() && !pCurrChar
->HasFlag(PLAYER_FLAGS
,PLAYER_FLAGS_RESTING
) )
752 pCurrChar
->SetByteFlag(UNIT_FIELD_BYTES_2
, 1, UNIT_BYTE2_FLAG_FFA_PVP
);
754 if(pCurrChar
->HasFlag(PLAYER_FLAGS
, PLAYER_FLAGS_CONTESTED_PVP
))
755 pCurrChar
->SetContestedPvP();
757 // Apply at_login requests
758 if(pCurrChar
->HasAtLoginFlag(AT_LOGIN_RESET_SPELLS
))
760 pCurrChar
->resetSpells();
761 SendNotification(LANG_RESET_SPELLS
);
764 if(pCurrChar
->HasAtLoginFlag(AT_LOGIN_RESET_TALENTS
))
766 pCurrChar
->resetTalents(true);
767 pCurrChar
->SendTalentsInfoData(false); // original talents send already in to SendInitialPacketsBeforeAddToMap, resend reset state
768 SendNotification(LANG_RESET_TALENTS
);
771 // show time before shutdown if shutdown planned.
772 if(sWorld
.IsShutdowning())
773 sWorld
.ShutdownMsg(true,pCurrChar
);
775 if(sWorld
.getConfig(CONFIG_ALL_TAXI_PATHS
))
776 pCurrChar
->SetTaxiCheater(true);
778 if(pCurrChar
->isGameMaster())
779 SendNotification(LANG_GM_ON
);
781 std::string IP_str
= GetRemoteAddress();
782 sLog
.outChar("Account: %d (IP: %s) Login Character:[%s] (guid:%u)",
783 GetAccountId(), IP_str
.c_str(), pCurrChar
->GetName(), pCurrChar
->GetGUIDLow());
785 if(!pCurrChar
->IsStandState() && !pCurrChar
->hasUnitState(UNIT_STAT_STUNNED
))
786 pCurrChar
->SetStandState(UNIT_STAND_STATE_STAND
);
788 m_playerLoading
= false;
792 void WorldSession::HandleSetFactionAtWar( WorldPacket
& recv_data
)
794 DEBUG_LOG( "WORLD: Received CMSG_SET_FACTION_ATWAR" );
799 recv_data
>> repListID
;
802 GetPlayer()->GetReputationMgr().SetAtWar(repListID
, flag
);
805 //I think this function is never used :/ I dunno, but i guess this opcode not exists
806 void WorldSession::HandleSetFactionCheat( WorldPacket
& /*recv_data*/ )
808 sLog
.outError("WORLD SESSION: HandleSetFactionCheat, not expected call, please report.");
813 recv_data >> FactionID;
814 recv_data >> Standing;
816 std::list<struct Factions>::iterator itr;
818 for(itr = GetPlayer()->factions.begin(); itr != GetPlayer()->factions.end(); ++itr)
820 if(itr->ReputationListID == FactionID)
822 itr->Standing += Standing;
823 itr->Flags = (itr->Flags | 1);
828 GetPlayer()->GetReputationMgr().SendStates();
831 void WorldSession::HandleMeetingStoneInfo( WorldPacket
& /*recv_data*/ )
833 DEBUG_LOG( "WORLD: Received CMSG_MEETING_STONE_INFO" );
835 SendLfgUpdate(0, 0, 0);
838 void WorldSession::HandleTutorialFlag( WorldPacket
& recv_data
)
843 uint32 wInt
= (iFlag
/ 32);
846 //sLog.outError("CHEATER? Account:[%d] Guid[%u] tried to send wrong CMSG_TUTORIAL_FLAG", GetAccountId(),GetGUID());
849 uint32 rInt
= (iFlag
% 32);
851 uint32 tutflag
= GetTutorialInt( wInt
);
852 tutflag
|= (1 << rInt
);
853 SetTutorialInt( wInt
, tutflag
);
855 //sLog.outDebug("Received Tutorial Flag Set {%u}.", iFlag);
858 void WorldSession::HandleTutorialClear( WorldPacket
& /*recv_data*/ )
860 for (int i
= 0; i
< 8; ++i
)
861 SetTutorialInt( i
, 0xFFFFFFFF );
864 void WorldSession::HandleTutorialReset( WorldPacket
& /*recv_data*/ )
866 for (int i
= 0; i
< 8; ++i
)
867 SetTutorialInt( i
, 0x00000000 );
870 void WorldSession::HandleSetWatchedFactionOpcode(WorldPacket
& recv_data
)
872 DEBUG_LOG("WORLD: Received CMSG_SET_WATCHED_FACTION");
875 GetPlayer()->SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX
, fact
);
878 void WorldSession::HandleSetFactionInactiveOpcode(WorldPacket
& recv_data
)
880 DEBUG_LOG("WORLD: Received CMSG_SET_FACTION_INACTIVE");
883 recv_data
>> replistid
>> inactive
;
885 _player
->GetReputationMgr().SetInactive(replistid
, inactive
);
888 void WorldSession::HandleShowingHelmOpcode( WorldPacket
& /*recv_data*/ )
890 DEBUG_LOG("CMSG_SHOWING_HELM for %s", _player
->GetName());
891 _player
->ToggleFlag(PLAYER_FLAGS
, PLAYER_FLAGS_HIDE_HELM
);
894 void WorldSession::HandleShowingCloakOpcode( WorldPacket
& /*recv_data*/ )
896 DEBUG_LOG("CMSG_SHOWING_CLOAK for %s", _player
->GetName());
897 _player
->ToggleFlag(PLAYER_FLAGS
, PLAYER_FLAGS_HIDE_CLOAK
);
900 void WorldSession::HandleCharRenameOpcode(WorldPacket
& recv_data
)
906 recv_data
>> newname
;
908 // prevent character rename to invalid name
909 if (!normalizePlayerName(newname
))
911 WorldPacket
data(SMSG_CHAR_RENAME
, 1);
912 data
<< uint8(CHAR_NAME_NO_NAME
);
917 uint8 res
= ObjectMgr::CheckPlayerName(newname
,true);
918 if (res
!= CHAR_NAME_SUCCESS
)
920 WorldPacket
data(SMSG_CHAR_RENAME
, 1);
926 // check name limitations
927 if (GetSecurity() == SEC_PLAYER
&& sObjectMgr
.IsReservedName(newname
))
929 WorldPacket
data(SMSG_CHAR_RENAME
, 1);
930 data
<< uint8(CHAR_NAME_RESERVED
);
935 std::string escaped_newname
= newname
;
936 CharacterDatabase
.escape_string(escaped_newname
);
938 // make sure that the character belongs to the current account, that rename at login is enabled
939 // and that there is no character with the desired new name
940 CharacterDatabase
.AsyncPQuery(&WorldSession::HandleChangePlayerNameOpcodeCallBack
,
941 GetAccountId(), newname
,
942 "SELECT guid, name FROM characters WHERE guid = %d AND account = %d AND (at_login & %d) = %d AND NOT EXISTS (SELECT NULL FROM characters WHERE name = '%s')",
943 GUID_LOPART(guid
), GetAccountId(), AT_LOGIN_RENAME
, AT_LOGIN_RENAME
, escaped_newname
.c_str()
947 void WorldSession::HandleChangePlayerNameOpcodeCallBack(QueryResult
*result
, uint32 accountId
, std::string newname
)
949 WorldSession
* session
= sWorld
.FindSession(accountId
);
952 if(result
) delete result
;
958 WorldPacket
data(SMSG_CHAR_RENAME
, 1);
959 data
<< uint8(CHAR_CREATE_ERROR
);
960 session
->SendPacket( &data
);
964 uint32 guidLow
= result
->Fetch()[0].GetUInt32();
965 uint64 guid
= MAKE_NEW_GUID(guidLow
, 0, HIGHGUID_PLAYER
);
966 std::string oldname
= result
->Fetch()[1].GetCppString();
970 CharacterDatabase
.PExecute("UPDATE characters set name = '%s', at_login = at_login & ~ %u WHERE guid ='%u'", newname
.c_str(), uint32(AT_LOGIN_RENAME
), guidLow
);
971 CharacterDatabase
.PExecute("DELETE FROM character_declinedname WHERE guid ='%u'", guidLow
);
973 sLog
.outChar("Account: %d (IP: %s) Character:[%s] (guid:%u) Changed name to: %s", session
->GetAccountId(), session
->GetRemoteAddress().c_str(), oldname
.c_str(), guidLow
, newname
.c_str());
975 WorldPacket
data(SMSG_CHAR_RENAME
, 1+8+(newname
.size()+1));
976 data
<< uint8(RESPONSE_SUCCESS
);
977 data
<< uint64(guid
);
979 session
->SendPacket(&data
);
982 void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket
& recv_data
)
988 // not accept declined names for unsupported languages
990 if(!sObjectMgr
.GetPlayerNameByGUID(guid
, name
))
992 WorldPacket
data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT
, 4+8);
994 data
<< uint64(guid
);
1000 if(!Utf8toWStr(name
, wname
))
1002 WorldPacket
data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT
, 4+8);
1004 data
<< uint64(guid
);
1009 if(!isCyrillicCharacter(wname
[0])) // name already stored as only single alphabet using
1011 WorldPacket
data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT
, 4+8);
1013 data
<< uint64(guid
);
1019 DeclinedName declinedname
;
1023 if(name2
!= name
) // character have different name
1025 WorldPacket
data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT
, 4+8);
1027 data
<< uint64(guid
);
1032 for(int i
= 0; i
< MAX_DECLINED_NAME_CASES
; ++i
)
1034 recv_data
>> declinedname
.name
[i
];
1035 if(!normalizePlayerName(declinedname
.name
[i
]))
1037 WorldPacket
data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT
, 4+8);
1039 data
<< uint64(guid
);
1045 if(!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname
, 0), declinedname
))
1047 WorldPacket
data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT
, 4+8);
1049 data
<< uint64(guid
);
1054 for(int i
= 0; i
< MAX_DECLINED_NAME_CASES
; ++i
)
1055 CharacterDatabase
.escape_string(declinedname
.name
[i
]);
1057 CharacterDatabase
.BeginTransaction();
1058 CharacterDatabase
.PExecute("DELETE FROM character_declinedname WHERE guid = '%u'", GUID_LOPART(guid
));
1059 CharacterDatabase
.PExecute("INSERT INTO character_declinedname (guid, genitive, dative, accusative, instrumental, prepositional) VALUES ('%u','%s','%s','%s','%s','%s')",
1060 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());
1061 CharacterDatabase
.CommitTransaction();
1063 WorldPacket
data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT
, 4+8);
1064 data
<< uint32(0); // OK
1065 data
<< uint64(guid
);
1069 void WorldSession::HandleAlterAppearance( WorldPacket
& recv_data
)
1071 sLog
.outDebug("CMSG_ALTER_APPEARANCE");
1073 uint32 Hair
, Color
, FacialHair
;
1074 recv_data
>> Hair
>> Color
>> FacialHair
;
1076 BarberShopStyleEntry
const* bs_hair
= sBarberShopStyleStore
.LookupEntry(Hair
);
1078 if(!bs_hair
|| bs_hair
->type
!= 0 || bs_hair
->race
!= _player
->getRace() || bs_hair
->gender
!= _player
->getGender())
1081 BarberShopStyleEntry
const* bs_facialHair
= sBarberShopStyleStore
.LookupEntry(FacialHair
);
1083 if(!bs_facialHair
|| bs_facialHair
->type
!= 2 || bs_facialHair
->race
!= _player
->getRace() || bs_facialHair
->gender
!= _player
->getGender())
1086 uint32 Cost
= _player
->GetBarberShopCost(bs_hair
->hair_id
, Color
, bs_facialHair
->hair_id
);
1089 // 1,3 - not enough money
1090 // 2 - you have to seat on barber chair
1091 if(_player
->GetMoney() < Cost
)
1093 WorldPacket
data(SMSG_BARBER_SHOP_RESULT
, 4);
1094 data
<< uint32(1); // no money
1100 WorldPacket
data(SMSG_BARBER_SHOP_RESULT
, 4);
1101 data
<< uint32(0); // ok
1105 _player
->ModifyMoney(-int32(Cost
)); // it isn't free
1106 _player
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER
, Cost
);
1108 _player
->SetByteValue(PLAYER_BYTES
, 2, uint8(bs_hair
->hair_id
));
1109 _player
->SetByteValue(PLAYER_BYTES
, 3, uint8(Color
));
1110 _player
->SetByteValue(PLAYER_BYTES_2
, 0, uint8(bs_facialHair
->hair_id
));
1112 _player
->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP
, 1);
1114 _player
->SetStandState(0); // stand up
1117 void WorldSession::HandleRemoveGlyph( WorldPacket
& recv_data
)
1122 if(slot
>= MAX_GLYPH_SLOT_INDEX
)
1124 sLog
.outDebug("Client sent wrong glyph slot number in opcode CMSG_REMOVE_GLYPH %u", slot
);
1128 if(uint32 glyph
= _player
->GetGlyph(slot
))
1130 if(GlyphPropertiesEntry
const *gp
= sGlyphPropertiesStore
.LookupEntry(glyph
))
1132 _player
->RemoveAurasDueToSpell(gp
->SpellId
);
1133 _player
->SetGlyph(slot
, 0);
1134 _player
->SendTalentsInfoData(false);
1139 void WorldSession::HandleCharCustomize(WorldPacket
& recv_data
)
1142 std::string newname
;
1145 recv_data
>> newname
;
1147 uint8 gender
, skin
, face
, hairStyle
, hairColor
, facialHair
;
1148 recv_data
>> gender
>> skin
>> hairColor
>> hairStyle
>> facialHair
>> face
;
1150 QueryResult
*result
= CharacterDatabase
.PQuery("SELECT at_login FROM characters WHERE guid ='%u'", GUID_LOPART(guid
));
1153 WorldPacket
data(SMSG_CHAR_CUSTOMIZE
, 1);
1154 data
<< uint8(CHAR_CREATE_ERROR
);
1155 SendPacket( &data
);
1159 Field
*fields
= result
->Fetch();
1160 uint32 at_loginFlags
= fields
[0].GetUInt32();
1163 if (!(at_loginFlags
& AT_LOGIN_CUSTOMIZE
))
1165 WorldPacket
data(SMSG_CHAR_CUSTOMIZE
, 1);
1166 data
<< uint8(CHAR_CREATE_ERROR
);
1167 SendPacket( &data
);
1171 // prevent character rename to invalid name
1172 if (!normalizePlayerName(newname
))
1174 WorldPacket
data(SMSG_CHAR_CUSTOMIZE
, 1);
1175 data
<< uint8(CHAR_NAME_NO_NAME
);
1176 SendPacket( &data
);
1180 uint8 res
= ObjectMgr::CheckPlayerName(newname
,true);
1181 if (res
!= CHAR_NAME_SUCCESS
)
1183 WorldPacket
data(SMSG_CHAR_CUSTOMIZE
, 1);
1185 SendPacket( &data
);
1189 // check name limitations
1190 if (GetSecurity() == SEC_PLAYER
&& sObjectMgr
.IsReservedName(newname
))
1192 WorldPacket
data(SMSG_CHAR_CUSTOMIZE
, 1);
1193 data
<< uint8(CHAR_NAME_RESERVED
);
1194 SendPacket( &data
);
1198 // character with this name already exist
1199 if (uint64 newguid
= sObjectMgr
.GetPlayerGUIDByName(newname
))
1201 if (newguid
!= guid
)
1203 WorldPacket
data(SMSG_CHAR_CUSTOMIZE
, 1);
1204 data
<< uint8(CHAR_CREATE_NAME_IN_USE
);
1205 SendPacket( &data
);
1210 CharacterDatabase
.escape_string(newname
);
1211 Player::Customize(guid
, gender
, skin
, face
, hairStyle
, hairColor
, facialHair
);
1212 CharacterDatabase
.PExecute("UPDATE characters set name = '%s', at_login = at_login & ~ %u WHERE guid ='%u'", newname
.c_str(), uint32(AT_LOGIN_CUSTOMIZE
), GUID_LOPART(guid
));
1213 CharacterDatabase
.PExecute("DELETE FROM character_declinedname WHERE guid ='%u'", GUID_LOPART(guid
));
1215 std::string IP_str
= GetRemoteAddress();
1216 sLog
.outChar("Account: %d (IP: %s), Character guid: %u Customized to: %s", GetAccountId(), IP_str
.c_str(), GUID_LOPART(guid
), newname
.c_str());
1218 WorldPacket
data(SMSG_CHAR_CUSTOMIZE
, 1+8+(newname
.size()+1)+6);
1219 data
<< uint8(RESPONSE_SUCCESS
);
1220 data
<< uint64(guid
);
1222 data
<< uint8(gender
);
1223 data
<< uint8(skin
);
1224 data
<< uint8(face
);
1225 data
<< uint8(hairStyle
);
1226 data
<< uint8(hairColor
);
1227 data
<< uint8(facialHair
);
1231 void WorldSession::HandleEquipmentSetSave(WorldPacket
&recv_data
)
1233 sLog
.outDebug("CMSG_EQUIPMENT_SET_SAVE");
1236 if(!recv_data
.readPackGUID(setGuid
))
1241 if(index
>= MAX_EQUIPMENT_SET_INDEX
) // client set slots amount
1247 std::string iconName
;
1248 recv_data
>> iconName
;
1252 eqSet
.Guid
= setGuid
;
1254 eqSet
.IconName
= iconName
;
1255 eqSet
.state
= EQUIPMENT_SET_NEW
;
1257 for(uint32 i
= 0; i
< EQUIPMENT_SLOT_END
; ++i
)
1260 if(!recv_data
.readPackGUID(itemGuid
))
1263 Item
*item
= _player
->GetItemByPos(INVENTORY_SLOT_BAG_0
, i
);
1265 if(!item
&& itemGuid
) // cheating check 1
1268 if(item
&& item
->GetGUID() != itemGuid
) // cheating check 2
1271 eqSet
.Items
[i
] = GUID_LOPART(itemGuid
);
1274 _player
->SetEquipmentSet(index
, eqSet
);
1277 void WorldSession::HandleEquipmentSetDelete(WorldPacket
&recv_data
)
1279 sLog
.outDebug("CMSG_EQUIPMENT_SET_DELETE");
1282 if(!recv_data
.readPackGUID(setGuid
))
1285 _player
->DeleteEquipmentSet(setGuid
);
1288 void WorldSession::HandleEquipmentSetUse(WorldPacket
&recv_data
)
1290 sLog
.outDebug("CMSG_EQUIPMENT_SET_USE");
1291 recv_data
.hexlike();
1293 for(uint32 i
= 0; i
< EQUIPMENT_SLOT_END
; ++i
)
1296 if(!recv_data
.readPackGUID(itemGuid
))
1299 uint8 srcbag
, srcslot
;
1300 recv_data
>> srcbag
>> srcslot
;
1302 sLog
.outDebug("Item " I64FMT
": srcbag %u, srcslot %u", itemGuid
, srcbag
, srcslot
);
1304 Item
*item
= _player
->GetItemByGuid(itemGuid
);
1306 uint16 dstpos
= i
| (INVENTORY_SLOT_BAG_0
<< 8);
1310 Item
*uItem
= _player
->GetItemByPos(INVENTORY_SLOT_BAG_0
, i
);
1314 ItemPosCountVec sDest
;
1315 uint8 msg
= _player
->CanStoreItem( NULL_BAG
, NULL_SLOT
, sDest
, uItem
, false );
1316 if(msg
== EQUIP_ERR_OK
)
1318 _player
->RemoveItem(INVENTORY_SLOT_BAG_0
, i
, true);
1319 _player
->StoreItem( sDest
, uItem
, true );
1322 _player
->SendEquipError(msg
, uItem
, NULL
);
1327 if(item
->GetPos() == dstpos
)
1330 _player
->SwapItem(item
->GetPos(), dstpos
);
1333 WorldPacket
data(SMSG_EQUIPMENT_SET_USE_RESULT
, 1);
1334 data
<< uint8(0); // 4 - equipment swap failed - inventory is full