[9529] Make Player::IsValidPos const
[getmangos.git] / src / game / CharacterHandler.cpp
blobe3a3c9b964769f1441926bb74953d318e917a52e
1 /*
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
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 "ObjectDefines.h"
29 #include "Player.h"
30 #include "Guild.h"
31 #include "UpdateMask.h"
32 #include "Auth/md5.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 "ArenaTeam.h"
40 #include "Language.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
52 private:
53 uint32 m_accountId;
54 uint64 m_guid;
55 public:
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; }
60 bool Initialize();
63 bool LoginQueryHolder::Initialize()
65 SetSize(MAX_PLAYER_LOGIN_QUERY);
67 bool res = true;
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,"
72 "position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost,"
73 "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,"
74 "arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk,"
75 "health, power1, power2, power3, power4, power5, power6, power7, specCount, activeSpec FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid));
76 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGROUP, "SELECT groupId FROM group_member WHERE memberGuid ='%u'", GUID_LOPART(m_guid));
77 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));
78 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));
79 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLS, "SELECT spell,active,disabled FROM character_spell WHERE guid = '%u'", GUID_LOPART(m_guid));
80 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));
81 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS,"SELECT quest,time FROM character_queststatus_daily WHERE guid = '%u'", GUID_LOPART(m_guid));
82 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADREPUTATION, "SELECT faction,standing,flags FROM character_reputation WHERE guid = '%u'", GUID_LOPART(m_guid));
83 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));
84 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACTIONS, "SELECT spec,button,action,type FROM character_action WHERE guid = '%u' ORDER BY button", GUID_LOPART(m_guid));
85 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSOCIALLIST, "SELECT friend,flags,note FROM character_social WHERE guid = '%u' LIMIT 255", GUID_LOPART(m_guid));
86 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));
87 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS, "SELECT spell,item,time FROM character_spell_cooldown WHERE guid = '%u'", GUID_LOPART(m_guid));
88 if(sWorld.getConfig(CONFIG_BOOL_DECLINED_NAMES_USED))
89 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_declinedname WHERE guid = '%u'", GUID_LOPART(m_guid));
90 // in other case still be dummy query
91 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGUILD, "SELECT guildid,rank FROM guild_member WHERE guid = '%u'", GUID_LOPART(m_guid));
92 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));
93 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS, "SELECT achievement, date FROM character_achievement WHERE guid = '%u'", GUID_LOPART(m_guid));
94 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS,"SELECT criteria, counter, date FROM character_achievement_progress WHERE guid = '%u'", GUID_LOPART(m_guid));
95 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));
96 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));
97 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACCOUNTDATA, "SELECT type, time, data FROM character_account_data WHERE guid='%u'", GUID_LOPART(m_guid));
98 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSKILLS, "SELECT skill, value, max FROM character_skills WHERE guid = '%u'", GUID_LOPART(m_guid));
99 res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGLYPHS, "SELECT spec, slot, glyph FROM character_glyphs WHERE guid='%u'", GUID_LOPART(m_guid));
101 return res;
104 // don't call WorldSession directly
105 // it may get deleted before the query callbacks get executed
106 // instead pass an account id to this handler
107 class CharacterHandler
109 public:
110 void HandleCharEnumCallback(QueryResult * result, uint32 account)
112 WorldSession * session = sWorld.FindSession(account);
113 if(!session)
115 delete result;
116 return;
118 session->HandleCharEnum(result);
120 void HandlePlayerLoginCallback(QueryResult * /*dummy*/, SqlQueryHolder * holder)
122 if (!holder) return;
123 WorldSession *session = sWorld.FindSession(((LoginQueryHolder*)holder)->GetAccountId());
124 if(!session)
126 delete holder;
127 return;
129 session->HandlePlayerLogin((LoginQueryHolder*)holder);
131 } chrHandler;
133 void WorldSession::HandleCharEnum(QueryResult * result)
135 WorldPacket data(SMSG_CHAR_ENUM, 100); // we guess size
137 uint8 num = 0;
139 data << num;
141 if( result )
145 uint32 guidlow = (*result)[0].GetUInt32();
146 sLog.outDetail("Loading char guid %u from account %u.", guidlow, GetAccountId());
147 if(Player::BuildEnumData(result, &data))
148 ++num;
150 while( result->NextRow() );
152 delete result;
155 data.put<uint8>(0, num);
157 SendPacket( &data );
160 void WorldSession::HandleCharEnumOpcode( WorldPacket & /*recv_data*/ )
162 /// get all the data necessary for loading all characters (along with their pets) on the account
163 CharacterDatabase.AsyncPQuery(&chrHandler, &CharacterHandler::HandleCharEnumCallback, GetAccountId(),
164 !sWorld.getConfig(CONFIG_BOOL_DECLINED_NAMES_USED) ?
165 // ------- Query Without Declined Names --------
166 // 0 1 2 3 4 5 6 7
167 "SELECT characters.guid, characters.name, characters.race, characters.class, characters.gender, characters.playerBytes, characters.playerBytes2, characters.level, "
168 // 8 9 10 11 12 13 14
169 "characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z, guild_member.guildid, characters.playerFlags, "
170 // 15 16 17 18 19
171 "characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.data "
172 "FROM characters LEFT JOIN character_pet ON characters.guid=character_pet.owner AND character_pet.slot='%u' "
173 "LEFT JOIN guild_member ON characters.guid = guild_member.guid "
174 "WHERE characters.account = '%u' ORDER BY characters.guid"
176 // --------- Query With Declined Names ---------
177 // 0 1 2 3 4 5 6 7
178 "SELECT characters.guid, characters.name, characters.race, characters.class, characters.gender, characters.playerBytes, characters.playerBytes2, characters.level, "
179 // 8 9 10 11 12 13 14
180 "characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z, guild_member.guildid, characters.playerFlags, "
181 // 15 16 17 18 19 20
182 "characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.data, character_declinedname.genitive "
183 "FROM characters LEFT JOIN character_pet ON characters.guid = character_pet.owner AND character_pet.slot='%u' "
184 "LEFT JOIN character_declinedname ON characters.guid = character_declinedname.guid "
185 "LEFT JOIN guild_member ON characters.guid = guild_member.guid "
186 "WHERE characters.account = '%u' ORDER BY characters.guid",
187 PET_SAVE_AS_CURRENT, GetAccountId());
190 void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
192 std::string name;
193 uint8 race_, class_;
195 recv_data >> name;
197 recv_data >> race_;
198 recv_data >> class_;
200 WorldPacket data(SMSG_CHAR_CREATE, 1); // returned with diff.values in all cases
202 if(GetSecurity() == SEC_PLAYER)
204 if(uint32 mask = sWorld.getConfig(CONFIG_UINT32_CHARACTERS_CREATING_DISABLED))
206 bool disabled = false;
208 uint32 team = Player::TeamForRace(race_);
209 switch(team)
211 case ALLIANCE: disabled = mask & (1 << 0); break;
212 case HORDE: disabled = mask & (1 << 1); break;
215 if(disabled)
217 data << (uint8)CHAR_CREATE_DISABLED;
218 SendPacket( &data );
219 return;
224 ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(class_);
225 ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(race_);
227 if( !classEntry || !raceEntry )
229 data << (uint8)CHAR_CREATE_FAILED;
230 SendPacket( &data );
231 sLog.outError("Class: %u or Race %u not found in DBC (Wrong DBC files?) or Cheater?", class_, race_);
232 return;
235 // prevent character creating Expansion race without Expansion account
236 if (raceEntry->expansion > Expansion())
238 data << (uint8)CHAR_CREATE_EXPANSION;
239 sLog.outError("Expansion %u account:[%d] tried to Create character with expansion %u race (%u)", Expansion(), GetAccountId(), raceEntry->expansion, race_);
240 SendPacket( &data );
241 return;
244 // prevent character creating Expansion class without Expansion account
245 if (classEntry->expansion > Expansion())
247 data << (uint8)CHAR_CREATE_EXPANSION_CLASS;
248 sLog.outError("Expansion %u account:[%d] tried to Create character with expansion %u class (%u)", Expansion(), GetAccountId(), classEntry->expansion, class_);
249 SendPacket( &data );
250 return;
253 // prevent character creating with invalid name
254 if (!normalizePlayerName(name))
256 data << (uint8)CHAR_NAME_NO_NAME;
257 SendPacket( &data );
258 sLog.outError("Account:[%d] but tried to Create character with empty [name]", GetAccountId());
259 return;
262 // check name limitations
263 uint8 res = ObjectMgr::CheckPlayerName(name, true);
264 if (res != CHAR_NAME_SUCCESS)
266 data << uint8(res);
267 SendPacket( &data );
268 return;
271 if (GetSecurity() == SEC_PLAYER && sObjectMgr.IsReservedName(name))
273 data << (uint8)CHAR_NAME_RESERVED;
274 SendPacket( &data );
275 return;
278 if (sObjectMgr.GetPlayerGUIDByName(name))
280 data << (uint8)CHAR_CREATE_NAME_IN_USE;
281 SendPacket( &data );
282 return;
285 QueryResult *resultacct = loginDatabase.PQuery("SELECT SUM(numchars) FROM realmcharacters WHERE acctid = '%d'", GetAccountId());
286 if (resultacct)
288 Field *fields=resultacct->Fetch();
289 uint32 acctcharcount = fields[0].GetUInt32();
290 delete resultacct;
292 if (acctcharcount >= sWorld.getConfig(CONFIG_UINT32_CHARACTERS_PER_ACCOUNT))
294 data << (uint8)CHAR_CREATE_ACCOUNT_LIMIT;
295 SendPacket( &data );
296 return;
300 QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(guid) FROM characters WHERE account = '%d'", GetAccountId());
301 uint8 charcount = 0;
302 if ( result )
304 Field *fields = result->Fetch();
305 charcount = fields[0].GetUInt8();
306 delete result;
308 if (charcount >= sWorld.getConfig(CONFIG_UINT32_CHARACTERS_PER_REALM))
310 data << (uint8)CHAR_CREATE_SERVER_LIMIT;
311 SendPacket( &data );
312 return;
316 // speedup check for heroic class disabled case
317 uint32 heroic_free_slots = sWorld.getConfig(CONFIG_UINT32_HEROIC_CHARACTERS_PER_REALM);
318 if(heroic_free_slots == 0 && GetSecurity() == SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT)
320 data << (uint8)CHAR_CREATE_UNIQUE_CLASS_LIMIT;
321 SendPacket( &data );
322 return;
325 // speedup check for heroic class disabled case
326 uint32 req_level_for_heroic = sWorld.getConfig(CONFIG_UINT32_MIN_LEVEL_FOR_HEROIC_CHARACTER_CREATING);
327 if(GetSecurity() == SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT && req_level_for_heroic > sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
329 data << (uint8)CHAR_CREATE_LEVEL_REQUIREMENT;
330 SendPacket( &data );
331 return;
334 bool AllowTwoSideAccounts = sWorld.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_ACCOUNTS) || GetSecurity() > SEC_PLAYER;
335 CinematicsSkipMode skipCinematics = CinematicsSkipMode(sWorld.getConfig(CONFIG_UINT32_SKIP_CINEMATICS));
337 bool have_same_race = false;
339 // if 0 then allowed creating without any characters
340 bool have_req_level_for_heroic = (req_level_for_heroic==0);
342 if(!AllowTwoSideAccounts || skipCinematics == CINEMATICS_SKIP_SAME_RACE || class_ == CLASS_DEATH_KNIGHT)
344 QueryResult *result2 = CharacterDatabase.PQuery("SELECT level,race,class FROM characters WHERE account = '%u' %s",
345 GetAccountId(), (skipCinematics == CINEMATICS_SKIP_SAME_RACE || class_ == CLASS_DEATH_KNIGHT) ? "" : "LIMIT 1");
346 if(result2)
348 uint32 team_= Player::TeamForRace(race_);
350 Field* field = result2->Fetch();
351 uint8 acc_race = field[1].GetUInt32();
353 if(GetSecurity() == SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT)
355 uint8 acc_class = field[2].GetUInt32();
356 if(acc_class == CLASS_DEATH_KNIGHT)
358 if(heroic_free_slots > 0)
359 --heroic_free_slots;
361 if(heroic_free_slots == 0)
363 data << (uint8)CHAR_CREATE_UNIQUE_CLASS_LIMIT;
364 SendPacket( &data );
365 delete result2;
366 return;
370 if(!have_req_level_for_heroic)
372 uint32 acc_level = field[0].GetUInt32();
373 if(acc_level >= req_level_for_heroic)
374 have_req_level_for_heroic = true;
378 // need to check team only for first character
379 // TODO: what to if account already has characters of both races?
380 if (!AllowTwoSideAccounts)
382 uint32 acc_team = 0;
383 if(acc_race > 0)
384 acc_team = Player::TeamForRace(acc_race);
386 if(acc_team != team_)
388 data << (uint8)CHAR_CREATE_PVP_TEAMS_VIOLATION;
389 SendPacket( &data );
390 delete result2;
391 return;
395 // search same race for cinematic or same class if need
396 // TODO: check if cinematic already shown? (already logged in?; cinematic field)
397 while ((skipCinematics == CINEMATICS_SKIP_SAME_RACE && !have_same_race) || class_ == CLASS_DEATH_KNIGHT)
399 if(!result2->NextRow())
400 break;
402 field = result2->Fetch();
403 acc_race = field[1].GetUInt32();
405 if(!have_same_race)
406 have_same_race = race_ == acc_race;
408 if(GetSecurity() == SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT)
410 uint8 acc_class = field[2].GetUInt32();
411 if(acc_class == CLASS_DEATH_KNIGHT)
413 if(heroic_free_slots > 0)
414 --heroic_free_slots;
416 if(heroic_free_slots == 0)
418 data << (uint8)CHAR_CREATE_UNIQUE_CLASS_LIMIT;
419 SendPacket( &data );
420 delete result2;
421 return;
425 if(!have_req_level_for_heroic)
427 uint32 acc_level = field[0].GetUInt32();
428 if(acc_level >= req_level_for_heroic)
429 have_req_level_for_heroic = true;
433 delete result2;
437 if(GetSecurity() == SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT && !have_req_level_for_heroic)
439 data << (uint8)CHAR_CREATE_LEVEL_REQUIREMENT;
440 SendPacket( &data );
441 return;
444 // extract other data required for player creating
445 uint8 gender, skin, face, hairStyle, hairColor, facialHair, outfitId;
446 recv_data >> gender >> skin >> face;
447 recv_data >> hairStyle >> hairColor >> facialHair >> outfitId;
449 Player *pNewChar = new Player(this);
450 if(!pNewChar->Create( sObjectMgr.GenerateLowGuid(HIGHGUID_PLAYER), name, race_, class_, gender, skin, face, hairStyle, hairColor, facialHair, outfitId ))
452 // Player not create (race/class problem?)
453 delete pNewChar;
455 data << (uint8)CHAR_CREATE_ERROR;
456 SendPacket( &data );
458 return;
461 if ((have_same_race && skipCinematics == CINEMATICS_SKIP_SAME_RACE) || skipCinematics == CINEMATICS_SKIP_ALL)
462 pNewChar->setCinematic(1); // not show intro
464 pNewChar->SetAtLoginFlag(AT_LOGIN_FIRST); // First login
466 // Player created, save it now
467 pNewChar->SaveToDB();
468 charcount += 1;
470 loginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid= '%d' AND realmid = '%d'", GetAccountId(), realmID);
471 loginDatabase.PExecute("INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (%u, %u, %u)", charcount, GetAccountId(), realmID);
473 delete pNewChar; // created only to call SaveToDB()
475 data << (uint8)CHAR_CREATE_SUCCESS;
476 SendPacket( &data );
478 std::string IP_str = GetRemoteAddress();
479 sLog.outBasic("Account: %d (IP: %s) Create Character:[%s]", GetAccountId(), IP_str.c_str(), name.c_str());
480 sLog.outChar("Account: %d (IP: %s) Create Character:[%s]", GetAccountId(), IP_str.c_str(), name.c_str());
483 void WorldSession::HandleCharDeleteOpcode( WorldPacket & recv_data )
485 uint64 guid;
486 recv_data >> guid;
488 // can't delete loaded character
489 if(sObjectMgr.GetPlayer(guid))
490 return;
492 uint32 accountId = 0;
493 std::string name;
495 // is guild leader
496 if(sObjectMgr.GetGuildByLeader(guid))
498 WorldPacket data(SMSG_CHAR_DELETE, 1);
499 data << (uint8)CHAR_DELETE_FAILED_GUILD_LEADER;
500 SendPacket( &data );
501 return;
504 // is arena team captain
505 if(sObjectMgr.GetArenaTeamByCaptain(guid))
507 WorldPacket data(SMSG_CHAR_DELETE, 1);
508 data << (uint8)CHAR_DELETE_FAILED_ARENA_CAPTAIN;
509 SendPacket( &data );
510 return;
513 QueryResult *result = CharacterDatabase.PQuery("SELECT account,name FROM characters WHERE guid='%u'", GUID_LOPART(guid));
514 if(result)
516 Field *fields = result->Fetch();
517 accountId = fields[0].GetUInt32();
518 name = fields[1].GetCppString();
519 delete result;
522 // prevent deleting other players' characters using cheating tools
523 if(accountId != GetAccountId())
524 return;
526 std::string IP_str = GetRemoteAddress();
527 sLog.outBasic("Account: %d (IP: %s) Delete Character:[%s] (guid: %u)", GetAccountId(), IP_str.c_str(), name.c_str(), GUID_LOPART(guid));
528 sLog.outChar("Account: %d (IP: %s) Delete Character:[%s] (guid: %u)", GetAccountId(), IP_str.c_str(), name.c_str(), GUID_LOPART(guid));
530 if(sLog.IsOutCharDump()) // optimize GetPlayerDump call
532 std::string dump = PlayerDumpWriter().GetDump(GUID_LOPART(guid));
533 sLog.outCharDump(dump.c_str(), GetAccountId(), GUID_LOPART(guid), name.c_str());
536 Player::DeleteFromDB(guid, GetAccountId());
538 WorldPacket data(SMSG_CHAR_DELETE, 1);
539 data << (uint8)CHAR_DELETE_SUCCESS;
540 SendPacket( &data );
543 void WorldSession::HandlePlayerLoginOpcode( WorldPacket & recv_data )
545 if(PlayerLoading() || GetPlayer() != NULL)
547 sLog.outError("Player tryes to login again, AccountId = %d", GetAccountId());
548 return;
551 m_playerLoading = true;
552 uint64 playerGuid = 0;
554 DEBUG_LOG( "WORLD: Recvd Player Logon Message" );
556 recv_data >> playerGuid;
558 LoginQueryHolder *holder = new LoginQueryHolder(GetAccountId(), playerGuid);
559 if(!holder->Initialize())
561 delete holder; // delete all unprocessed queries
562 m_playerLoading = false;
563 return;
566 CharacterDatabase.DelayQueryHolder(&chrHandler, &CharacterHandler::HandlePlayerLoginCallback, holder);
569 void WorldSession::HandlePlayerLogin(LoginQueryHolder *holder)
571 uint64 playerGuid = holder->GetGuid();
573 Player *pCurrChar = new Player(this);
574 pCurrChar->GetMotionMaster()->Initialize();
576 // "GetAccountId()==db stored account id" checked in LoadFromDB (prevent login not own character using cheating tools)
577 if(!pCurrChar->LoadFromDB(GUID_LOPART(playerGuid), holder))
579 KickPlayer(); // disconnect client, player no set to session and it will not deleted or saved at kick
580 delete pCurrChar; // delete it manually
581 delete holder; // delete all unprocessed queries
582 m_playerLoading = false;
583 return;
586 SetPlayer(pCurrChar);
588 pCurrChar->SendDungeonDifficulty(false);
590 WorldPacket data( SMSG_LOGIN_VERIFY_WORLD, 20 );
591 data << pCurrChar->GetMapId();
592 data << pCurrChar->GetPositionX();
593 data << pCurrChar->GetPositionY();
594 data << pCurrChar->GetPositionZ();
595 data << pCurrChar->GetOrientation();
596 SendPacket(&data);
598 // load player specific part before send times
599 LoadAccountData(holder->GetResult(PLAYER_LOGIN_QUERY_LOADACCOUNTDATA),PER_CHARACTER_CACHE_MASK);
600 SendAccountDataTimes(PER_CHARACTER_CACHE_MASK);
602 data.Initialize(SMSG_FEATURE_SYSTEM_STATUS, 2); // added in 2.2.0
603 data << uint8(2); // unknown value
604 data << uint8(0); // enable(1)/disable(0) voice chat interface in client
605 SendPacket(&data);
607 // Send MOTD
609 data.Initialize(SMSG_MOTD, 50); // new in 2.0.1
610 data << (uint32)0;
612 uint32 linecount=0;
613 std::string str_motd = sWorld.GetMotd();
614 std::string::size_type pos, nextpos;
616 pos = 0;
617 while ( (nextpos= str_motd.find('@',pos)) != std::string::npos )
619 if (nextpos != pos)
621 data << str_motd.substr(pos, nextpos-pos);
622 ++linecount;
624 pos = nextpos + 1;
627 if (pos < str_motd.length())
629 data << str_motd.substr(pos);
630 ++linecount;
633 data.put(0, linecount);
635 SendPacket( &data );
636 DEBUG_LOG( "WORLD: Sent motd (SMSG_MOTD)" );
639 //QueryResult *result = CharacterDatabase.PQuery("SELECT guildid,rank FROM guild_member WHERE guid = '%u'",pCurrChar->GetGUIDLow());
640 QueryResult *resultGuild = holder->GetResult(PLAYER_LOGIN_QUERY_LOADGUILD);
642 if(resultGuild)
644 Field *fields = resultGuild->Fetch();
645 pCurrChar->SetInGuild(fields[0].GetUInt32());
646 pCurrChar->SetRank(fields[1].GetUInt32());
647 delete resultGuild;
649 else if(pCurrChar->GetGuildId()) // clear guild related fields in case wrong data about non existed membership
651 pCurrChar->SetInGuild(0);
652 pCurrChar->SetRank(0);
655 if(pCurrChar->GetGuildId() != 0)
657 Guild* guild = sObjectMgr.GetGuildById(pCurrChar->GetGuildId());
658 if(guild)
660 data.Initialize(SMSG_GUILD_EVENT, (1+1+guild->GetMOTD().size()+1));
661 data << uint8(GE_MOTD);
662 data << uint8(1);
663 data << guild->GetMOTD();
664 SendPacket(&data);
665 DEBUG_LOG( "WORLD: Sent guild-motd (SMSG_GUILD_EVENT)" );
667 guild->DisplayGuildBankTabsInfo(this);
669 guild->BroadcastEvent(GE_SIGNED_ON, pCurrChar->GetGUID(), 1, pCurrChar->GetName(), "", "");
671 else
673 // remove wrong guild data
674 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());
675 pCurrChar->SetInGuild(0);
679 data.Initialize(SMSG_LEARNED_DANCE_MOVES, 4+4);
680 data << uint32(0);
681 data << uint32(0);
682 SendPacket(&data);
684 pCurrChar->SendInitialPacketsBeforeAddToMap();
686 //Show cinematic at the first time that player login
687 if( !pCurrChar->getCinematic() )
689 pCurrChar->setCinematic(1);
691 if(ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(pCurrChar->getClass()))
693 if (cEntry->CinematicSequence)
694 pCurrChar->SendCinematicStart(cEntry->CinematicSequence);
695 else if (ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(pCurrChar->getRace()))
696 pCurrChar->SendCinematicStart(rEntry->CinematicSequence);
700 if (!pCurrChar->GetMap()->Add(pCurrChar))
702 AreaTrigger const* at = sObjectMgr.GetGoBackTrigger(pCurrChar->GetMapId());
703 if(at)
704 pCurrChar->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, pCurrChar->GetOrientation());
705 else
706 pCurrChar->TeleportToHomebind();
709 sObjectAccessor.AddObject(pCurrChar);
710 //sLog.outDebug("Player %s added to Map.",pCurrChar->GetName());
712 pCurrChar->SendInitialPacketsAfterAddToMap();
714 CharacterDatabase.PExecute("UPDATE characters SET online = 1 WHERE guid = '%u'", pCurrChar->GetGUIDLow());
715 loginDatabase.PExecute("UPDATE account SET active_realm_id = %d WHERE id = '%u'", realmID, GetAccountId());
716 pCurrChar->SetInGameTime( getMSTime() );
718 // announce group about member online (must be after add to player list to receive announce to self)
719 if(Group *group = pCurrChar->GetGroup())
721 //pCurrChar->groupInfo.group->SendInit(this); // useless
722 group->SendUpdate();
725 // friend status
726 sSocialMgr.SendFriendStatus(pCurrChar, FRIEND_ONLINE, pCurrChar->GetGUIDLow(), true);
728 // Place character in world (and load zone) before some object loading
729 pCurrChar->LoadCorpse();
731 // setting Ghost+speed if dead
732 if (pCurrChar->m_deathState != ALIVE)
734 // not blizz like, we must correctly save and load player instead...
735 if(pCurrChar->getRace() == RACE_NIGHTELF)
736 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)
737 pCurrChar->CastSpell(pCurrChar, 8326, true); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?)
739 pCurrChar->SetMovement(MOVE_WATER_WALK);
742 pCurrChar->ContinueTaxiFlight();
744 // reset for all pets before pet loading
745 if(pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS))
746 Pet::resetTalentsForAllPetsOf(pCurrChar);
748 // Load pet if any (if player not alive and in taxi flight or another then pet will remember as temporary unsummoned)
749 pCurrChar->LoadPet();
751 // Set FFA PvP for non GM in non-rest mode
752 if(sWorld.IsFFAPvPRealm() && !pCurrChar->isGameMaster() && !pCurrChar->HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_RESTING) )
753 pCurrChar->SetFFAPvP(true);
755 if(pCurrChar->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP))
756 pCurrChar->SetContestedPvP();
758 // Apply at_login requests
759 if(pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_SPELLS))
761 pCurrChar->resetSpells();
762 SendNotification(LANG_RESET_SPELLS);
765 if(pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_TALENTS))
767 pCurrChar->resetTalents(true);
768 pCurrChar->SendTalentsInfoData(false); // original talents send already in to SendInitialPacketsBeforeAddToMap, resend reset state
769 SendNotification(LANG_RESET_TALENTS); // we can use SMSG_TALENTS_INVOLUNTARILY_RESET here
772 if (pCurrChar->HasAtLoginFlag(AT_LOGIN_FIRST))
773 pCurrChar->RemoveAtLoginFlag(AT_LOGIN_FIRST);
775 // show time before shutdown if shutdown planned.
776 if(sWorld.IsShutdowning())
777 sWorld.ShutdownMsg(true,pCurrChar);
779 if(sWorld.getConfig(CONFIG_BOOL_ALL_TAXI_PATHS))
780 pCurrChar->SetTaxiCheater(true);
782 if(pCurrChar->isGameMaster())
783 SendNotification(LANG_GM_ON);
785 std::string IP_str = GetRemoteAddress();
786 sLog.outChar("Account: %d (IP: %s) Login Character:[%s] (guid:%u)",
787 GetAccountId(), IP_str.c_str(), pCurrChar->GetName(), pCurrChar->GetGUIDLow());
789 if(!pCurrChar->IsStandState() && !pCurrChar->hasUnitState(UNIT_STAT_STUNNED))
790 pCurrChar->SetStandState(UNIT_STAND_STATE_STAND);
792 m_playerLoading = false;
793 delete holder;
796 void WorldSession::HandleSetFactionAtWar( WorldPacket & recv_data )
798 DEBUG_LOG( "WORLD: Received CMSG_SET_FACTION_ATWAR" );
800 uint32 repListID;
801 uint8 flag;
803 recv_data >> repListID;
804 recv_data >> flag;
806 GetPlayer()->GetReputationMgr().SetAtWar(repListID, flag);
809 //I think this function is never used :/ I dunno, but i guess this opcode not exists
810 void WorldSession::HandleSetFactionCheat( WorldPacket & /*recv_data*/ )
812 sLog.outError("WORLD SESSION: HandleSetFactionCheat, not expected call, please report.");
814 uint32 FactionID;
815 uint32 Standing;
817 recv_data >> FactionID;
818 recv_data >> Standing;
820 std::list<struct Factions>::iterator itr;
822 for(itr = GetPlayer()->factions.begin(); itr != GetPlayer()->factions.end(); ++itr)
824 if(itr->ReputationListID == FactionID)
826 itr->Standing += Standing;
827 itr->Flags = (itr->Flags | 1);
828 break;
832 GetPlayer()->GetReputationMgr().SendStates();
835 void WorldSession::HandleMeetingStoneInfo( WorldPacket & /*recv_data*/ )
837 DEBUG_LOG( "WORLD: Received CMSG_MEETING_STONE_INFO" );
839 SendLfgUpdate(0, 0, 0);
842 void WorldSession::HandleTutorialFlag( WorldPacket & recv_data )
844 uint32 iFlag;
845 recv_data >> iFlag;
847 uint32 wInt = (iFlag / 32);
848 if (wInt >= 8)
850 //sLog.outError("CHEATER? Account:[%d] Guid[%u] tried to send wrong CMSG_TUTORIAL_FLAG", GetAccountId(),GetGUID());
851 return;
853 uint32 rInt = (iFlag % 32);
855 uint32 tutflag = GetTutorialInt( wInt );
856 tutflag |= (1 << rInt);
857 SetTutorialInt( wInt, tutflag );
859 //sLog.outDebug("Received Tutorial Flag Set {%u}.", iFlag);
862 void WorldSession::HandleTutorialClear( WorldPacket & /*recv_data*/ )
864 for (int i = 0; i < 8; ++i)
865 SetTutorialInt( i, 0xFFFFFFFF );
868 void WorldSession::HandleTutorialReset( WorldPacket & /*recv_data*/ )
870 for (int i = 0; i < 8; ++i)
871 SetTutorialInt( i, 0x00000000 );
874 void WorldSession::HandleSetWatchedFactionOpcode(WorldPacket & recv_data)
876 DEBUG_LOG("WORLD: Received CMSG_SET_WATCHED_FACTION");
877 int32 repId;
878 recv_data >> repId;
879 GetPlayer()->SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, repId);
882 void WorldSession::HandleSetFactionInactiveOpcode(WorldPacket & recv_data)
884 DEBUG_LOG("WORLD: Received CMSG_SET_FACTION_INACTIVE");
885 uint32 replistid;
886 uint8 inactive;
887 recv_data >> replistid >> inactive;
889 _player->GetReputationMgr().SetInactive(replistid, inactive);
892 void WorldSession::HandleShowingHelmOpcode( WorldPacket & /*recv_data*/ )
894 DEBUG_LOG("CMSG_SHOWING_HELM for %s", _player->GetName());
895 _player->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM);
898 void WorldSession::HandleShowingCloakOpcode( WorldPacket & /*recv_data*/ )
900 DEBUG_LOG("CMSG_SHOWING_CLOAK for %s", _player->GetName());
901 _player->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK);
904 void WorldSession::HandleCharRenameOpcode(WorldPacket& recv_data)
906 uint64 guid;
907 std::string newname;
909 recv_data >> guid;
910 recv_data >> newname;
912 // prevent character rename to invalid name
913 if (!normalizePlayerName(newname))
915 WorldPacket data(SMSG_CHAR_RENAME, 1);
916 data << uint8(CHAR_NAME_NO_NAME);
917 SendPacket( &data );
918 return;
921 uint8 res = ObjectMgr::CheckPlayerName(newname,true);
922 if (res != CHAR_NAME_SUCCESS)
924 WorldPacket data(SMSG_CHAR_RENAME, 1);
925 data << uint8(res);
926 SendPacket( &data );
927 return;
930 // check name limitations
931 if (GetSecurity() == SEC_PLAYER && sObjectMgr.IsReservedName(newname))
933 WorldPacket data(SMSG_CHAR_RENAME, 1);
934 data << uint8(CHAR_NAME_RESERVED);
935 SendPacket( &data );
936 return;
939 std::string escaped_newname = newname;
940 CharacterDatabase.escape_string(escaped_newname);
942 // make sure that the character belongs to the current account, that rename at login is enabled
943 // and that there is no character with the desired new name
944 CharacterDatabase.AsyncPQuery(&WorldSession::HandleChangePlayerNameOpcodeCallBack,
945 GetAccountId(), newname,
946 "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')",
947 GUID_LOPART(guid), GetAccountId(), AT_LOGIN_RENAME, AT_LOGIN_RENAME, escaped_newname.c_str()
951 void WorldSession::HandleChangePlayerNameOpcodeCallBack(QueryResult *result, uint32 accountId, std::string newname)
953 WorldSession * session = sWorld.FindSession(accountId);
954 if(!session)
956 if(result) delete result;
957 return;
960 if (!result)
962 WorldPacket data(SMSG_CHAR_RENAME, 1);
963 data << uint8(CHAR_CREATE_ERROR);
964 session->SendPacket( &data );
965 return;
968 uint32 guidLow = result->Fetch()[0].GetUInt32();
969 uint64 guid = MAKE_NEW_GUID(guidLow, 0, HIGHGUID_PLAYER);
970 std::string oldname = result->Fetch()[1].GetCppString();
972 delete result;
974 CharacterDatabase.PExecute("UPDATE characters set name = '%s', at_login = at_login & ~ %u WHERE guid ='%u'", newname.c_str(), uint32(AT_LOGIN_RENAME), guidLow);
975 CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid ='%u'", guidLow);
977 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());
979 WorldPacket data(SMSG_CHAR_RENAME, 1+8+(newname.size()+1));
980 data << uint8(RESPONSE_SUCCESS);
981 data << uint64(guid);
982 data << newname;
983 session->SendPacket(&data);
986 void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recv_data)
988 uint64 guid;
990 recv_data >> guid;
992 // not accept declined names for unsupported languages
993 std::string name;
994 if(!sObjectMgr.GetPlayerNameByGUID(guid, name))
996 WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8);
997 data << uint32(1);
998 data << uint64(guid);
999 SendPacket(&data);
1000 return;
1003 std::wstring wname;
1004 if(!Utf8toWStr(name, wname))
1006 WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8);
1007 data << uint32(1);
1008 data << uint64(guid);
1009 SendPacket(&data);
1010 return;
1013 if(!isCyrillicCharacter(wname[0])) // name already stored as only single alphabet using
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 std::string name2;
1023 DeclinedName declinedname;
1025 recv_data >> name2;
1027 if(name2 != name) // character have different name
1029 WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8);
1030 data << uint32(1);
1031 data << uint64(guid);
1032 SendPacket(&data);
1033 return;
1036 for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
1038 recv_data >> declinedname.name[i];
1039 if(!normalizePlayerName(declinedname.name[i]))
1041 WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8);
1042 data << uint32(1);
1043 data << uint64(guid);
1044 SendPacket(&data);
1045 return;
1049 if(!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname, 0), declinedname))
1051 WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8);
1052 data << uint32(1);
1053 data << uint64(guid);
1054 SendPacket(&data);
1055 return;
1058 for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
1059 CharacterDatabase.escape_string(declinedname.name[i]);
1061 CharacterDatabase.BeginTransaction();
1062 CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid = '%u'", GUID_LOPART(guid));
1063 CharacterDatabase.PExecute("INSERT INTO character_declinedname (guid, genitive, dative, accusative, instrumental, prepositional) VALUES ('%u','%s','%s','%s','%s','%s')",
1064 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());
1065 CharacterDatabase.CommitTransaction();
1067 WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8);
1068 data << uint32(0); // OK
1069 data << uint64(guid);
1070 SendPacket(&data);
1073 void WorldSession::HandleAlterAppearance( WorldPacket & recv_data )
1075 sLog.outDebug("CMSG_ALTER_APPEARANCE");
1077 uint32 Hair, Color, FacialHair;
1078 recv_data >> Hair >> Color >> FacialHair;
1080 BarberShopStyleEntry const* bs_hair = sBarberShopStyleStore.LookupEntry(Hair);
1082 if(!bs_hair || bs_hair->type != 0 || bs_hair->race != _player->getRace() || bs_hair->gender != _player->getGender())
1083 return;
1085 BarberShopStyleEntry const* bs_facialHair = sBarberShopStyleStore.LookupEntry(FacialHair);
1087 if(!bs_facialHair || bs_facialHair->type != 2 || bs_facialHair->race != _player->getRace() || bs_facialHair->gender != _player->getGender())
1088 return;
1090 uint32 Cost = _player->GetBarberShopCost(bs_hair->hair_id, Color, bs_facialHair->hair_id);
1092 // 0 - ok
1093 // 1,3 - not enough money
1094 // 2 - you have to seat on barber chair
1095 if(_player->GetMoney() < Cost)
1097 WorldPacket data(SMSG_BARBER_SHOP_RESULT, 4);
1098 data << uint32(1); // no money
1099 SendPacket(&data);
1100 return;
1102 else
1104 WorldPacket data(SMSG_BARBER_SHOP_RESULT, 4);
1105 data << uint32(0); // ok
1106 SendPacket(&data);
1109 _player->ModifyMoney(-int32(Cost)); // it isn't free
1110 _player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER, Cost);
1112 _player->SetByteValue(PLAYER_BYTES, 2, uint8(bs_hair->hair_id));
1113 _player->SetByteValue(PLAYER_BYTES, 3, uint8(Color));
1114 _player->SetByteValue(PLAYER_BYTES_2, 0, uint8(bs_facialHair->hair_id));
1116 _player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP, 1);
1118 _player->SetStandState(0); // stand up
1121 void WorldSession::HandleRemoveGlyph( WorldPacket & recv_data )
1123 uint32 slot;
1124 recv_data >> slot;
1126 if(slot >= MAX_GLYPH_SLOT_INDEX)
1128 sLog.outDebug("Client sent wrong glyph slot number in opcode CMSG_REMOVE_GLYPH %u", slot);
1129 return;
1132 if(_player->GetGlyph(slot))
1134 _player->ApplyGlyph(slot, false);
1135 _player->SetGlyph(slot, 0);
1136 _player->SendTalentsInfoData(false);
1140 void WorldSession::HandleCharCustomize(WorldPacket& recv_data)
1142 uint64 guid;
1143 std::string newname;
1145 recv_data >> guid;
1146 recv_data >> newname;
1148 uint8 gender, skin, face, hairStyle, hairColor, facialHair;
1149 recv_data >> gender >> skin >> hairColor >> hairStyle >> facialHair >> face;
1151 QueryResult *result = CharacterDatabase.PQuery("SELECT at_login FROM characters WHERE guid ='%u'", GUID_LOPART(guid));
1152 if (!result)
1154 WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1);
1155 data << uint8(CHAR_CREATE_ERROR);
1156 SendPacket( &data );
1157 return;
1160 Field *fields = result->Fetch();
1161 uint32 at_loginFlags = fields[0].GetUInt32();
1162 delete result;
1164 if (!(at_loginFlags & AT_LOGIN_CUSTOMIZE))
1166 WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1);
1167 data << uint8(CHAR_CREATE_ERROR);
1168 SendPacket( &data );
1169 return;
1172 // prevent character rename to invalid name
1173 if (!normalizePlayerName(newname))
1175 WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1);
1176 data << uint8(CHAR_NAME_NO_NAME);
1177 SendPacket( &data );
1178 return;
1181 uint8 res = ObjectMgr::CheckPlayerName(newname,true);
1182 if (res != CHAR_NAME_SUCCESS)
1184 WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1);
1185 data << uint8(res);
1186 SendPacket( &data );
1187 return;
1190 // check name limitations
1191 if (GetSecurity() == SEC_PLAYER && sObjectMgr.IsReservedName(newname))
1193 WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1);
1194 data << uint8(CHAR_NAME_RESERVED);
1195 SendPacket( &data );
1196 return;
1199 // character with this name already exist
1200 if (uint64 newguid = sObjectMgr.GetPlayerGUIDByName(newname))
1202 if (newguid != guid)
1204 WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1);
1205 data << uint8(CHAR_CREATE_NAME_IN_USE);
1206 SendPacket( &data );
1207 return;
1211 CharacterDatabase.escape_string(newname);
1212 Player::Customize(guid, gender, skin, face, hairStyle, hairColor, facialHair);
1213 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));
1214 CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid ='%u'", GUID_LOPART(guid));
1216 std::string IP_str = GetRemoteAddress();
1217 sLog.outChar("Account: %d (IP: %s), Character guid: %u Customized to: %s", GetAccountId(), IP_str.c_str(), GUID_LOPART(guid), newname.c_str());
1219 WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1+8+(newname.size()+1)+6);
1220 data << uint8(RESPONSE_SUCCESS);
1221 data << uint64(guid);
1222 data << newname;
1223 data << uint8(gender);
1224 data << uint8(skin);
1225 data << uint8(face);
1226 data << uint8(hairStyle);
1227 data << uint8(hairColor);
1228 data << uint8(facialHair);
1229 SendPacket(&data);
1232 void WorldSession::HandleEquipmentSetSave(WorldPacket &recv_data)
1234 sLog.outDebug("CMSG_EQUIPMENT_SET_SAVE");
1236 uint64 setGuid;
1237 if(!recv_data.readPackGUID(setGuid))
1238 return;
1240 uint32 index;
1241 recv_data >> index;
1242 if(index >= MAX_EQUIPMENT_SET_INDEX) // client set slots amount
1243 return;
1245 std::string name;
1246 recv_data >> name;
1248 std::string iconName;
1249 recv_data >> iconName;
1251 EquipmentSet eqSet;
1253 eqSet.Guid = setGuid;
1254 eqSet.Name = name;
1255 eqSet.IconName = iconName;
1256 eqSet.state = EQUIPMENT_SET_NEW;
1258 for(uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i)
1260 uint64 itemGuid;
1261 if(!recv_data.readPackGUID(itemGuid))
1262 return;
1264 Item *item = _player->GetItemByPos(INVENTORY_SLOT_BAG_0, i);
1266 if(!item && itemGuid) // cheating check 1
1267 return;
1269 if(item && item->GetGUID() != itemGuid) // cheating check 2
1270 return;
1272 eqSet.Items[i] = GUID_LOPART(itemGuid);
1275 _player->SetEquipmentSet(index, eqSet);
1278 void WorldSession::HandleEquipmentSetDelete(WorldPacket &recv_data)
1280 sLog.outDebug("CMSG_EQUIPMENT_SET_DELETE");
1282 uint64 setGuid;
1283 if(!recv_data.readPackGUID(setGuid))
1284 return;
1286 _player->DeleteEquipmentSet(setGuid);
1289 void WorldSession::HandleEquipmentSetUse(WorldPacket &recv_data)
1291 sLog.outDebug("CMSG_EQUIPMENT_SET_USE");
1292 recv_data.hexlike();
1294 for(uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i)
1296 uint64 itemGuid;
1297 if(!recv_data.readPackGUID(itemGuid))
1298 return;
1300 uint8 srcbag, srcslot;
1301 recv_data >> srcbag >> srcslot;
1303 sLog.outDebug("Item " I64FMT ": srcbag %u, srcslot %u", itemGuid, srcbag, srcslot);
1305 Item *item = _player->GetItemByGuid(itemGuid);
1307 uint16 dstpos = i | (INVENTORY_SLOT_BAG_0 << 8);
1309 if(!item)
1311 Item *uItem = _player->GetItemByPos(INVENTORY_SLOT_BAG_0, i);
1312 if(!uItem)
1313 continue;
1315 ItemPosCountVec sDest;
1316 uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, sDest, uItem, false );
1317 if(msg == EQUIP_ERR_OK)
1319 _player->RemoveItem(INVENTORY_SLOT_BAG_0, i, true);
1320 _player->StoreItem( sDest, uItem, true );
1322 else
1323 _player->SendEquipError(msg, uItem, NULL);
1325 continue;
1328 if(item->GetPos() == dstpos)
1329 continue;
1331 _player->SwapItem(item->GetPos(), dstpos);
1334 WorldPacket data(SMSG_EQUIPMENT_SET_USE_RESULT, 1);
1335 data << uint8(0); // 4 - equipment swap failed - inventory is full
1336 SendPacket(&data);