aboutsummaryrefslogtreecommitdiff
path: root/src/game/CharacterHandler.cpp
diff options
context:
space:
mode:
authormaximius <none@none>2009-10-17 15:51:44 -0700
committermaximius <none@none>2009-10-17 15:51:44 -0700
commite585187b248f48b3c6e9247b49fa07c6565d65e5 (patch)
tree637c5b7ddacf41040bef4ea4f75a97da64c6a9bc /src/game/CharacterHandler.cpp
parent26b5e033ffde3d161382fc9addbfa99738379641 (diff)
*Backed out changeset 3be01fb200a5
--HG-- branch : trunk
Diffstat (limited to 'src/game/CharacterHandler.cpp')
-rw-r--r--src/game/CharacterHandler.cpp246
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);