diff options
author | maximius <none@none> | 2009-10-17 15:51:44 -0700 |
---|---|---|
committer | maximius <none@none> | 2009-10-17 15:51:44 -0700 |
commit | e585187b248f48b3c6e9247b49fa07c6565d65e5 (patch) | |
tree | 637c5b7ddacf41040bef4ea4f75a97da64c6a9bc /src/game/CharacterHandler.cpp | |
parent | 26b5e033ffde3d161382fc9addbfa99738379641 (diff) |
*Backed out changeset 3be01fb200a5
--HG--
branch : trunk
Diffstat (limited to 'src/game/CharacterHandler.cpp')
-rw-r--r-- | src/game/CharacterHandler.cpp | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/src/game/CharacterHandler.cpp b/src/game/CharacterHandler.cpp index e348d3b6f55..e1e6eae86a3 100644 --- a/src/game/CharacterHandler.cpp +++ b/src/game/CharacterHandler.cpp @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "Common.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" @@ -27,6 +28,7 @@ #include "Auth/md5.h" #include "Database/DatabaseEnv.h" #include "Database/DatabaseImpl.h" + #include "ArenaTeam.h" #include "Chat.h" #include "Group.h" @@ -41,6 +43,7 @@ #include "UpdateMask.h" #include "Util.h" #include "ScriptCalls.h" + class LoginQueryHolder : public SqlQueryHolder { private: @@ -53,10 +56,13 @@ class LoginQueryHolder : public SqlQueryHolder uint32 GetAccountId() const { return m_accountId; } bool Initialize(); }; + bool LoginQueryHolder::Initialize() { SetSize(MAX_PLAYER_LOGIN_QUERY); + bool res = true; + // NOTE: all fields in `characters` must be read to prevent lost character data at next save in case wrong DB structure. // !!! NOTE: including unused `zone`,`online` 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, instance_id, speccount, activespec FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid)); @@ -86,8 +92,10 @@ bool LoginQueryHolder::Initialize() res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGLYPHS, "SELECT spec, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6 FROM character_glyphs WHERE guid='%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADTALENTS, "SELECT spell, spec FROM character_talent WHERE guid='%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACCOUNTDATA, "SELECT type, time, data FROM character_account_data WHERE guid='%u'", GUID_LOPART(m_guid)); + return res; } + // don't call WorldSession directly // it may get deleted before the query callbacks get executed // instead pass an account id to this handler @@ -116,11 +124,15 @@ class CharacterHandler session->HandlePlayerLogin((LoginQueryHolder*)holder); } } chrHandler; + void WorldSession::HandleCharEnum(QueryResult * result) { WorldPacket data(SMSG_CHAR_ENUM, 100); // we guess size + uint8 num = 0; + data << num; + if( result ) { do @@ -131,11 +143,15 @@ void WorldSession::HandleCharEnum(QueryResult * result) ++num; } while( result->NextRow() ); + delete result; } + data.put<uint8>(0, num); + SendPacket( &data ); } + void WorldSession::HandleCharEnumOpcode( WorldPacket & /*recv_data*/ ) { /// get all the data necessary for loading all characters (along with their pets) on the account @@ -165,25 +181,32 @@ void WorldSession::HandleCharEnumOpcode( WorldPacket & /*recv_data*/ ) "WHERE characters.account = '%u' ORDER BY characters.guid", PET_SAVE_AS_CURRENT,GetAccountId()); } + void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) { std::string name; uint8 race_,class_; + recv_data >> name; + recv_data >> race_; recv_data >> class_; + WorldPacket data(SMSG_CHAR_CREATE, 1); // returned with diff.values in all cases + if(GetSecurity() == SEC_PLAYER) { if(uint32 mask = sWorld.getConfig(CONFIG_CHARACTERS_CREATING_DISABLED)) { bool disabled = false; + uint32 team = Player::TeamForRace(race_); switch(team) { case ALLIANCE: disabled = mask & (1<<0); break; case HORDE: disabled = mask & (1<<1); break; } + if(disabled) { data << (uint8)CHAR_CREATE_DISABLED; @@ -192,8 +215,10 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) } } } + ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(class_); ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(race_); + if( !classEntry || !raceEntry ) { data << (uint8)CHAR_CREATE_FAILED; @@ -201,6 +226,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) sLog.outError("Class: %u or Race %u not found in DBC (Wrong DBC files?) or Cheater?", class_, race_); return; } + // prevent character creating Expansion race without Expansion account if (raceEntry->addon > Expansion()) { @@ -209,6 +235,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) SendPacket( &data ); return; } + // prevent character creating Expansion class without Expansion account if (classEntry->addon > Expansion()) { @@ -217,6 +244,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) SendPacket( &data ); return; } + // prevent character creating with invalid name if (!normalizePlayerName(name)) { @@ -225,6 +253,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) sLog.outError("Account:[%d] but tried to Create character with empty [name] ",GetAccountId()); return; } + // check name limitations uint8 res = ObjectMgr::CheckPlayerName(name,true); if (res != CHAR_NAME_SUCCESS) @@ -233,24 +262,28 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) SendPacket( &data ); return; } + if (GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(name)) { data << (uint8)CHAR_NAME_RESERVED; SendPacket( &data ); return; } + if (objmgr.GetPlayerGUIDByName(name)) { data << (uint8)CHAR_CREATE_NAME_IN_USE; SendPacket( &data ); return; } + QueryResult *resultacct = loginDatabase.PQuery("SELECT SUM(numchars) FROM realmcharacters WHERE acctid = '%d'", GetAccountId()); if (resultacct) { Field *fields=resultacct->Fetch(); uint32 acctcharcount = fields[0].GetUInt32(); delete resultacct; + if (acctcharcount >= sWorld.getConfig(CONFIG_CHARACTERS_PER_ACCOUNT)) { data << (uint8)CHAR_CREATE_ACCOUNT_LIMIT; @@ -258,6 +291,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) return; } } + QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(guid) FROM characters WHERE account = '%d'", GetAccountId()); uint8 charcount = 0; if ( result ) @@ -265,6 +299,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) Field *fields=result->Fetch(); charcount = fields[0].GetUInt8(); delete result; + if (charcount >= sWorld.getConfig(CONFIG_CHARACTERS_PER_REALM)) { data << (uint8)CHAR_CREATE_SERVER_LIMIT; @@ -272,6 +307,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) return; } } + // speedup check for heroic class disabled case uint32 heroic_free_slots = sWorld.getConfig(CONFIG_HEROIC_CHARACTERS_PER_REALM); if(heroic_free_slots==0 && GetSecurity()==SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT) @@ -280,6 +316,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) SendPacket( &data ); return; } + // speedup check for heroic class disabled case uint32 req_level_for_heroic = sWorld.getConfig(CONFIG_MIN_LEVEL_FOR_HEROIC_CHARACTER_CREATING); if(GetSecurity()==SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT && req_level_for_heroic > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) @@ -288,11 +325,15 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) SendPacket( &data ); return; } + bool AllowTwoSideAccounts = !sWorld.IsPvPRealm() || sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || GetSecurity() > SEC_PLAYER; uint32 skipCinematics = sWorld.getConfig(CONFIG_SKIP_CINEMATICS); + bool have_same_race = false; + // if 0 then allowed creating without any characters bool have_req_level_for_heroic = (req_level_for_heroic==0); + if(!AllowTwoSideAccounts || skipCinematics == 1 || class_ == CLASS_DEATH_KNIGHT) { QueryResult *result2 = CharacterDatabase.PQuery("SELECT level,race,class FROM characters WHERE account = '%u' %s", @@ -300,8 +341,10 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) if(result2) { uint32 team_= Player::TeamForRace(race_); + Field* field = result2->Fetch(); uint8 acc_race = field[1].GetUInt32(); + if(GetSecurity()==SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT) { uint8 acc_class = field[2].GetUInt32(); @@ -309,6 +352,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) { if(heroic_free_slots > 0) --heroic_free_slots; + if(heroic_free_slots==0) { data << (uint8)CHAR_CREATE_UNIQUE_CLASS_LIMIT; @@ -316,6 +360,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) return; } } + if(!have_req_level_for_heroic) { uint32 acc_level = field[0].GetUInt32(); @@ -323,6 +368,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) have_req_level_for_heroic = true; } } + // need to check team only for first character // TODO: what to if account already has characters of both races? if (!AllowTwoSideAccounts) @@ -330,6 +376,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) uint32 acc_team=0; if(acc_race > 0) acc_team = Player::TeamForRace(acc_race); + if(acc_team != team_) { data << (uint8)CHAR_CREATE_PVP_TEAMS_VIOLATION; @@ -338,16 +385,20 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) return; } } + // search same race for cinematic or same class if need // TODO: check if cinematic already shown? (already logged in?; cinematic field) while ((skipCinematics == 1 && !have_same_race) || class_ == CLASS_DEATH_KNIGHT) { if(!result2->NextRow()) break; + field = result2->Fetch(); acc_race = field[1].GetUInt32(); + if(!have_same_race) have_same_race = race_ == acc_race; + if(GetSecurity()==SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT) { uint8 acc_class = field[2].GetUInt32(); @@ -355,6 +406,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) { if(heroic_free_slots > 0) --heroic_free_slots; + if(heroic_free_slots==0) { data << (uint8)CHAR_CREATE_UNIQUE_CLASS_LIMIT; @@ -362,6 +414,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) return; } } + if(!have_req_level_for_heroic) { uint32 acc_level = field[0].GetUInt32(); @@ -373,12 +426,14 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) delete result2; } } + if(GetSecurity()==SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT && !have_req_level_for_heroic) { data << (uint8)CHAR_CREATE_LEVEL_REQUIREMENT; SendPacket( &data ); return; } + // extract other data required for player creating uint8 gender, skin, face, hairStyle, hairColor, facialHair, outfitId; recv_data >> gender; @@ -388,44 +443,58 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) recv_data >> hairColor; recv_data >> facialHair; recv_data >> outfitId; + if(recv_data.rpos() < recv_data.wpos()) { uint8 unk; recv_data >> unk; sLog.outDebug("Character creation %s (account %u) has unhandled tail data: [%u]", name.c_str(), GetAccountId(), unk); } + Player * pNewChar = new Player(this); if(!pNewChar->Create( objmgr.GenerateLowGuid(HIGHGUID_PLAYER), name, race_, class_, gender, skin, face, hairStyle, hairColor, facialHair, outfitId )) { // Player not create (race/class problem?) delete pNewChar; + data << (uint8)CHAR_CREATE_ERROR; SendPacket( &data ); + return; } + if ((have_same_race && skipCinematics == 1) || skipCinematics == 2) pNewChar->setCinematic(1); // not show intro + // Player created, save it now pNewChar->SaveToDB(); charcount+=1; + loginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid= '%d' AND realmid = '%d'", GetAccountId(), realmID); loginDatabase.PExecute("INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (%u, %u, %u)", charcount, GetAccountId(), realmID); + delete pNewChar; // created only to call SaveToDB() + data << (uint8)CHAR_CREATE_SUCCESS; SendPacket( &data ); + std::string IP_str = GetRemoteAddress(); sLog.outDetail("Account: %d (IP: %s) Create Character:[%s]",GetAccountId(),IP_str.c_str(),name.c_str()); sLog.outChar("Account: %d (IP: %s) Create Character:[%s]",GetAccountId(),IP_str.c_str(),name.c_str()); } + void WorldSession::HandleCharDeleteOpcode( WorldPacket & recv_data ) { uint64 guid; recv_data >> guid; + // can't delete loaded character if(objmgr.GetPlayer(guid)) return; + uint32 accountId = 0; std::string name; + // is guild leader if(objmgr.GetGuildByLeader(guid)) { @@ -434,6 +503,7 @@ void WorldSession::HandleCharDeleteOpcode( WorldPacket & recv_data ) SendPacket( &data ); return; } + // is arena team captain if(objmgr.GetArenaTeamByCaptain(guid)) { @@ -442,6 +512,7 @@ void WorldSession::HandleCharDeleteOpcode( WorldPacket & recv_data ) SendPacket( &data ); return; } + QueryResult *result = CharacterDatabase.PQuery("SELECT account,name FROM characters WHERE guid='%u'", GUID_LOPART(guid)); if(result) { @@ -450,22 +521,28 @@ void WorldSession::HandleCharDeleteOpcode( WorldPacket & recv_data ) name = fields[1].GetCppString(); delete result; } + // prevent deleting other players' characters using cheating tools if(accountId != GetAccountId()) return; + std::string IP_str = GetRemoteAddress(); sLog.outDetail("Account: %d (IP: %s) Delete Character:[%s] (guid: %u)",GetAccountId(),IP_str.c_str(),name.c_str(),GUID_LOPART(guid)); sLog.outChar("Account: %d (IP: %s) Delete Character:[%s] (guid: %u)",GetAccountId(),IP_str.c_str(),name.c_str(),GUID_LOPART(guid)); + if(sLog.IsOutCharDump()) // optimize GetPlayerDump call { std::string dump = PlayerDumpWriter().GetDump(GUID_LOPART(guid)); sLog.outCharDump(dump.c_str(),GetAccountId(),GUID_LOPART(guid),name.c_str()); } + Player::DeleteFromDB(guid, GetAccountId()); + WorldPacket data(SMSG_CHAR_DELETE, 1); data << (uint8)CHAR_DELETE_SUCCESS; SendPacket( &data ); } + void WorldSession::HandlePlayerLoginOpcode( WorldPacket & recv_data ) { if(PlayerLoading() || GetPlayer() != NULL) @@ -473,10 +550,14 @@ void WorldSession::HandlePlayerLoginOpcode( WorldPacket & recv_data ) sLog.outError("Player tryes to login again, AccountId = %d",GetAccountId()); return; } + m_playerLoading = true; uint64 playerGuid = 0; + DEBUG_LOG( "WORLD: Recvd Player Logon Message" ); + recv_data >> playerGuid; + LoginQueryHolder *holder = new LoginQueryHolder(GetAccountId(), playerGuid); if(!holder->Initialize()) { @@ -484,14 +565,18 @@ void WorldSession::HandlePlayerLoginOpcode( WorldPacket & recv_data ) m_playerLoading = false; return; } + CharacterDatabase.DelayQueryHolder(&chrHandler, &CharacterHandler::HandlePlayerLoginCallback, holder); } + void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) { uint64 playerGuid = holder->GetGuid(); + Player* pCurrChar = new Player(this); // for send server info and strings (config) ChatHandler chH = ChatHandler(pCurrChar); + // "GetAccountId()==db stored account id" checked in LoadFromDB (prevent login not own character using cheating tools) if(!pCurrChar->LoadFromDB(GUID_LOPART(playerGuid), holder)) { @@ -501,9 +586,13 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) m_playerLoading = false; return; } + pCurrChar->GetMotionMaster()->Initialize(); + SetPlayer(pCurrChar); + pCurrChar->SendDungeonDifficulty(false); + WorldPacket data( SMSG_LOGIN_VERIFY_WORLD, 20 ); data << pCurrChar->GetMapId(); data << pCurrChar->GetPositionX(); @@ -511,20 +600,25 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) data << pCurrChar->GetPositionZ(); data << pCurrChar->GetOrientation(); SendPacket(&data); + // load player specific part before send times LoadAccountData(holder->GetResult(PLAYER_LOGIN_QUERY_LOADACCOUNTDATA),PER_CHARACTER_CACHE_MASK); SendAccountDataTimes(); + data.Initialize(SMSG_FEATURE_SYSTEM_STATUS, 2); // added in 2.2.0 data << uint8(2); // unknown value data << uint8(0); // enable(1)/disable(0) voice chat interface in client SendPacket(&data); + // Send MOTD { data.Initialize(SMSG_MOTD, 50); // new in 2.0.1 data << (uint32)0; + uint32 linecount=0; std::string str_motd = sWorld.GetMotd(); std::string::size_type pos, nextpos; + pos = 0; while ( (nextpos= str_motd.find('@',pos)) != std::string::npos ) { @@ -535,25 +629,33 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) } pos = nextpos+1; } + if (pos<str_motd.length()) { data << str_motd.substr(pos); ++linecount; } + data.put(0, linecount); + SendPacket( &data ); DEBUG_LOG( "WORLD: Sent motd (SMSG_MOTD)" ); + // send server info if(sWorld.getConfig(CONFIG_ENABLE_SINFO_LOGIN) == 1) chH.PSendSysMessage(_FULLVERSION); + DEBUG_LOG( "WORLD: Sent server info" ); } + data.Initialize(SMSG_LEARNED_DANCE_MOVES, 4+4); data << uint32(0); data << uint32(0); SendPacket(&data); + //QueryResult *result = CharacterDatabase.PQuery("SELECT guildid,rank FROM guild_member WHERE guid = '%u'",pCurrChar->GetGUIDLow()); QueryResult *resultGuild = holder->GetResult(PLAYER_LOGIN_QUERY_LOADGUILD); + if(resultGuild) { Field *fields = resultGuild->Fetch(); @@ -566,6 +668,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) pCurrChar->SetInGuild(0); pCurrChar->SetRank(0); } + if(pCurrChar->GetGuildId() != 0) { Guild* guild = objmgr.GetGuildById(pCurrChar->GetGuildId()); @@ -577,6 +680,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) data << guild->GetMOTD(); SendPacket(&data); DEBUG_LOG( "WORLD: Sent guild-motd (SMSG_GUILD_EVENT)" ); + data.Initialize(SMSG_GUILD_EVENT, (5+10)); // we guess size data<<(uint8)GE_SIGNED_ON; data<<(uint8)1; @@ -584,6 +688,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) data<<pCurrChar->GetGUID(); guild->BroadcastPacket(&data); DEBUG_LOG( "WORLD: Sent guild-signed-on (SMSG_GUILD_EVENT)" ); + // Increment online members of the guild guild->IncOnlineMemberCount(); } @@ -594,24 +699,30 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) pCurrChar->SetInGuild(0); } } + if(!pCurrChar->isAlive()) pCurrChar->SendCorpseReclaimDelay(true); + pCurrChar->SendInitialPacketsBeforeAddToMap(); + //Show cinematic at the first time that player login if( !pCurrChar->getCinematic() ) { pCurrChar->setCinematic(1); + if(ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(pCurrChar->getClass())) { if (cEntry->CinematicSequence) pCurrChar->SendCinematicStart(cEntry->CinematicSequence); else if (ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(pCurrChar->getRace())) pCurrChar->SendCinematicStart(rEntry->CinematicSequence); + // send new char string if not empty if (!sWorld.GetNewCharString().empty()) chH.PSendSysMessage(sWorld.GetNewCharString().c_str()); } } + if (!pCurrChar->GetMap()->Add(pCurrChar)) { AreaTrigger const* at = objmgr.GetGoBackTrigger(pCurrChar->GetMapId()); @@ -620,22 +731,29 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) else pCurrChar->TeleportTo(pCurrChar->m_homebindMapId, pCurrChar->m_homebindX, pCurrChar->m_homebindY, pCurrChar->m_homebindZ, pCurrChar->GetOrientation()); } + ObjectAccessor::Instance().AddObject(pCurrChar); //sLog.outDebug("Player %s added to Map.",pCurrChar->GetName()); + pCurrChar->SendInitialPacketsAfterAddToMap(); + CharacterDatabase.PExecute("UPDATE characters SET online = 1 WHERE guid = '%u'", pCurrChar->GetGUIDLow()); loginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = '%u'", GetAccountId()); pCurrChar->SetInGameTime( getMSTime() ); + // announce group about member online (must be after add to player list to receive announce to self) if(Group *group = pCurrChar->GetGroup()) { //pCurrChar->groupInfo.group->SendInit(this); // useless group->SendUpdate(); } + // friend status sSocialMgr.SendFriendStatus(pCurrChar, FRIEND_ONLINE, pCurrChar->GetGUIDLow(), true); + // Place character in world (and load zone) before some object loading pCurrChar->LoadCorpse(); + // setting Ghost+speed if dead if (pCurrChar->m_deathState != ALIVE) { @@ -643,57 +761,78 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) if(pCurrChar->getRace() == RACE_NIGHTELF) pCurrChar->CastSpell(pCurrChar, SPELL_ID_NE_GHOST, 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) pCurrChar->CastSpell(pCurrChar, SPELL_ID_GHOST, true, 0); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?) + pCurrChar->SetMovement(MOVE_WATER_WALK); } + pCurrChar->ContinueTaxiFlight(); + // reset for all pets before pet loading if(pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS)) Pet::resetTalentsForAllPetsOf(pCurrChar); + // Load pet if any (if player not alive and in taxi flight or another then pet will remember as temporary unsummoned) pCurrChar->LoadPet(); + // Set FFA PvP for non GM in non-rest mode if(sWorld.IsFFAPvPRealm() && !pCurrChar->isGameMaster() && !pCurrChar->HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_RESTING) ) pCurrChar->SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); + if(pCurrChar->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP)) pCurrChar->SetContestedPvP(); + // Apply at_login requests if(pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_SPELLS)) { pCurrChar->resetSpells(); SendNotification(LANG_RESET_SPELLS); } + if(pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_TALENTS)) { pCurrChar->resetTalents(true); pCurrChar->SendTalentsInfoData(false); // original talents send already in to SendInitialPacketsBeforeAddToMap, resend reset state SendNotification(LANG_RESET_TALENTS); } + // show time before shutdown if shutdown planned. if(sWorld.IsShutdowning()) sWorld.ShutdownMsg(true,pCurrChar); + if(sWorld.getConfig(CONFIG_ALL_TAXI_PATHS)) pCurrChar->SetTaxiCheater(true); + if(pCurrChar->isGameMaster()) SendNotification(LANG_GM_ON); + std::string IP_str = GetRemoteAddress(); sLog.outChar("Account: %d (IP: %s) Login Character:[%s] (guid:%u)", GetAccountId(),IP_str.c_str(),pCurrChar->GetName() ,pCurrChar->GetGUIDLow()); + if(!pCurrChar->IsStandState() && !pCurrChar->hasUnitState(UNIT_STAT_STUNNED)) pCurrChar->SetStandState(UNIT_STAND_STATE_STAND); + m_playerLoading = false; + //Hook for OnLogin Event Script->OnLogin(pCurrChar); + delete holder; } + void WorldSession::HandleSetFactionAtWar( WorldPacket & recv_data ) { DEBUG_LOG( "WORLD: Received CMSG_SET_FACTION_ATWAR" ); + uint32 repListID; uint8 flag; + recv_data >> repListID; recv_data >> flag; + GetPlayer()->GetReputationMgr().SetAtWar(repListID,flag); } + //I think this function is never used :/ I dunno, but i guess this opcode not exists void WorldSession::HandleSetFactionCheat( WorldPacket & /*recv_data*/ ) { @@ -701,9 +840,12 @@ void WorldSession::HandleSetFactionCheat( WorldPacket & /*recv_data*/ ) /* uint32 FactionID; uint32 Standing; + recv_data >> FactionID; recv_data >> Standing; + std::list<struct Factions>::iterator itr; + for(itr = GetPlayer()->factions.begin(); itr != GetPlayer()->factions.end(); ++itr) { if(itr->ReputationListID == FactionID) @@ -716,15 +858,19 @@ void WorldSession::HandleSetFactionCheat( WorldPacket & /*recv_data*/ ) */ GetPlayer()->GetReputationMgr().SendStates(); } + void WorldSession::HandleMeetingStoneInfo( WorldPacket & /*recv_data*/ ) { DEBUG_LOG( "WORLD: Received CMSG_MEETING_STONE_INFO" ); + SendLfgUpdate(0, 0, 0); } + void WorldSession::HandleTutorialFlag( WorldPacket & recv_data ) { uint32 iFlag; recv_data >> iFlag; + uint32 wInt = (iFlag / 32); if (wInt >= 8) { @@ -732,21 +878,26 @@ void WorldSession::HandleTutorialFlag( WorldPacket & recv_data ) return; } uint32 rInt = (iFlag % 32); + uint32 tutflag = GetTutorialInt( wInt ); tutflag |= (1 << rInt); SetTutorialInt( wInt, tutflag ); + //sLog.outDebug("Received Tutorial Flag Set {%u}.", iFlag); } + void WorldSession::HandleTutorialClear( WorldPacket & /*recv_data*/ ) { for (int i = 0; i < 8; ++i) SetTutorialInt( i, 0xFFFFFFFF ); } + void WorldSession::HandleTutorialReset( WorldPacket & /*recv_data*/ ) { for (int i = 0; i < 8; ++i) SetTutorialInt( i, 0x00000000 ); } + void WorldSession::HandleSetWatchedFactionOpcode(WorldPacket & recv_data) { DEBUG_LOG("WORLD: Received CMSG_SET_WATCHED_FACTION"); @@ -754,30 +905,37 @@ void WorldSession::HandleSetWatchedFactionOpcode(WorldPacket & recv_data) recv_data >> fact; GetPlayer()->SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fact); } + void WorldSession::HandleSetFactionInactiveOpcode(WorldPacket & recv_data) { DEBUG_LOG("WORLD: Received CMSG_SET_FACTION_INACTIVE"); uint32 replistid; uint8 inactive; recv_data >> replistid >> inactive; + _player->GetReputationMgr().SetInactive(replistid, inactive); } + void WorldSession::HandleShowingHelmOpcode( WorldPacket & /*recv_data*/ ) { DEBUG_LOG("CMSG_SHOWING_HELM for %s", _player->GetName()); _player->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM); } + void WorldSession::HandleShowingCloakOpcode( WorldPacket & /*recv_data*/ ) { DEBUG_LOG("CMSG_SHOWING_CLOAK for %s", _player->GetName()); _player->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK); } + void WorldSession::HandleCharRenameOpcode(WorldPacket& recv_data) { uint64 guid; std::string newname; + recv_data >> guid; recv_data >> newname; + // prevent character rename to invalid name if (!normalizePlayerName(newname)) { @@ -786,6 +944,7 @@ void WorldSession::HandleCharRenameOpcode(WorldPacket& recv_data) SendPacket( &data ); return; } + uint8 res = ObjectMgr::CheckPlayerName(newname,true); if (res != CHAR_NAME_SUCCESS) { @@ -794,6 +953,7 @@ void WorldSession::HandleCharRenameOpcode(WorldPacket& recv_data) SendPacket( &data ); return; } + // check name limitations if (GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname)) { @@ -802,8 +962,10 @@ void WorldSession::HandleCharRenameOpcode(WorldPacket& recv_data) SendPacket( &data ); return; } + std::string escaped_newname = newname; CharacterDatabase.escape_string(escaped_newname); + // make sure that the character belongs to the current account, that rename at login is enabled // and that there is no character with the desired new name CharacterDatabase.AsyncPQuery(&WorldSession::HandleChangePlayerNameOpcodeCallBack, @@ -812,6 +974,7 @@ void WorldSession::HandleCharRenameOpcode(WorldPacket& recv_data) GUID_LOPART(guid), GetAccountId(), AT_LOGIN_RENAME, AT_LOGIN_RENAME, escaped_newname.c_str() ); } + void WorldSession::HandleChangePlayerNameOpcodeCallBack(QueryResult *result, uint32 accountId, std::string newname) { WorldSession * session = sWorld.FindSession(accountId); @@ -820,6 +983,7 @@ void WorldSession::HandleChangePlayerNameOpcodeCallBack(QueryResult *result, uin if(result) delete result; return; } + if (!result) { WorldPacket data(SMSG_CHAR_RENAME, 1); @@ -827,23 +991,31 @@ void WorldSession::HandleChangePlayerNameOpcodeCallBack(QueryResult *result, uin session->SendPacket( &data ); return; } + uint32 guidLow = result->Fetch()[0].GetUInt32(); uint64 guid = MAKE_NEW_GUID(guidLow, 0, HIGHGUID_PLAYER); std::string oldname = result->Fetch()[1].GetCppString(); + delete result; + CharacterDatabase.PExecute("UPDATE characters set name = '%s', at_login = at_login & ~ %u WHERE guid ='%u'", newname.c_str(), uint32(AT_LOGIN_RENAME), guidLow); CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid ='%u'", guidLow); + 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()); + WorldPacket data(SMSG_CHAR_RENAME, 1+8+(newname.size()+1)); data << uint8(RESPONSE_SUCCESS); data << uint64(guid); data << newname; session->SendPacket(&data); } + void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recv_data) { uint64 guid; + recv_data >> guid; + // not accept declined names for unsupported languages std::string name; if(!objmgr.GetPlayerNameByGUID(guid, name)) @@ -854,6 +1026,7 @@ void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recv_data) SendPacket(&data); return; } + std::wstring wname; if(!Utf8toWStr(name, wname)) { @@ -863,6 +1036,7 @@ void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recv_data) SendPacket(&data); return; } + if(!isCyrillicCharacter(wname[0])) // name already stored as only single alphabet using { WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8); @@ -871,9 +1045,12 @@ void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recv_data) SendPacket(&data); return; } + std::string name2; DeclinedName declinedname; + recv_data >> name2; + if(name2 != name) // character have different name { WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8); @@ -882,6 +1059,7 @@ void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recv_data) SendPacket(&data); return; } + for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) { recv_data >> declinedname.name[i]; @@ -894,6 +1072,7 @@ void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recv_data) return; } } + if(!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname, 0), declinedname)) { WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8); @@ -902,30 +1081,41 @@ void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recv_data) SendPacket(&data); return; } + for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) CharacterDatabase.escape_string(declinedname.name[i]); + CharacterDatabase.BeginTransaction(); CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid = '%u'", GUID_LOPART(guid)); CharacterDatabase.PExecute("INSERT INTO character_declinedname (guid, genitive, dative, accusative, instrumental, prepositional) VALUES ('%u','%s','%s','%s','%s','%s')", 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()); CharacterDatabase.CommitTransaction(); + WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8); data << uint32(0); // OK data << uint64(guid); SendPacket(&data); } + void WorldSession::HandleAlterAppearance( WorldPacket & recv_data ) { sLog.outDebug("CMSG_ALTER_APPEARANCE"); + uint32 Hair, Color, FacialHair; recv_data >> Hair >> Color >> FacialHair; + BarberShopStyleEntry const* bs_hair = sBarberShopStyleStore.LookupEntry(Hair); + if(!bs_hair || bs_hair->type != 0 || bs_hair->race != _player->getRace() || bs_hair->gender != _player->getGender()) return; + BarberShopStyleEntry const* bs_facialHair = sBarberShopStyleStore.LookupEntry(FacialHair); + if(!bs_facialHair || bs_facialHair->type != 2 || bs_facialHair->race != _player->getRace() || bs_facialHair->gender != _player->getGender()) return; + uint32 Cost = _player->GetBarberShopCost(bs_hair->hair_id, Color, bs_facialHair->hair_id); + // 0 - ok // 1,3 - not enough money // 2 - you have to seat on barber chair @@ -942,23 +1132,30 @@ void WorldSession::HandleAlterAppearance( WorldPacket & recv_data ) data << uint32(0); // ok SendPacket(&data); } + _player->ModifyMoney(-int32(Cost)); // it isn't free _player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER, Cost); + _player->SetByteValue(PLAYER_BYTES, 2, uint8(bs_hair->hair_id)); _player->SetByteValue(PLAYER_BYTES, 3, uint8(Color)); _player->SetByteValue(PLAYER_BYTES_2, 0, uint8(bs_facialHair->hair_id)); + _player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP, 1); + _player->SetStandState(0); // stand up } + void WorldSession::HandleRemoveGlyph( WorldPacket & recv_data ) { uint32 slot; recv_data >> slot; + if(slot >= MAX_GLYPH_SLOT_INDEX) { sLog.outDebug("Client sent wrong glyph slot number in opcode CMSG_REMOVE_GLYPH %u", slot); return; } + if(uint32 glyph = _player->GetGlyph(slot)) { if(GlyphPropertiesEntry const *gp = sGlyphPropertiesStore.LookupEntry(glyph)) @@ -969,14 +1166,18 @@ void WorldSession::HandleRemoveGlyph( WorldPacket & recv_data ) } } } + void WorldSession::HandleCharCustomize(WorldPacket& recv_data) { uint64 guid; std::string newname; + recv_data >> guid; recv_data >> newname; + uint8 gender, skin, face, hairStyle, hairColor, facialHair; recv_data >> gender >> skin >> hairColor >> hairStyle >> facialHair >> face; + QueryResult *result = CharacterDatabase.PQuery("SELECT at_login FROM characters WHERE guid ='%u'", GUID_LOPART(guid)); if (!result) { @@ -985,9 +1186,11 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data) SendPacket( &data ); return; } + Field *fields = result->Fetch(); uint32 at_loginFlags = fields[0].GetUInt32(); delete result; + if (!(at_loginFlags & AT_LOGIN_CUSTOMIZE)) { WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); @@ -995,6 +1198,7 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data) SendPacket( &data ); return; } + // prevent character rename to invalid name if (!normalizePlayerName(newname)) { @@ -1003,6 +1207,7 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data) SendPacket( &data ); return; } + uint8 res = ObjectMgr::CheckPlayerName(newname,true); if (res != CHAR_NAME_SUCCESS) { @@ -1011,6 +1216,7 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data) SendPacket( &data ); return; } + // check name limitations if (GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname)) { @@ -1019,6 +1225,7 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data) SendPacket( &data ); return; } + // character with this name already exist if (uint64 newguid = objmgr.GetPlayerGUIDByName(newname)) { @@ -1030,6 +1237,7 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data) return; } } + CharacterDatabase.escape_string(newname); if(QueryResult *result = CharacterDatabase.PQuery("SELECT name FROM characters WHERE guid ='%u'", GUID_LOPART(guid))) { @@ -1040,6 +1248,7 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data) Player::Customize(guid, gender, skin, face, hairStyle, hairColor, facialHair); 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)); CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid ='%u'", GUID_LOPART(guid)); + WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1+8+(newname.size()+1)+6); data << uint8(RESPONSE_SUCCESS); data << uint64(guid); @@ -1052,66 +1261,90 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data) data << uint8(facialHair); SendPacket(&data); } + void WorldSession::HandleEquipmentSetSave(WorldPacket &recv_data) { sLog.outDebug("CMSG_EQUIPMENT_SET_SAVE"); + uint64 setGuid; if(!recv_data.readPackGUID(setGuid)) return; + uint32 index; recv_data >> index; if(index >= MAX_EQUIPMENT_SET_INDEX) // client set slots amount return; + std::string name; recv_data >> name; + std::string iconName; recv_data >> iconName; + EquipmentSet eqSet; + eqSet.Guid = setGuid; eqSet.Name = name; eqSet.IconName = iconName; eqSet.state = EQUIPMENT_SET_NEW; + for(uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i) { uint64 itemGuid; if(!recv_data.readPackGUID(itemGuid)) return; + Item *item = _player->GetItemByPos(INVENTORY_SLOT_BAG_0, i); + if(!item && itemGuid) // cheating check 1 return; + if(item && item->GetGUID() != itemGuid) // cheating check 2 return; + eqSet.Items[i] = GUID_LOPART(itemGuid); } + _player->SetEquipmentSet(index, eqSet); } + void WorldSession::HandleEquipmentSetDelete(WorldPacket &recv_data) { sLog.outDebug("CMSG_EQUIPMENT_SET_DELETE"); + uint64 setGuid; if(!recv_data.readPackGUID(setGuid)) return; + _player->DeleteEquipmentSet(setGuid); } + void WorldSession::HandleEquipmentSetUse(WorldPacket &recv_data) { sLog.outDebug("CMSG_EQUIPMENT_SET_USE"); recv_data.hexlike(); + for(uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i) { uint64 itemGuid; if(!recv_data.readPackGUID(itemGuid)) return; + uint8 srcbag, srcslot; recv_data >> srcbag >> srcslot; + sLog.outDebug("Item " UI64FMTD ": srcbag %u, srcslot %u", itemGuid, srcbag, srcslot); + Item *item = _player->GetItemByGuid(itemGuid); + uint16 dstpos = i | (INVENTORY_SLOT_BAG_0 << 8); + if(!item) { Item *uItem = _player->GetItemByPos(INVENTORY_SLOT_BAG_0, i); if(!uItem) continue; + ItemPosCountVec sDest; uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, sDest, uItem, false ); if(msg == EQUIP_ERR_OK) @@ -1121,48 +1354,61 @@ void WorldSession::HandleEquipmentSetUse(WorldPacket &recv_data) } else _player->SendEquipError(msg, uItem, NULL); + continue; } + if(item->GetPos() == dstpos) continue; + _player->SwapItem(item->GetPos(), dstpos); } + WorldPacket data(SMSG_EQUIPMENT_SET_USE_RESULT, 1); data << uint8(0); // 4 - equipment swap failed - inventory is full SendPacket(&data); } + void WorldSession::HandleOnPVPKill(Player *killed) { Script->OnPVPKill(GetPlayer(), killed); } + bool WorldSession::HandleOnPlayerChat(const char *text) { return Script->OnPlayerChat(GetPlayer(), text); } + uint32 WorldSession::HandleOnGetXP(uint32 amount) { return Script->OnGetXP(GetPlayer(), amount); } + int32 WorldSession::HandleOnGetMoney(int32 amount) { return Script->OnGetMoney(GetPlayer(), amount); } + void WorldSession::HandleOnAreaChange(AreaTableEntry const *pArea) { Script->OnAreaChange(GetPlayer(), pArea); } + bool WorldSession::HandleOnItemClick(Item *pItem) { return Script->OnItemClick(GetPlayer(), pItem); } + bool WorldSession::HandleOnItemOpen(Item *pItem) { return Script->OnItemOpen(GetPlayer(), pItem); } + bool WorldSession::HandleOnGoClick(GameObject *pGameObject) { return Script->OnGoClick(GetPlayer(), pGameObject); } + void WorldSession::HandleOnCreatureKill(Creature *pCreature) { Script->OnCreatureKill(GetPlayer(), pCreature); |