diff options
Diffstat (limited to 'src/game/CharacterHandler.cpp')
-rw-r--r-- | src/game/CharacterHandler.cpp | 2248 |
1 files changed, 1124 insertions, 1124 deletions
diff --git a/src/game/CharacterHandler.cpp b/src/game/CharacterHandler.cpp index 036de1e7399..1797b4cc7a1 100644 --- a/src/game/CharacterHandler.cpp +++ b/src/game/CharacterHandler.cpp @@ -1,1124 +1,1124 @@ -/*
- * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * 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 "Database/DatabaseEnv.h"
-#include "WorldPacket.h"
-#include "SharedDefines.h"
-#include "WorldSession.h"
-#include "Opcodes.h"
-#include "Log.h"
-#include "World.h"
-#include "ObjectMgr.h"
-#include "Player.h"
-#include "Guild.h"
-#include "UpdateMask.h"
-#include "Auth/md5.h"
-#include "MapManager.h"
-#include "ObjectAccessor.h"
-#include "Group.h"
-#include "Database/DatabaseImpl.h"
-#include "PlayerDump.h"
-#include "SocialMgr.h"
-#include "Util.h"
-#include "Language.h"
-
-class LoginQueryHolder : public SqlQueryHolder
-{
- private:
- uint32 m_accountId;
- uint64 m_guid;
- public:
- LoginQueryHolder(uint32 accountId, uint64 guid)
- : m_accountId(accountId), m_guid(guid) { }
- uint64 GetGuid() const { return m_guid; }
- 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, 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, gmstate, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGROUP, "SELECT leaderGuid FROM group_member WHERE memberGuid ='%u'", GUID_LOPART(m_guid));
- 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));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADAURAS, "SELECT caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'", GUID_LOPART(m_guid));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLS, "SELECT spell,slot,active,disabled FROM character_spell WHERE guid = '%u'", GUID_LOPART(m_guid));
- 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));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS,"SELECT quest,time FROM character_queststatus_daily WHERE guid = '%u'", GUID_LOPART(m_guid));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADTUTORIALS, "SELECT tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7 FROM character_tutorial WHERE account = '%u' AND realmid = '%u'", GetAccountId(), realmID);
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADREPUTATION, "SELECT faction,standing,flags FROM character_reputation WHERE guid = '%u'", GUID_LOPART(m_guid));
- 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));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACTIONS, "SELECT button,action,type,misc FROM character_action WHERE guid = '%u' ORDER BY button", GUID_LOPART(m_guid));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMAILCOUNT, "SELECT COUNT(id) FROM mail WHERE receiver = '%u' AND (checked & 1)=0 AND deliver_time <= '" I64FMTD "'", GUID_LOPART(m_guid),(uint64)time(NULL));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMAILDATE, "SELECT MIN(deliver_time) FROM mail WHERE receiver = '%u' AND (checked & 1)=0", GUID_LOPART(m_guid));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSOCIALLIST, "SELECT friend,flags,note FROM character_social WHERE guid = '%u' LIMIT 255", GUID_LOPART(m_guid));
- 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));
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS, "SELECT spell,item,time FROM character_spell_cooldown WHERE guid = '%u'", GUID_LOPART(m_guid));
- if(sWorld.getConfig(CONFIG_DECLINED_NAMES_USED))
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_declinedname WHERE guid = '%u'",GUID_LOPART(m_guid));
- // in other case still be dummy query
- res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGUILD, "SELECT guildid,rank FROM guild_member 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
-class CharacterHandler
-{
- public:
- void HandleCharEnumCallback(QueryResult * result, uint32 account)
- {
- WorldSession * session = sWorld.FindSession(account);
- if(!session)
- {
- delete result;
- return;
- }
- session->HandleCharEnum(result);
- }
- void HandlePlayerLoginCallback(QueryResult * /*dummy*/, SqlQueryHolder * holder)
- {
- if (!holder) return;
- WorldSession *session = sWorld.FindSession(((LoginQueryHolder*)holder)->GetAccountId());
- if(!session)
- {
- delete holder;
- return;
- }
- session->HandlePlayerLogin((LoginQueryHolder*)holder);
- }
-} chrHandler;
-
-void WorldSession::HandleCharEnum(QueryResult * result)
-{
- // keys can be non cleared if player open realm list and close it by 'cancel'
- loginDatabase.PExecute("UPDATE account SET v = '0', s = '0' WHERE id = '%u'", GetAccountId());
-
- WorldPacket data(SMSG_CHAR_ENUM, 100); // we guess size
-
- uint8 num = 0;
-
- data << num;
-
- if( result )
- {
- Player *plr = new Player(this);
- do
- {
- sLog.outDetail("Loading char guid %u from account %u.",(*result)[0].GetUInt32(),GetAccountId());
-
- if(plr->MinimalLoadFromDB( result, (*result)[0].GetUInt32() ))
- {
- plr->BuildEnumData( result, &data );
- ++num;
- }
- }
- while( result->NextRow() );
-
- delete plr;
- 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
- CharacterDatabase.AsyncPQuery(&chrHandler, &CharacterHandler::HandleCharEnumCallback, GetAccountId(),
- !sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) ?
- // ------- Query Without Declined Names --------
- // 0 1 2 3 4 5 6 7 8
- "SELECT characters.data, characters.name, characters.position_x, characters.position_y, characters.position_z, characters.map, characters.totaltime, characters.leveltime, characters.at_login, "
- // 9 10 11
- "character_pet.entry, character_pet.modelid, character_pet.level "
- "FROM characters LEFT JOIN character_pet ON characters.guid=character_pet.owner AND character_pet.slot='0' "
- "WHERE characters.account = '%u' ORDER BY characters.guid"
- :
- // --------- Query With Declined Names ---------
- // 0 1 2 3 4 5 6 7 8
- "SELECT characters.data, characters.name, characters.position_x, characters.position_y, characters.position_z, characters.map, characters.totaltime, characters.leveltime, characters.at_login, "
- // 9 10 11 12
- "character_pet.entry, character_pet.modelid, character_pet.level, genitive "
- "FROM characters LEFT JOIN character_pet ON characters.guid = character_pet.owner AND character_pet.slot='0' "
- "LEFT JOIN character_declinedname ON characters.guid = character_declinedname.guid "
- "WHERE characters.account = '%u' ORDER BY characters.guid",
- GetAccountId());
-}
-
-void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,1+1+1+1+1+1+1+1+1+1);
-
- std::string name;
- uint8 race_,class_;
- bool pTbc = this->IsTBC() && sWorld.getConfig(CONFIG_EXPANSION) > 0;
- recv_data >> name;
-
- // recheck with known string size
- CHECK_PACKET_SIZE(recv_data,(name.size()+1)+1+1+1+1+1+1+1+1+1);
-
- 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;
- SendPacket( &data );
- return;
- }
- }
- }
-
- if (!sChrClassesStore.LookupEntry(class_)||
- !sChrRacesStore.LookupEntry(race_))
- {
- data << (uint8)CHAR_CREATE_FAILED;
- SendPacket( &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 (!pTbc&&(race_>RACE_TROLL))
- {
- data << (uint8)CHAR_CREATE_EXPANSION;
- sLog.outError("No Expansion Account:[%d] but tried to Create TBC character",GetAccountId());
- SendPacket( &data );
- return;
- }
-
- // prevent character creating with invalid name
- if(!normalizePlayerName(name))
- {
- data << (uint8)CHAR_NAME_INVALID_CHARACTER;
- SendPacket( &data );
- sLog.outError("Account:[%d] but tried to Create character with empty [name] ",GetAccountId());
- return;
- }
-
- // check name limitations
- if(!ObjectMgr::IsValidName(name,true))
- {
- data << (uint8)CHAR_NAME_INVALID_CHARACTER;
- 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;
- SendPacket( &data );
- return;
- }
- }
-
- QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(guid) FROM characters WHERE account = '%d'", GetAccountId());
- uint8 charcount = 0;
- if ( result )
- {
- Field *fields=result->Fetch();
- charcount = fields[0].GetUInt8();
- delete result;
-
- if (charcount >= sWorld.getConfig(CONFIG_CHARACTERS_PER_REALM))
- {
- data << (uint8)CHAR_CREATE_SERVER_LIMIT;
- 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(!AllowTwoSideAccounts || skipCinematics == 1)
- {
- QueryResult *result2 = CharacterDatabase.PQuery("SELECT DISTINCT race FROM characters WHERE account = '%u' %s", GetAccountId(),skipCinematics == 1 ? "" : "LIMIT 1");
- if(result2)
- {
- uint32 team_= Player::TeamForRace(race_);
-
- Field* field = result2->Fetch();
- uint8 race = field[0].GetUInt32();
-
- // need to check team only for first character
- // TODO: what to if account already has characters of both races?
- if (!AllowTwoSideAccounts)
- {
- uint32 team=0;
- if(race > 0)
- team = Player::TeamForRace(race);
-
- if(team != team_)
- {
- data << (uint8)CHAR_CREATE_PVP_TEAMS_VIOLATION;
- SendPacket( &data );
- delete result2;
- return;
- }
- }
-
- if (skipCinematics == 1)
- {
- // TODO: check if cinematic already shown? (already logged in?; cinematic field)
- while (race_ != race && result2->NextRow())
- {
- field = result2->Fetch();
- race = field[0].GetUInt32();
- }
- have_same_race = race_ == race;
- }
- delete result2;
- }
- }
-
- // extract other data required for player creating
- uint8 gender, skin, face, hairStyle, hairColor, facialHair, outfitId;
- recv_data >> gender >> skin >> face;
- recv_data >> hairStyle >> hairColor >> facialHair >> outfitId;
-
- 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().c_str();
- sLog.outBasic("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 )
-{
- CHECK_PACKET_SIZE(recv_data,8);
-
- 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))
- {
- WorldPacket data(SMSG_CHAR_DELETE, 1);
- data << (uint8)CHAR_DELETE_FAILED_GUILD_LEADER;
- SendPacket( &data );
- return;
- }
-
- // is arena team captain
- if(objmgr.GetArenaTeamByCapitan(guid))
- {
- WorldPacket data(SMSG_CHAR_DELETE, 1);
- data << (uint8)CHAR_DELETE_FAILED_ARENA_CAPTAIN;
- SendPacket( &data );
- return;
- }
-
- QueryResult *result = CharacterDatabase.PQuery("SELECT account,name FROM characters WHERE guid='%u'", GUID_LOPART(guid));
- if(result)
- {
- Field *fields = result->Fetch();
- accountId = fields[0].GetUInt32();
- name = fields[1].GetCppString();
- delete result;
- }
-
- // prevent deleting other players' characters using cheating tools
- if(accountId != GetAccountId())
- return;
-
- std::string IP_str = GetRemoteAddress();
- sLog.outBasic("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 )
-{
- CHECK_PACKET_SIZE(recv_data,8);
-
- 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())
- {
- delete holder; // delete all unprocessed queries
- m_playerLoading = false;
- return;
- }
-
- CharacterDatabase.DelayQueryHolder(&chrHandler, &CharacterHandler::HandlePlayerLoginCallback, holder);
-}
-
-void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder)
-{
- uint64 playerGuid = holder->GetGuid();
-
- Player* pCurrChar = new Player(this);
- pCurrChar->GetMotionMaster()->Initialize();
-
- // "GetAccountId()==db stored account id" checked in LoadFromDB (prevent login not own character using cheating tools)
- if(!pCurrChar->LoadFromDB(GUID_LOPART(playerGuid), holder))
- {
- KickPlayer(); // disconnect client, player no set to session and it will not deleted or saved at kick
- delete pCurrChar; // delete it manually
- delete holder; // delete all unprocessed queries
- m_playerLoading = false;
- return;
- }
-
- SetPlayer(pCurrChar);
-
- pCurrChar->SendDungeonDifficulty(false);
-
- WorldPacket data( SMSG_LOGIN_VERIFY_WORLD, 20 );
- data << pCurrChar->GetMapId();
- data << pCurrChar->GetPositionX();
- data << pCurrChar->GetPositionY();
- data << pCurrChar->GetPositionZ();
- data << pCurrChar->GetOrientation();
- SendPacket(&data);
-
- data.Initialize( SMSG_ACCOUNT_DATA_TIMES, 128 );
- for(int i = 0; i < 32; i++)
- data << uint32(0);
- SendPacket(&data);
-
- 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 )
- {
- if (nextpos != pos)
- {
- data << str_motd.substr(pos,nextpos-pos);
- ++linecount;
- }
- 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)" );
- }
-
- if(pCurrChar->GetGuildId() != 0)
- {
- Guild* guild = objmgr.GetGuildById(pCurrChar->GetGuildId());
- if(guild)
- {
- data.Initialize(SMSG_GUILD_EVENT, (2+guild->GetMOTD().size()+1));
- data << (uint8)GE_MOTD;
- data << (uint8)1;
- 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;
- data<<pCurrChar->GetName();
- data<<pCurrChar->GetGUID();
- guild->BroadcastPacket(&data);
- DEBUG_LOG( "WORLD: Sent guild-signed-on (SMSG_GUILD_EVENT)" );
-
- // Increment online members of the guild
- guild->IncOnlineMemberCount();
- }
- else
- {
- // remove wrong guild data
- 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());
- pCurrChar->SetUInt32Value(PLAYER_GUILDID,0);
- pCurrChar->SetUInt32ValueInDB(PLAYER_GUILDID,0,pCurrChar->GetGUID());
- }
- }
-
- if(!pCurrChar->isAlive())
- pCurrChar->SendCorpseReclaimDelay(true);
-
- pCurrChar->SendInitialPacketsBeforeAddToMap();
-
- //Show cinematic at the first time that player login
- if( !pCurrChar->getCinematic() )
- {
- pCurrChar->setCinematic(1);
-
- ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(pCurrChar->getRace());
- if(rEntry)
- {
- data.Initialize( SMSG_TRIGGER_CINEMATIC,4 );
- data << uint32(rEntry->startmovie);
- 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();
- pCurrChar->SetInGuild(fields[0].GetUInt32());
- pCurrChar->SetRank(fields[1].GetUInt32());
- delete resultGuild;
- }
- else if(pCurrChar->GetGuildId()) // clear guild related fields in case wrong data about non existed membership
- {
- pCurrChar->SetInGuild(0);
- pCurrChar->SetRank(0);
- }
-
- if (!MapManager::Instance().GetMap(pCurrChar->GetMapId(), pCurrChar)->Add(pCurrChar))
- {
- AreaTrigger const* at = objmgr.GetGoBackTrigger(pCurrChar->GetMapId());
- if(at)
- pCurrChar->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, pCurrChar->GetOrientation());
- 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->GetSocial()->SendSocialList();
-
- 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 == DEAD )
- if (pCurrChar->m_deathState != ALIVE)
- {
- // not blizz like, we must correctly save and load player instead...
- if(pCurrChar->getRace() == RACE_NIGHTELF)
- pCurrChar->CastSpell(pCurrChar, 20584, true, 0);// auras SPELL_AURA_INCREASE_SPEED(+speed in wisp form), SPELL_AURA_INCREASE_SWIM_SPEED(+swim speed in wisp form), SPELL_AURA_TRANSFORM (to wisp form)
- pCurrChar->CastSpell(pCurrChar, 8326, true, 0); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?)
-
- //pCurrChar->SetUInt32Value(UNIT_FIELD_AURA+41, 8326);
- //pCurrChar->SetUInt32Value(UNIT_FIELD_AURA+42, 20584);
- //pCurrChar->SetUInt32Value(UNIT_FIELD_AURAFLAGS+6, 238);
- //pCurrChar->SetUInt32Value(UNIT_FIELD_AURALEVELS+11, 514);
- //pCurrChar->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS+11, 65535);
- //pCurrChar->SetUInt32Value(UNIT_FIELD_DISPLAYID, 1825);
- //if (pCurrChar->getRace() == RACE_NIGHTELF)
- //{
- // pCurrChar->SetSpeed(MOVE_RUN, 1.5f*1.2f, true);
- // pCurrChar->SetSpeed(MOVE_SWIM, 1.5f*1.2f, true);
- //}
- //else
- //{
- // pCurrChar->SetSpeed(MOVE_RUN, 1.5f, true);
- // pCurrChar->SetSpeed(MOVE_SWIM, 1.5f, true);
- //}
- pCurrChar->SetMovement(MOVE_WATER_WALK);
- }
-
- if(uint32 sourceNode = pCurrChar->m_taxi.GetTaxiSource())
- {
-
- sLog.outDebug( "WORLD: Restart character %u taxi flight", pCurrChar->GetGUIDLow() );
-
- uint32 MountId = objmgr.GetTaxiMount(sourceNode, pCurrChar->GetTeam());
- uint32 path = pCurrChar->m_taxi.GetCurrentTaxiPath();
-
- // search appropriate start path node
- uint32 startNode = 0;
-
- TaxiPathNodeList const& nodeList = sTaxiPathNodesByPath[path];
-
- float distPrev = MAP_SIZE*MAP_SIZE;
- float distNext =
- (nodeList[0].x-pCurrChar->GetPositionX())*(nodeList[0].x-pCurrChar->GetPositionX())+
- (nodeList[0].y-pCurrChar->GetPositionY())*(nodeList[0].y-pCurrChar->GetPositionY())+
- (nodeList[0].z-pCurrChar->GetPositionZ())*(nodeList[0].z-pCurrChar->GetPositionZ());
-
- for(uint32 i = 1; i < nodeList.size(); ++i)
- {
- TaxiPathNode const& node = nodeList[i];
- TaxiPathNode const& prevNode = nodeList[i-1];
-
- // skip nodes at another map
- if(node.mapid != pCurrChar->GetMapId())
- continue;
-
- distPrev = distNext;
-
- distNext =
- (node.x-pCurrChar->GetPositionX())*(node.x-pCurrChar->GetPositionX())+
- (node.y-pCurrChar->GetPositionY())*(node.y-pCurrChar->GetPositionY())+
- (node.z-pCurrChar->GetPositionZ())*(node.z-pCurrChar->GetPositionZ());
-
- float distNodes =
- (node.x-prevNode.x)*(node.x-prevNode.x)+
- (node.y-prevNode.y)*(node.y-prevNode.y)+
- (node.z-prevNode.z)*(node.z-prevNode.z);
-
- if(distNext + distPrev < distNodes)
- {
- startNode = i;
- break;
- }
- }
-
- SendDoFlight( MountId, path, startNode );
- }
-
- // Load pet if any and player is alive and not in taxi flight
- if(pCurrChar->isAlive() && pCurrChar->m_taxi.GetTaxiSource()==0)
- 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->SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_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);
- SendNotification(LANG_RESET_TALENTS);
- }
-
- // show time before shutdown if shutdown planned.
- if(sWorld.IsShutdowning())
- sWorld.ShutdownMsg(true,pCurrChar);
-
- if(sWorld.getConfig(CONFIG_ALWAYS_MAXSKILL)) // Max weapon skill when logging in
- pCurrChar->UpdateSkillsToMaxSkillsForLevel();
-
- //ImpConfig - Check if player has logged in before
- QueryResult *result = CharacterDatabase.PQuery("SELECT guid FROM has_logged_in_before WHERE guid = %u",pCurrChar->GetGUIDLow());
- if(!result)
- {
- sLog.outBasic("Character '%s' logging in for first time, applying skills and stuff",pCurrChar->GetName());
- CharacterDatabase.PExecute("INSERT INTO has_logged_in_before VALUES (%u)",pCurrChar->GetGUIDLow());
-
- //Reputations if "StartAllReputation" is enabled, -- TODO: Fix this in a better way
- if(sWorld.getConfig(CONFIG_START_ALL_REP))
- {
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(942),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(935),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(936),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(1011),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(970),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(967),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(989),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(932),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(934),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(1038),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(1077),42999);
-
- // Factions depending on team, like cities and some more stuff
- switch(pCurrChar->GetTeam())
- {
- case ALLIANCE:
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(72),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(47),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(69),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(930),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(730),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(978),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(54),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(946),42999);
- break;
- case HORDE:
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(76),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(68),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(81),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(911),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(729),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(941),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(530),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(947),42999);
- break;
- }
- }
- }
- else
- sLog.outBasic("Character '%s' has logged in before",pCurrChar->GetName());
-
- if(sWorld.getConfig(CONFIG_START_ALL_TAXI))
- 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->GetGUID());
-
- m_playerLoading = false;
- delete holder;
-}
-
-void WorldSession::HandleSetFactionAtWar( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,4+1);
-
- DEBUG_LOG( "WORLD: Received CMSG_SET_FACTION_ATWAR" );
-
- uint32 repListID;
- uint8 flag;
-
- recv_data >> repListID;
- recv_data >> flag;
-
- FactionStateList::iterator itr = GetPlayer()->m_factions.find(repListID);
- if (itr == GetPlayer()->m_factions.end())
- return;
-
- // always invisible or hidden faction can't change war state
- if(itr->second.Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN) )
- return;
-
- GetPlayer()->SetFactionAtWar(&itr->second,flag);
-}
-
-//I think this function is never used :/ I dunno, but i guess this opcode not exists
-void WorldSession::HandleSetFactionCheat( WorldPacket & /*recv_data*/ )
-{
- //CHECK_PACKET_SIZE(recv_data,4+4);
-
- //sLog.outDebug("WORLD SESSION: HandleSetFactionCheat");
- /*
- 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)
- {
- itr->Standing += Standing;
- itr->Flags = (itr->Flags | 1);
- break;
- }
- }
- */
- GetPlayer()->UpdateReputation();
-}
-
-void WorldSession::HandleMeetingStoneInfo( WorldPacket & /*recv_data*/ )
-{
- DEBUG_LOG( "WORLD: Received CMSG_MEETING_STONE_INFO" );
-
- WorldPacket data(SMSG_MEETINGSTONE_SETQUEUE, 5);
- data << uint32(0) << uint8(6);
- SendPacket(&data);
-}
-
-void WorldSession::HandleTutorialFlag( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,4);
-
- uint32 iFlag;
- recv_data >> iFlag;
-
- uint32 wInt = (iFlag / 32);
- if (wInt >= 8)
- {
- //sLog.outError("CHEATER? Account:[%d] Guid[%u] tried to send wrong CMSG_TUTORIAL_FLAG", GetAccountId(),GetGUID());
- return;
- }
- uint32 rInt = (iFlag % 32);
-
- uint32 tutflag = GetPlayer()->GetTutorialInt( wInt );
- tutflag |= (1 << rInt);
- GetPlayer()->SetTutorialInt( wInt, tutflag );
-
- //sLog.outDebug("Received Tutorial Flag Set {%u}.", iFlag);
-}
-
-void WorldSession::HandleTutorialClear( WorldPacket & /*recv_data*/ )
-{
- for ( uint32 iI = 0; iI < 8; iI++)
- GetPlayer()->SetTutorialInt( iI, 0xFFFFFFFF );
-}
-
-void WorldSession::HandleTutorialReset( WorldPacket & /*recv_data*/ )
-{
- for ( uint32 iI = 0; iI < 8; iI++)
- GetPlayer()->SetTutorialInt( iI, 0x00000000 );
-}
-
-void WorldSession::HandleSetWatchedFactionIndexOpcode(WorldPacket & recv_data)
-{
- CHECK_PACKET_SIZE(recv_data,4);
-
- DEBUG_LOG("WORLD: Received CMSG_SET_WATCHED_FACTION");
- uint32 fact;
- recv_data >> fact;
- GetPlayer()->SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fact);
-}
-
-void WorldSession::HandleSetWatchedFactionInactiveOpcode(WorldPacket & recv_data)
-{
- CHECK_PACKET_SIZE(recv_data,4+1);
-
- DEBUG_LOG("WORLD: Received CMSG_SET_FACTION_INACTIVE");
- uint32 replistid;
- uint8 inactive;
- recv_data >> replistid >> inactive;
-
- FactionStateList::iterator itr = _player->m_factions.find(replistid);
- if (itr == _player->m_factions.end())
- return;
-
- _player->SetFactionInactive(&itr->second, inactive);
-}
-
-void WorldSession::HandleToggleHelmOpcode( WorldPacket & /*recv_data*/ )
-{
- DEBUG_LOG("CMSG_TOGGLE_HELM for %s", _player->GetName());
- _player->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM);
-}
-
-void WorldSession::HandleToggleCloakOpcode( WorldPacket & /*recv_data*/ )
-{
- DEBUG_LOG("CMSG_TOGGLE_CLOAK for %s", _player->GetName());
- _player->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK);
-}
-
-void WorldSession::HandleChangePlayerNameOpcode(WorldPacket& recv_data)
-{
- CHECK_PACKET_SIZE(recv_data,8+1);
-
- uint64 guid;
- std::string newname;
- std::string oldname;
-
- CHECK_PACKET_SIZE(recv_data, 8+1);
-
- recv_data >> guid;
- recv_data >> newname;
-
- QueryResult *result = CharacterDatabase.PQuery("SELECT at_login FROM characters WHERE guid ='%u'", GUID_LOPART(guid));
- if (result)
- {
- uint32 at_loginFlags;
- Field *fields = result->Fetch();
- at_loginFlags = fields[0].GetUInt32();
- delete result;
-
- if (!(at_loginFlags & AT_LOGIN_RENAME))
- {
- WorldPacket data(SMSG_CHAR_RENAME, 1);
- data << (uint8)CHAR_CREATE_ERROR;
- SendPacket( &data );
- return;
- }
- }
- else
- {
- WorldPacket data(SMSG_CHAR_RENAME, 1);
- data << (uint8)CHAR_CREATE_ERROR;
- SendPacket( &data );
- return;
- }
-
- if(!objmgr.GetPlayerNameByGUID(guid, oldname)) // character not exist, because we have no name for this guid
- {
- WorldPacket data(SMSG_CHAR_RENAME, 1);
- data << (uint8)CHAR_LOGIN_NO_CHARACTER;
- SendPacket( &data );
- return;
- }
-
- // prevent character rename to invalid name
- if(!normalizePlayerName(newname))
- {
- WorldPacket data(SMSG_CHAR_RENAME, 1);
- data << (uint8)CHAR_NAME_NO_NAME;
- SendPacket( &data );
- return;
- }
-
- if(!ObjectMgr::IsValidName(newname,true))
- {
- WorldPacket data(SMSG_CHAR_RENAME, 1);
- data << (uint8)CHAR_NAME_INVALID_CHARACTER;
- SendPacket( &data );
- return;
- }
-
- // check name limitations
- if(GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname))
- {
- WorldPacket data(SMSG_CHAR_RENAME, 1);
- data << (uint8)CHAR_NAME_RESERVED;
- SendPacket( &data );
- return;
- }
-
- if(objmgr.GetPlayerGUIDByName(newname)) // character with this name already exist
- {
- WorldPacket data(SMSG_CHAR_RENAME, 1);
- data << (uint8)CHAR_CREATE_ERROR;
- SendPacket( &data );
- return;
- }
-
- if(newname == oldname) // checked by client
- {
- WorldPacket data(SMSG_CHAR_RENAME, 1);
- data << (uint8)CHAR_NAME_FAILURE;
- SendPacket( &data );
- return;
- }
-
- // we have to check character at_login_flag & AT_LOGIN_RENAME also (fake packets hehe)
-
- CharacterDatabase.escape_string(newname);
- CharacterDatabase.PExecute("UPDATE characters set name = '%s', at_login = at_login & ~ %u WHERE guid ='%u'", newname.c_str(), uint32(AT_LOGIN_RENAME),GUID_LOPART(guid));
- CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid ='%u'", GUID_LOPART(guid));
-
- std::string IP_str = GetRemoteAddress();
- sLog.outChar("Account: %d (IP: %s) Character:[%s] (guid:%u) Changed name to: %s",GetAccountId(),IP_str.c_str(),oldname.c_str(),GUID_LOPART(guid),newname.c_str());
-
- WorldPacket data(SMSG_CHAR_RENAME,1+8+(newname.size()+1));
- data << (uint8)RESPONSE_SUCCESS;
- data << guid;
- data << newname;
- SendPacket(&data);
-}
-
-void WorldSession::HandleDeclinedPlayerNameOpcode(WorldPacket& recv_data)
-{
- uint64 guid;
-
- CHECK_PACKET_SIZE(recv_data, 8+6);
- recv_data >> guid;
-
- // not accept declined names for unsupported languages
- std::string name;
- if(!objmgr.GetPlayerNameByGUID(guid,name))
- {
- WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8);
- data << (uint32)1;
- data << guid;
- SendPacket(&data);
- return;
- }
-
- std::wstring wname;
- if(!Utf8toWStr(name,wname))
- {
- WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8);
- data << (uint32)1;
- data << guid;
- 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);
- data << (uint32)1;
- data << guid;
- 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);
- data << (uint32)1;
- data << guid;
- SendPacket(&data);
- return;
- }
-
- for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
- {
- recv_data >> declinedname.name[i];
- if(!normalizePlayerName(declinedname.name[i]))
- {
- WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8);
- data << (uint32)1;
- data << guid;
- SendPacket(&data);
- return;
- }
- }
-
- if(!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname,0),declinedname))
- {
- WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8);
- data << (uint32)1;
- data << guid;
- 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 << guid;
- SendPacket(&data);
-}
+/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * 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 "Database/DatabaseEnv.h" +#include "WorldPacket.h" +#include "SharedDefines.h" +#include "WorldSession.h" +#include "Opcodes.h" +#include "Log.h" +#include "World.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "Guild.h" +#include "UpdateMask.h" +#include "Auth/md5.h" +#include "MapManager.h" +#include "ObjectAccessor.h" +#include "Group.h" +#include "Database/DatabaseImpl.h" +#include "PlayerDump.h" +#include "SocialMgr.h" +#include "Util.h" +#include "Language.h" + +class LoginQueryHolder : public SqlQueryHolder +{ + private: + uint32 m_accountId; + uint64 m_guid; + public: + LoginQueryHolder(uint32 accountId, uint64 guid) + : m_accountId(accountId), m_guid(guid) { } + uint64 GetGuid() const { return m_guid; } + 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, 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, gmstate, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGROUP, "SELECT leaderGuid FROM group_member WHERE memberGuid ='%u'", GUID_LOPART(m_guid)); + 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)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADAURAS, "SELECT caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLS, "SELECT spell,slot,active,disabled FROM character_spell WHERE guid = '%u'", GUID_LOPART(m_guid)); + 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)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS,"SELECT quest,time FROM character_queststatus_daily WHERE guid = '%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADTUTORIALS, "SELECT tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7 FROM character_tutorial WHERE account = '%u' AND realmid = '%u'", GetAccountId(), realmID); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADREPUTATION, "SELECT faction,standing,flags FROM character_reputation WHERE guid = '%u'", GUID_LOPART(m_guid)); + 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)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACTIONS, "SELECT button,action,type,misc FROM character_action WHERE guid = '%u' ORDER BY button", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMAILCOUNT, "SELECT COUNT(id) FROM mail WHERE receiver = '%u' AND (checked & 1)=0 AND deliver_time <= '" I64FMTD "'", GUID_LOPART(m_guid),(uint64)time(NULL)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMAILDATE, "SELECT MIN(deliver_time) FROM mail WHERE receiver = '%u' AND (checked & 1)=0", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSOCIALLIST, "SELECT friend,flags,note FROM character_social WHERE guid = '%u' LIMIT 255", GUID_LOPART(m_guid)); + 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)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS, "SELECT spell,item,time FROM character_spell_cooldown WHERE guid = '%u'", GUID_LOPART(m_guid)); + if(sWorld.getConfig(CONFIG_DECLINED_NAMES_USED)) + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_declinedname WHERE guid = '%u'",GUID_LOPART(m_guid)); + // in other case still be dummy query + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGUILD, "SELECT guildid,rank FROM guild_member 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 +class CharacterHandler +{ + public: + void HandleCharEnumCallback(QueryResult * result, uint32 account) + { + WorldSession * session = sWorld.FindSession(account); + if(!session) + { + delete result; + return; + } + session->HandleCharEnum(result); + } + void HandlePlayerLoginCallback(QueryResult * /*dummy*/, SqlQueryHolder * holder) + { + if (!holder) return; + WorldSession *session = sWorld.FindSession(((LoginQueryHolder*)holder)->GetAccountId()); + if(!session) + { + delete holder; + return; + } + session->HandlePlayerLogin((LoginQueryHolder*)holder); + } +} chrHandler; + +void WorldSession::HandleCharEnum(QueryResult * result) +{ + // keys can be non cleared if player open realm list and close it by 'cancel' + loginDatabase.PExecute("UPDATE account SET v = '0', s = '0' WHERE id = '%u'", GetAccountId()); + + WorldPacket data(SMSG_CHAR_ENUM, 100); // we guess size + + uint8 num = 0; + + data << num; + + if( result ) + { + Player *plr = new Player(this); + do + { + sLog.outDetail("Loading char guid %u from account %u.",(*result)[0].GetUInt32(),GetAccountId()); + + if(plr->MinimalLoadFromDB( result, (*result)[0].GetUInt32() )) + { + plr->BuildEnumData( result, &data ); + ++num; + } + } + while( result->NextRow() ); + + delete plr; + 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 + CharacterDatabase.AsyncPQuery(&chrHandler, &CharacterHandler::HandleCharEnumCallback, GetAccountId(), + !sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) ? + // ------- Query Without Declined Names -------- + // 0 1 2 3 4 5 6 7 8 + "SELECT characters.data, characters.name, characters.position_x, characters.position_y, characters.position_z, characters.map, characters.totaltime, characters.leveltime, characters.at_login, " + // 9 10 11 + "character_pet.entry, character_pet.modelid, character_pet.level " + "FROM characters LEFT JOIN character_pet ON characters.guid=character_pet.owner AND character_pet.slot='0' " + "WHERE characters.account = '%u' ORDER BY characters.guid" + : + // --------- Query With Declined Names --------- + // 0 1 2 3 4 5 6 7 8 + "SELECT characters.data, characters.name, characters.position_x, characters.position_y, characters.position_z, characters.map, characters.totaltime, characters.leveltime, characters.at_login, " + // 9 10 11 12 + "character_pet.entry, character_pet.modelid, character_pet.level, genitive " + "FROM characters LEFT JOIN character_pet ON characters.guid = character_pet.owner AND character_pet.slot='0' " + "LEFT JOIN character_declinedname ON characters.guid = character_declinedname.guid " + "WHERE characters.account = '%u' ORDER BY characters.guid", + GetAccountId()); +} + +void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,1+1+1+1+1+1+1+1+1+1); + + std::string name; + uint8 race_,class_; + bool pTbc = this->IsTBC() && sWorld.getConfig(CONFIG_EXPANSION) > 0; + recv_data >> name; + + // recheck with known string size + CHECK_PACKET_SIZE(recv_data,(name.size()+1)+1+1+1+1+1+1+1+1+1); + + 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; + SendPacket( &data ); + return; + } + } + } + + if (!sChrClassesStore.LookupEntry(class_)|| + !sChrRacesStore.LookupEntry(race_)) + { + data << (uint8)CHAR_CREATE_FAILED; + SendPacket( &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 (!pTbc&&(race_>RACE_TROLL)) + { + data << (uint8)CHAR_CREATE_EXPANSION; + sLog.outError("No Expansion Account:[%d] but tried to Create TBC character",GetAccountId()); + SendPacket( &data ); + return; + } + + // prevent character creating with invalid name + if(!normalizePlayerName(name)) + { + data << (uint8)CHAR_NAME_INVALID_CHARACTER; + SendPacket( &data ); + sLog.outError("Account:[%d] but tried to Create character with empty [name] ",GetAccountId()); + return; + } + + // check name limitations + if(!ObjectMgr::IsValidName(name,true)) + { + data << (uint8)CHAR_NAME_INVALID_CHARACTER; + 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; + SendPacket( &data ); + return; + } + } + + QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(guid) FROM characters WHERE account = '%d'", GetAccountId()); + uint8 charcount = 0; + if ( result ) + { + Field *fields=result->Fetch(); + charcount = fields[0].GetUInt8(); + delete result; + + if (charcount >= sWorld.getConfig(CONFIG_CHARACTERS_PER_REALM)) + { + data << (uint8)CHAR_CREATE_SERVER_LIMIT; + 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(!AllowTwoSideAccounts || skipCinematics == 1) + { + QueryResult *result2 = CharacterDatabase.PQuery("SELECT DISTINCT race FROM characters WHERE account = '%u' %s", GetAccountId(),skipCinematics == 1 ? "" : "LIMIT 1"); + if(result2) + { + uint32 team_= Player::TeamForRace(race_); + + Field* field = result2->Fetch(); + uint8 race = field[0].GetUInt32(); + + // need to check team only for first character + // TODO: what to if account already has characters of both races? + if (!AllowTwoSideAccounts) + { + uint32 team=0; + if(race > 0) + team = Player::TeamForRace(race); + + if(team != team_) + { + data << (uint8)CHAR_CREATE_PVP_TEAMS_VIOLATION; + SendPacket( &data ); + delete result2; + return; + } + } + + if (skipCinematics == 1) + { + // TODO: check if cinematic already shown? (already logged in?; cinematic field) + while (race_ != race && result2->NextRow()) + { + field = result2->Fetch(); + race = field[0].GetUInt32(); + } + have_same_race = race_ == race; + } + delete result2; + } + } + + // extract other data required for player creating + uint8 gender, skin, face, hairStyle, hairColor, facialHair, outfitId; + recv_data >> gender >> skin >> face; + recv_data >> hairStyle >> hairColor >> facialHair >> outfitId; + + 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().c_str(); + sLog.outBasic("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 ) +{ + CHECK_PACKET_SIZE(recv_data,8); + + 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)) + { + WorldPacket data(SMSG_CHAR_DELETE, 1); + data << (uint8)CHAR_DELETE_FAILED_GUILD_LEADER; + SendPacket( &data ); + return; + } + + // is arena team captain + if(objmgr.GetArenaTeamByCapitan(guid)) + { + WorldPacket data(SMSG_CHAR_DELETE, 1); + data << (uint8)CHAR_DELETE_FAILED_ARENA_CAPTAIN; + SendPacket( &data ); + return; + } + + QueryResult *result = CharacterDatabase.PQuery("SELECT account,name FROM characters WHERE guid='%u'", GUID_LOPART(guid)); + if(result) + { + Field *fields = result->Fetch(); + accountId = fields[0].GetUInt32(); + name = fields[1].GetCppString(); + delete result; + } + + // prevent deleting other players' characters using cheating tools + if(accountId != GetAccountId()) + return; + + std::string IP_str = GetRemoteAddress(); + sLog.outBasic("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 ) +{ + CHECK_PACKET_SIZE(recv_data,8); + + 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()) + { + delete holder; // delete all unprocessed queries + m_playerLoading = false; + return; + } + + CharacterDatabase.DelayQueryHolder(&chrHandler, &CharacterHandler::HandlePlayerLoginCallback, holder); +} + +void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) +{ + uint64 playerGuid = holder->GetGuid(); + + Player* pCurrChar = new Player(this); + pCurrChar->GetMotionMaster()->Initialize(); + + // "GetAccountId()==db stored account id" checked in LoadFromDB (prevent login not own character using cheating tools) + if(!pCurrChar->LoadFromDB(GUID_LOPART(playerGuid), holder)) + { + KickPlayer(); // disconnect client, player no set to session and it will not deleted or saved at kick + delete pCurrChar; // delete it manually + delete holder; // delete all unprocessed queries + m_playerLoading = false; + return; + } + + SetPlayer(pCurrChar); + + pCurrChar->SendDungeonDifficulty(false); + + WorldPacket data( SMSG_LOGIN_VERIFY_WORLD, 20 ); + data << pCurrChar->GetMapId(); + data << pCurrChar->GetPositionX(); + data << pCurrChar->GetPositionY(); + data << pCurrChar->GetPositionZ(); + data << pCurrChar->GetOrientation(); + SendPacket(&data); + + data.Initialize( SMSG_ACCOUNT_DATA_TIMES, 128 ); + for(int i = 0; i < 32; i++) + data << uint32(0); + SendPacket(&data); + + 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 ) + { + if (nextpos != pos) + { + data << str_motd.substr(pos,nextpos-pos); + ++linecount; + } + 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)" ); + } + + if(pCurrChar->GetGuildId() != 0) + { + Guild* guild = objmgr.GetGuildById(pCurrChar->GetGuildId()); + if(guild) + { + data.Initialize(SMSG_GUILD_EVENT, (2+guild->GetMOTD().size()+1)); + data << (uint8)GE_MOTD; + data << (uint8)1; + 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; + data<<pCurrChar->GetName(); + data<<pCurrChar->GetGUID(); + guild->BroadcastPacket(&data); + DEBUG_LOG( "WORLD: Sent guild-signed-on (SMSG_GUILD_EVENT)" ); + + // Increment online members of the guild + guild->IncOnlineMemberCount(); + } + else + { + // remove wrong guild data + 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()); + pCurrChar->SetUInt32Value(PLAYER_GUILDID,0); + pCurrChar->SetUInt32ValueInDB(PLAYER_GUILDID,0,pCurrChar->GetGUID()); + } + } + + if(!pCurrChar->isAlive()) + pCurrChar->SendCorpseReclaimDelay(true); + + pCurrChar->SendInitialPacketsBeforeAddToMap(); + + //Show cinematic at the first time that player login + if( !pCurrChar->getCinematic() ) + { + pCurrChar->setCinematic(1); + + ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(pCurrChar->getRace()); + if(rEntry) + { + data.Initialize( SMSG_TRIGGER_CINEMATIC,4 ); + data << uint32(rEntry->startmovie); + 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(); + pCurrChar->SetInGuild(fields[0].GetUInt32()); + pCurrChar->SetRank(fields[1].GetUInt32()); + delete resultGuild; + } + else if(pCurrChar->GetGuildId()) // clear guild related fields in case wrong data about non existed membership + { + pCurrChar->SetInGuild(0); + pCurrChar->SetRank(0); + } + + if (!MapManager::Instance().GetMap(pCurrChar->GetMapId(), pCurrChar)->Add(pCurrChar)) + { + AreaTrigger const* at = objmgr.GetGoBackTrigger(pCurrChar->GetMapId()); + if(at) + pCurrChar->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, pCurrChar->GetOrientation()); + 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->GetSocial()->SendSocialList(); + + 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 == DEAD ) + if (pCurrChar->m_deathState != ALIVE) + { + // not blizz like, we must correctly save and load player instead... + if(pCurrChar->getRace() == RACE_NIGHTELF) + pCurrChar->CastSpell(pCurrChar, 20584, true, 0);// auras SPELL_AURA_INCREASE_SPEED(+speed in wisp form), SPELL_AURA_INCREASE_SWIM_SPEED(+swim speed in wisp form), SPELL_AURA_TRANSFORM (to wisp form) + pCurrChar->CastSpell(pCurrChar, 8326, true, 0); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?) + + //pCurrChar->SetUInt32Value(UNIT_FIELD_AURA+41, 8326); + //pCurrChar->SetUInt32Value(UNIT_FIELD_AURA+42, 20584); + //pCurrChar->SetUInt32Value(UNIT_FIELD_AURAFLAGS+6, 238); + //pCurrChar->SetUInt32Value(UNIT_FIELD_AURALEVELS+11, 514); + //pCurrChar->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS+11, 65535); + //pCurrChar->SetUInt32Value(UNIT_FIELD_DISPLAYID, 1825); + //if (pCurrChar->getRace() == RACE_NIGHTELF) + //{ + // pCurrChar->SetSpeed(MOVE_RUN, 1.5f*1.2f, true); + // pCurrChar->SetSpeed(MOVE_SWIM, 1.5f*1.2f, true); + //} + //else + //{ + // pCurrChar->SetSpeed(MOVE_RUN, 1.5f, true); + // pCurrChar->SetSpeed(MOVE_SWIM, 1.5f, true); + //} + pCurrChar->SetMovement(MOVE_WATER_WALK); + } + + if(uint32 sourceNode = pCurrChar->m_taxi.GetTaxiSource()) + { + + sLog.outDebug( "WORLD: Restart character %u taxi flight", pCurrChar->GetGUIDLow() ); + + uint32 MountId = objmgr.GetTaxiMount(sourceNode, pCurrChar->GetTeam()); + uint32 path = pCurrChar->m_taxi.GetCurrentTaxiPath(); + + // search appropriate start path node + uint32 startNode = 0; + + TaxiPathNodeList const& nodeList = sTaxiPathNodesByPath[path]; + + float distPrev = MAP_SIZE*MAP_SIZE; + float distNext = + (nodeList[0].x-pCurrChar->GetPositionX())*(nodeList[0].x-pCurrChar->GetPositionX())+ + (nodeList[0].y-pCurrChar->GetPositionY())*(nodeList[0].y-pCurrChar->GetPositionY())+ + (nodeList[0].z-pCurrChar->GetPositionZ())*(nodeList[0].z-pCurrChar->GetPositionZ()); + + for(uint32 i = 1; i < nodeList.size(); ++i) + { + TaxiPathNode const& node = nodeList[i]; + TaxiPathNode const& prevNode = nodeList[i-1]; + + // skip nodes at another map + if(node.mapid != pCurrChar->GetMapId()) + continue; + + distPrev = distNext; + + distNext = + (node.x-pCurrChar->GetPositionX())*(node.x-pCurrChar->GetPositionX())+ + (node.y-pCurrChar->GetPositionY())*(node.y-pCurrChar->GetPositionY())+ + (node.z-pCurrChar->GetPositionZ())*(node.z-pCurrChar->GetPositionZ()); + + float distNodes = + (node.x-prevNode.x)*(node.x-prevNode.x)+ + (node.y-prevNode.y)*(node.y-prevNode.y)+ + (node.z-prevNode.z)*(node.z-prevNode.z); + + if(distNext + distPrev < distNodes) + { + startNode = i; + break; + } + } + + SendDoFlight( MountId, path, startNode ); + } + + // Load pet if any and player is alive and not in taxi flight + if(pCurrChar->isAlive() && pCurrChar->m_taxi.GetTaxiSource()==0) + 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->SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_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); + SendNotification(LANG_RESET_TALENTS); + } + + // show time before shutdown if shutdown planned. + if(sWorld.IsShutdowning()) + sWorld.ShutdownMsg(true,pCurrChar); + + if(sWorld.getConfig(CONFIG_ALWAYS_MAXSKILL)) // Max weapon skill when logging in + pCurrChar->UpdateSkillsToMaxSkillsForLevel(); + + //ImpConfig - Check if player has logged in before + QueryResult *result = CharacterDatabase.PQuery("SELECT guid FROM has_logged_in_before WHERE guid = %u",pCurrChar->GetGUIDLow()); + if(!result) + { + sLog.outBasic("Character '%s' logging in for first time, applying skills and stuff",pCurrChar->GetName()); + CharacterDatabase.PExecute("INSERT INTO has_logged_in_before VALUES (%u)",pCurrChar->GetGUIDLow()); + + //Reputations if "StartAllReputation" is enabled, -- TODO: Fix this in a better way + if(sWorld.getConfig(CONFIG_START_ALL_REP)) + { + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(942),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(935),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(936),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(1011),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(970),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(967),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(989),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(932),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(934),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(1038),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(1077),42999); + + // Factions depending on team, like cities and some more stuff + switch(pCurrChar->GetTeam()) + { + case ALLIANCE: + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(72),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(47),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(69),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(930),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(730),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(978),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(54),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(946),42999); + break; + case HORDE: + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(76),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(68),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(81),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(911),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(729),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(941),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(530),42999); + pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(947),42999); + break; + } + } + } + else + sLog.outBasic("Character '%s' has logged in before",pCurrChar->GetName()); + + if(sWorld.getConfig(CONFIG_START_ALL_TAXI)) + 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->GetGUID()); + + m_playerLoading = false; + delete holder; +} + +void WorldSession::HandleSetFactionAtWar( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,4+1); + + DEBUG_LOG( "WORLD: Received CMSG_SET_FACTION_ATWAR" ); + + uint32 repListID; + uint8 flag; + + recv_data >> repListID; + recv_data >> flag; + + FactionStateList::iterator itr = GetPlayer()->m_factions.find(repListID); + if (itr == GetPlayer()->m_factions.end()) + return; + + // always invisible or hidden faction can't change war state + if(itr->second.Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN) ) + return; + + GetPlayer()->SetFactionAtWar(&itr->second,flag); +} + +//I think this function is never used :/ I dunno, but i guess this opcode not exists +void WorldSession::HandleSetFactionCheat( WorldPacket & /*recv_data*/ ) +{ + //CHECK_PACKET_SIZE(recv_data,4+4); + + //sLog.outDebug("WORLD SESSION: HandleSetFactionCheat"); + /* + 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) + { + itr->Standing += Standing; + itr->Flags = (itr->Flags | 1); + break; + } + } + */ + GetPlayer()->UpdateReputation(); +} + +void WorldSession::HandleMeetingStoneInfo( WorldPacket & /*recv_data*/ ) +{ + DEBUG_LOG( "WORLD: Received CMSG_MEETING_STONE_INFO" ); + + WorldPacket data(SMSG_MEETINGSTONE_SETQUEUE, 5); + data << uint32(0) << uint8(6); + SendPacket(&data); +} + +void WorldSession::HandleTutorialFlag( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,4); + + uint32 iFlag; + recv_data >> iFlag; + + uint32 wInt = (iFlag / 32); + if (wInt >= 8) + { + //sLog.outError("CHEATER? Account:[%d] Guid[%u] tried to send wrong CMSG_TUTORIAL_FLAG", GetAccountId(),GetGUID()); + return; + } + uint32 rInt = (iFlag % 32); + + uint32 tutflag = GetPlayer()->GetTutorialInt( wInt ); + tutflag |= (1 << rInt); + GetPlayer()->SetTutorialInt( wInt, tutflag ); + + //sLog.outDebug("Received Tutorial Flag Set {%u}.", iFlag); +} + +void WorldSession::HandleTutorialClear( WorldPacket & /*recv_data*/ ) +{ + for ( uint32 iI = 0; iI < 8; iI++) + GetPlayer()->SetTutorialInt( iI, 0xFFFFFFFF ); +} + +void WorldSession::HandleTutorialReset( WorldPacket & /*recv_data*/ ) +{ + for ( uint32 iI = 0; iI < 8; iI++) + GetPlayer()->SetTutorialInt( iI, 0x00000000 ); +} + +void WorldSession::HandleSetWatchedFactionIndexOpcode(WorldPacket & recv_data) +{ + CHECK_PACKET_SIZE(recv_data,4); + + DEBUG_LOG("WORLD: Received CMSG_SET_WATCHED_FACTION"); + uint32 fact; + recv_data >> fact; + GetPlayer()->SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fact); +} + +void WorldSession::HandleSetWatchedFactionInactiveOpcode(WorldPacket & recv_data) +{ + CHECK_PACKET_SIZE(recv_data,4+1); + + DEBUG_LOG("WORLD: Received CMSG_SET_FACTION_INACTIVE"); + uint32 replistid; + uint8 inactive; + recv_data >> replistid >> inactive; + + FactionStateList::iterator itr = _player->m_factions.find(replistid); + if (itr == _player->m_factions.end()) + return; + + _player->SetFactionInactive(&itr->second, inactive); +} + +void WorldSession::HandleToggleHelmOpcode( WorldPacket & /*recv_data*/ ) +{ + DEBUG_LOG("CMSG_TOGGLE_HELM for %s", _player->GetName()); + _player->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM); +} + +void WorldSession::HandleToggleCloakOpcode( WorldPacket & /*recv_data*/ ) +{ + DEBUG_LOG("CMSG_TOGGLE_CLOAK for %s", _player->GetName()); + _player->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK); +} + +void WorldSession::HandleChangePlayerNameOpcode(WorldPacket& recv_data) +{ + CHECK_PACKET_SIZE(recv_data,8+1); + + uint64 guid; + std::string newname; + std::string oldname; + + CHECK_PACKET_SIZE(recv_data, 8+1); + + recv_data >> guid; + recv_data >> newname; + + QueryResult *result = CharacterDatabase.PQuery("SELECT at_login FROM characters WHERE guid ='%u'", GUID_LOPART(guid)); + if (result) + { + uint32 at_loginFlags; + Field *fields = result->Fetch(); + at_loginFlags = fields[0].GetUInt32(); + delete result; + + if (!(at_loginFlags & AT_LOGIN_RENAME)) + { + WorldPacket data(SMSG_CHAR_RENAME, 1); + data << (uint8)CHAR_CREATE_ERROR; + SendPacket( &data ); + return; + } + } + else + { + WorldPacket data(SMSG_CHAR_RENAME, 1); + data << (uint8)CHAR_CREATE_ERROR; + SendPacket( &data ); + return; + } + + if(!objmgr.GetPlayerNameByGUID(guid, oldname)) // character not exist, because we have no name for this guid + { + WorldPacket data(SMSG_CHAR_RENAME, 1); + data << (uint8)CHAR_LOGIN_NO_CHARACTER; + SendPacket( &data ); + return; + } + + // prevent character rename to invalid name + if(!normalizePlayerName(newname)) + { + WorldPacket data(SMSG_CHAR_RENAME, 1); + data << (uint8)CHAR_NAME_NO_NAME; + SendPacket( &data ); + return; + } + + if(!ObjectMgr::IsValidName(newname,true)) + { + WorldPacket data(SMSG_CHAR_RENAME, 1); + data << (uint8)CHAR_NAME_INVALID_CHARACTER; + SendPacket( &data ); + return; + } + + // check name limitations + if(GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname)) + { + WorldPacket data(SMSG_CHAR_RENAME, 1); + data << (uint8)CHAR_NAME_RESERVED; + SendPacket( &data ); + return; + } + + if(objmgr.GetPlayerGUIDByName(newname)) // character with this name already exist + { + WorldPacket data(SMSG_CHAR_RENAME, 1); + data << (uint8)CHAR_CREATE_ERROR; + SendPacket( &data ); + return; + } + + if(newname == oldname) // checked by client + { + WorldPacket data(SMSG_CHAR_RENAME, 1); + data << (uint8)CHAR_NAME_FAILURE; + SendPacket( &data ); + return; + } + + // we have to check character at_login_flag & AT_LOGIN_RENAME also (fake packets hehe) + + CharacterDatabase.escape_string(newname); + CharacterDatabase.PExecute("UPDATE characters set name = '%s', at_login = at_login & ~ %u WHERE guid ='%u'", newname.c_str(), uint32(AT_LOGIN_RENAME),GUID_LOPART(guid)); + CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid ='%u'", GUID_LOPART(guid)); + + std::string IP_str = GetRemoteAddress(); + sLog.outChar("Account: %d (IP: %s) Character:[%s] (guid:%u) Changed name to: %s",GetAccountId(),IP_str.c_str(),oldname.c_str(),GUID_LOPART(guid),newname.c_str()); + + WorldPacket data(SMSG_CHAR_RENAME,1+8+(newname.size()+1)); + data << (uint8)RESPONSE_SUCCESS; + data << guid; + data << newname; + SendPacket(&data); +} + +void WorldSession::HandleDeclinedPlayerNameOpcode(WorldPacket& recv_data) +{ + uint64 guid; + + CHECK_PACKET_SIZE(recv_data, 8+6); + recv_data >> guid; + + // not accept declined names for unsupported languages + std::string name; + if(!objmgr.GetPlayerNameByGUID(guid,name)) + { + WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8); + data << (uint32)1; + data << guid; + SendPacket(&data); + return; + } + + std::wstring wname; + if(!Utf8toWStr(name,wname)) + { + WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8); + data << (uint32)1; + data << guid; + 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); + data << (uint32)1; + data << guid; + 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); + data << (uint32)1; + data << guid; + SendPacket(&data); + return; + } + + for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) + { + recv_data >> declinedname.name[i]; + if(!normalizePlayerName(declinedname.name[i])) + { + WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8); + data << (uint32)1; + data << guid; + SendPacket(&data); + return; + } + } + + if(!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname,0),declinedname)) + { + WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT,4+8); + data << (uint32)1; + data << guid; + 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 << guid; + SendPacket(&data); +} |