diff options
Diffstat (limited to 'src/game/WorldSession.cpp')
-rw-r--r-- | src/game/WorldSession.cpp | 1004 |
1 files changed, 511 insertions, 493 deletions
diff --git a/src/game/WorldSession.cpp b/src/game/WorldSession.cpp index 9ec9624182c..b6585185eea 100644 --- a/src/game/WorldSession.cpp +++ b/src/game/WorldSession.cpp @@ -1,493 +1,511 @@ -/* - * 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 - */ - -/** \file - \ingroup u2w -*/ - -#include "WorldSocket.h" -#include "Common.h" -#include "Database/DatabaseEnv.h" -#include "Log.h" -#include "Opcodes.h" -#include "WorldSocket.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "Player.h" -#include "ObjectMgr.h" -#include "Group.h" -#include "Guild.h" -#include "World.h" -#include "MapManager.h" -#include "ObjectAccessor.h" -#include "BattleGroundMgr.h" -#include "Language.h" // for CMSG_CANCEL_MOUNT_AURA handler -#include "Chat.h" -#include "SocialMgr.h" - -/// WorldSession constructor -WorldSession::WorldSession(uint32 id, WorldSocket *sock, uint32 sec, bool tbc, time_t mute_time, LocaleConstant locale) : -LookingForGroup_auto_join(false), LookingForGroup_auto_add(false), m_muteTime(mute_time), -_player(NULL), m_Socket(sock),_security(sec), _accountId(id), m_isTBC(tbc), -m_sessionDbcLocale(sWorld.GetAvailableDbcLocale(locale)), m_sessionDbLocaleIndex(objmgr.GetIndexForLocale(locale)), -_logoutTime(0), m_playerLoading(false), m_playerLogout(false), m_playerRecentlyLogout(false), m_latency(0) -{ - if (sock) - { - m_Address = sock->GetRemoteAddress (); - sock->AddReference (); - } -} - -/// WorldSession destructor -WorldSession::~WorldSession() -{ - ///- unload player if not unloaded - if(_player) - LogoutPlayer(true); - - /// - If have unclosed socket, close it - if (m_Socket) - { - m_Socket->CloseSocket (); - m_Socket->RemoveReference (); - m_Socket = NULL; - } - - ///- empty incoming packet queue - while(!_recvQueue.empty()) - { - WorldPacket *packet = _recvQueue.next(); - delete packet; - } - - sWorld.RemoveQueuedPlayer(this); -} - -void WorldSession::SizeError(WorldPacket const& packet, uint32 size) const -{ - sLog.outError("Client (account %u) send packet %s (%u) with size %u but expected %u (attempt crash server?), skipped", - GetAccountId(),LookupOpcodeName(packet.GetOpcode()),packet.GetOpcode(),packet.size(),size); -} - -/// Get the player name -char const* WorldSession::GetPlayerName() const -{ - return GetPlayer() ? GetPlayer()->GetName() : "<none>"; -} - -/// Send a packet to the client -void WorldSession::SendPacket(WorldPacket const* packet) -{ - if (!m_Socket) - return; - #ifdef MANGOS_DEBUG - // Code for network use statistic - static uint64 sendPacketCount = 0; - static uint64 sendPacketBytes = 0; - - static time_t firstTime = time(NULL); - static time_t lastTime = firstTime; // next 60 secs start time - - static uint64 sendLastPacketCount = 0; - static uint64 sendLastPacketBytes = 0; - - time_t cur_time = time(NULL); - - if((cur_time - lastTime) < 60) - { - sendPacketCount+=1; - sendPacketBytes+=packet->size(); - - sendLastPacketCount+=1; - sendLastPacketBytes+=packet->size(); - } - else - { - uint64 minTime = uint64(cur_time - lastTime); - uint64 fullTime = uint64(lastTime - firstTime); - sLog.outDetail("Send all time packets count: " I64FMTD " bytes: " I64FMTD " avr.count/sec: %f avr.bytes/sec: %f time: %u",sendPacketCount,sendPacketBytes,float(sendPacketCount)/fullTime,float(sendPacketBytes)/fullTime,uint32(fullTime)); - sLog.outDetail("Send last min packets count: " I64FMTD " bytes: " I64FMTD " avr.count/sec: %f avr.bytes/sec: %f",sendLastPacketCount,sendLastPacketBytes,float(sendLastPacketCount)/minTime,float(sendLastPacketBytes)/minTime); - - lastTime = cur_time; - sendLastPacketCount = 1; - sendLastPacketBytes = packet->wpos(); // wpos is real written size - } -#endif // !MANGOS_DEBUG - - if (m_Socket->SendPacket (*packet) == -1) - { - m_Socket->CloseSocket (); - } -} - -/// Add an incoming packet to the queue -void WorldSession::QueuePacket(WorldPacket* new_packet) -{ - _recvQueue.add(new_packet); -} - -/// Logging helper for unexpected opcodes -void WorldSession::logUnexpectedOpcode(WorldPacket* packet, const char *reason) -{ - sLog.outError( "SESSION: received unexpected opcode %s (0x%.4X) %s", - LookupOpcodeName(packet->GetOpcode()), - packet->GetOpcode(), - reason); -} - -/// Update the WorldSession (triggered by World update) -bool WorldSession::Update(uint32 /*diff*/) -{ - if (m_Socket) - if (m_Socket->IsClosed ()) - { - m_Socket->RemoveReference (); - m_Socket = NULL; - } - - WorldPacket *packet; - - ///- Retrieve packets from the receive queue and call the appropriate handlers - /// \todo Is there a way to consolidate the OpcondeHandlerTable and the g_worldOpcodeNames to only maintain 1 list? - /// answer : there is a way, but this is better, because it would use redundant RAM - while (!_recvQueue.empty()) - { - packet = _recvQueue.next(); - - /*#if 1 - sLog.outError( "MOEP: %s (0x%.4X)", - LookupOpcodeName(packet->GetOpcode()), - packet->GetOpcode()); - #endif*/ - - if(packet->GetOpcode() >= NUM_MSG_TYPES) - { - sLog.outError( "SESSION: received non-existed opcode %s (0x%.4X)", - LookupOpcodeName(packet->GetOpcode()), - packet->GetOpcode()); - } - else - { - OpcodeHandler& opHandle = opcodeTable[packet->GetOpcode()]; - switch (opHandle.status) - { - case STATUS_LOGGEDIN: - if(!_player) - { - // skip STATUS_LOGGEDIN opcode unexpected errors if player logout sometime ago - this can be network lag delayed packets - if(!m_playerRecentlyLogout) - logUnexpectedOpcode(packet, "the player has not logged in yet"); - } - else if(_player->IsInWorld()) - (this->*opHandle.handler)(*packet); - // lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer - break; - case STATUS_TRANSFER_PENDING: - if(!_player) - logUnexpectedOpcode(packet, "the player has not logged in yet"); - else if(_player->IsInWorld()) - logUnexpectedOpcode(packet, "the player is still in world"); - else - (this->*opHandle.handler)(*packet); - break; - case STATUS_AUTHED: - m_playerRecentlyLogout = false; - (this->*opHandle.handler)(*packet); - break; - case STATUS_NEVER: - sLog.outError( "SESSION: received not allowed opcode %s (0x%.4X)", - LookupOpcodeName(packet->GetOpcode()), - packet->GetOpcode()); - break; - } - } - - delete packet; - } - - ///- If necessary, log the player out - time_t currTime = time(NULL); - if (!m_Socket || (ShouldLogOut(currTime) && !m_playerLoading)) - LogoutPlayer(true); - - if (!m_Socket) - return false; //Will remove this session from the world session map - - return true; -} - -/// %Log the player out -void WorldSession::LogoutPlayer(bool Save) -{ - // finish pending transfers before starting the logout - while(_player && _player->IsBeingTeleported()) - HandleMoveWorldportAckOpcode(); - - m_playerLogout = true; - - if (_player) - { - ///- If the player just died before logging out, make him appear as a ghost - //FIXME: logout must be delayed in case lost connection with client in time of combat - if (_player->GetDeathTimer()) - { - _player->getHostilRefManager().deleteReferences(); - _player->BuildPlayerRepop(); - _player->RepopAtGraveyard(); - } - else if (!_player->getAttackers().empty()) - { - _player->CombatStop(); - _player->getHostilRefManager().setOnlineOfflineState(false); - _player->RemoveAllAurasOnDeath(); - - // build set of player who attack _player or who have pet attacking of _player - std::set<Player*> aset; - for(Unit::AttackerSet::const_iterator itr = _player->getAttackers().begin(); itr != _player->getAttackers().end(); ++itr) - { - Unit* owner = (*itr)->GetOwner(); // including player controlled case - if(owner) - { - if(owner->GetTypeId()==TYPEID_PLAYER) - aset.insert((Player*)owner); - } - else - if((*itr)->GetTypeId()==TYPEID_PLAYER) - aset.insert((Player*)(*itr)); - } - - _player->SetPvPDeath(!aset.empty()); - _player->KillPlayer(); - _player->BuildPlayerRepop(); - _player->RepopAtGraveyard(); - - // give honor to all attackers from set like group case - for(std::set<Player*>::const_iterator itr = aset.begin(); itr != aset.end(); ++itr) - (*itr)->RewardHonor(_player,aset.size()); - - // give bg rewards and update counters like kill by first from attackers - // this can't be called for all attackers. - if(!aset.empty()) - if(BattleGround *bg = _player->GetBattleGround()) - bg->HandleKillPlayer(_player,*aset.begin()); - } - else if(_player->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION)) - { - // this will kill character by SPELL_AURA_SPIRIT_OF_REDEMPTION - _player->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT); - //_player->SetDeathPvP(*); set at SPELL_AURA_SPIRIT_OF_REDEMPTION apply time - _player->KillPlayer(); - _player->BuildPlayerRepop(); - _player->RepopAtGraveyard(); - } - - ///- Remove player from battleground (teleport to entrance) - if(_player->InBattleGround()) - _player->LeaveBattleground(); - - for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) - { - if(int32 bgTypeId = _player->GetBattleGroundQueueId(i)) - { - _player->RemoveBattleGroundQueueId(bgTypeId); - sBattleGroundMgr.m_BattleGroundQueues[ bgTypeId ].RemovePlayer(_player->GetGUID(), true); - } - } - - ///- Reset the online field in the account table - // no point resetting online in character table here as Player::SaveToDB() will set it to 1 since player has not been removed from world at this stage - //No SQL injection as AccountID is uint32 - loginDatabase.PExecute("UPDATE account SET online = 0 WHERE id = '%u'", GetAccountId()); - - ///- If the player is in a guild, update the guild roster and broadcast a logout message to other guild members - Guild *guild = objmgr.GetGuildById(_player->GetGuildId()); - if(guild) - { - guild->LoadPlayerStatsByGuid(_player->GetGUID()); - guild->UpdateLogoutTime(_player->GetGUID()); - - WorldPacket data(SMSG_GUILD_EVENT, (1+1+12+8)); // name limited to 12 in character table. - data<<(uint8)GE_SIGNED_OFF; - data<<(uint8)1; - data<<_player->GetName(); - data<<_player->GetGUID(); - guild->BroadcastPacket(&data); - } - - ///- Remove pet - _player->RemovePet(NULL,PET_SAVE_AS_CURRENT, true); - - ///- empty buyback items and save the player in the database - // some save parts only correctly work in case player present in map/player_lists (pets, etc) - if(Save) - { - uint32 eslot; - for(int j = BUYBACK_SLOT_START; j < BUYBACK_SLOT_END; j++) - { - eslot = j - BUYBACK_SLOT_START; - _player->SetUInt64Value(PLAYER_FIELD_VENDORBUYBACK_SLOT_1+eslot*2,0); - _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1+eslot,0); - _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1+eslot,0); - } - _player->SaveToDB(); - } - - ///- Leave all channels before player delete... - _player->CleanupChannels(); - - ///- If the player is in a group (or invited), remove him. If the group if then only 1 person, disband the group. - _player->UninviteFromGroup(); - - // remove player from the group if he is: - // a) in group; b) not in raid group; c) logging out normally (not being kicked or disconnected) - if(_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && m_Socket) - _player->RemoveFromGroup(); - - ///- Remove the player from the world - // the player may not be in the world when logging out - // e.g if he got disconnected during a transfer to another map - // calls to GetMap in this case may cause crashes - if(_player->IsInWorld()) MapManager::Instance().GetMap(_player->GetMapId(), _player)->Remove(_player, false); - // RemoveFromWorld does cleanup that requires the player to be in the accessor - ObjectAccessor::Instance().RemoveObject(_player); - - ///- Send update to group - if(_player->GetGroup()) - _player->GetGroup()->SendUpdate(); - - ///- Broadcast a logout message to the player's friends - sSocialMgr.SendFriendStatus(_player, FRIEND_OFFLINE, _player->GetGUIDLow(), "", true); - - ///- Delete the player object - _player->CleanupsBeforeDelete(); // do some cleanup before deleting to prevent crash at crossreferences to already deleted data - - delete _player; - _player = NULL; - - ///- Send the 'logout complete' packet to the client - WorldPacket data( SMSG_LOGOUT_COMPLETE, 0 ); - SendPacket( &data ); - - ///- Since each account can only have one online character at any given time, ensure all characters for active account are marked as offline - //No SQL injection as AccountId is uint32 - CharacterDatabase.PExecute("UPDATE characters SET online = 0 WHERE account = '%u'", GetAccountId()); - sLog.outDebug( "SESSION: Sent SMSG_LOGOUT_COMPLETE Message" ); - } - - m_playerLogout = false; - m_playerRecentlyLogout = true; - LogoutRequest(0); -} - -/// Kick a player out of the World -void WorldSession::KickPlayer() -{ - if (m_Socket) - { - m_Socket->CloseSocket (); - } -} - -/// Cancel channeling handler - -void WorldSession::SendAreaTriggerMessage(const char* Text, ...) -{ - va_list ap; - char szStr [1024]; - szStr[0] = '\0'; - - va_start(ap, Text); - vsnprintf( szStr, 1024, Text, ap ); - va_end(ap); - - uint32 length = strlen(szStr)+1; - WorldPacket data(SMSG_AREA_TRIGGER_MESSAGE, 4+length); - data << length; - data << szStr; - SendPacket(&data); -} - -void WorldSession::SendNotification(const char *format,...) -{ - if(format) - { - va_list ap; - char szStr [1024]; - szStr[0] = '\0'; - va_start(ap, format); - vsnprintf( szStr, 1024, format, ap ); - va_end(ap); - - WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1)); - data << szStr; - SendPacket(&data); - } -} - -const char * WorldSession::GetMangosString( int32 entry ) -{ - return objmgr.GetMangosString(entry,GetSessionDbLocaleIndex()); -} - -void WorldSession::Handle_NULL( WorldPacket& recvPacket ) -{ - sLog.outError( "SESSION: received unhandled opcode %s (0x%.4X)", - LookupOpcodeName(recvPacket.GetOpcode()), - recvPacket.GetOpcode()); -} - -void WorldSession::Handle_EarlyProccess( WorldPacket& recvPacket ) -{ - sLog.outError( "SESSION: received opcode %s (0x%.4X) that must be proccessed in WorldSocket::OnRead", - LookupOpcodeName(recvPacket.GetOpcode()), - recvPacket.GetOpcode()); -} - -void WorldSession::Handle_ServerSide( WorldPacket& recvPacket ) -{ - sLog.outError( "SESSION: received sever-side opcode %s (0x%.4X)", - LookupOpcodeName(recvPacket.GetOpcode()), - recvPacket.GetOpcode()); -} - -void WorldSession::Handle_Depricated( WorldPacket& recvPacket ) -{ - sLog.outError( "SESSION: received depricated opcode %s (0x%.4X)", - LookupOpcodeName(recvPacket.GetOpcode()), - recvPacket.GetOpcode()); -} - -void WorldSession::SendAuthWaitQue(uint32 position) - { - if(position == 0) - { - WorldPacket packet( SMSG_AUTH_RESPONSE, 1 ); - packet << uint8( AUTH_OK ); - SendPacket(&packet); - } - else - { - WorldPacket packet( SMSG_AUTH_RESPONSE, 5 ); - packet << uint8( AUTH_WAIT_QUEUE ); - packet << uint32 (position); - SendPacket(&packet); - } - } - - +/*
+ * 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
+ */
+
+/** \file
+ \ingroup u2w
+*/
+
+#include "WorldSocket.h"
+#include "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "Log.h"
+#include "Opcodes.h"
+#include "WorldSocket.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Player.h"
+#include "ObjectMgr.h"
+#include "Group.h"
+#include "Guild.h"
+#include "World.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "BattleGroundMgr.h"
+#include "Language.h" // for CMSG_CANCEL_MOUNT_AURA handler
+#include "Chat.h"
+#include "SocialMgr.h"
+
+/// WorldSession constructor
+WorldSession::WorldSession(uint32 id, WorldSocket *sock, uint32 sec, bool tbc, time_t mute_time, LocaleConstant locale) :
+LookingForGroup_auto_join(false), LookingForGroup_auto_add(false), m_muteTime(mute_time),
+_player(NULL), m_Socket(sock),_security(sec), _accountId(id), m_isTBC(tbc),
+m_sessionDbcLocale(sWorld.GetAvailableDbcLocale(locale)), m_sessionDbLocaleIndex(objmgr.GetIndexForLocale(locale)),
+_logoutTime(0), m_playerLoading(false), m_playerLogout(false), m_playerRecentlyLogout(false), m_latency(0)
+{
+ if (sock)
+ {
+ m_Address = sock->GetRemoteAddress ();
+ sock->AddReference ();
+ }
+}
+
+/// WorldSession destructor
+WorldSession::~WorldSession()
+{
+ ///- unload player if not unloaded
+ if(_player)
+ LogoutPlayer(true);
+
+ /// - If have unclosed socket, close it
+ if (m_Socket)
+ {
+ m_Socket->CloseSocket ();
+ m_Socket->RemoveReference ();
+ m_Socket = NULL;
+ }
+
+ ///- empty incoming packet queue
+ while(!_recvQueue.empty())
+ {
+ WorldPacket *packet = _recvQueue.next();
+ delete packet;
+ }
+
+ sWorld.RemoveQueuedPlayer(this);
+}
+
+void WorldSession::SizeError(WorldPacket const& packet, uint32 size) const
+{
+ sLog.outError("Client (account %u) send packet %s (%u) with size %u but expected %u (attempt crash server?), skipped",
+ GetAccountId(),LookupOpcodeName(packet.GetOpcode()),packet.GetOpcode(),packet.size(),size);
+}
+
+/// Get the player name
+char const* WorldSession::GetPlayerName() const
+{
+ return GetPlayer() ? GetPlayer()->GetName() : "<none>";
+}
+
+/// Send a packet to the client
+void WorldSession::SendPacket(WorldPacket const* packet)
+{
+ if (!m_Socket)
+ return;
+ #ifdef MANGOS_DEBUG
+ // Code for network use statistic
+ static uint64 sendPacketCount = 0;
+ static uint64 sendPacketBytes = 0;
+
+ static time_t firstTime = time(NULL);
+ static time_t lastTime = firstTime; // next 60 secs start time
+
+ static uint64 sendLastPacketCount = 0;
+ static uint64 sendLastPacketBytes = 0;
+
+ time_t cur_time = time(NULL);
+
+ if((cur_time - lastTime) < 60)
+ {
+ sendPacketCount+=1;
+ sendPacketBytes+=packet->size();
+
+ sendLastPacketCount+=1;
+ sendLastPacketBytes+=packet->size();
+ }
+ else
+ {
+ uint64 minTime = uint64(cur_time - lastTime);
+ uint64 fullTime = uint64(lastTime - firstTime);
+ sLog.outDetail("Send all time packets count: " I64FMTD " bytes: " I64FMTD " avr.count/sec: %f avr.bytes/sec: %f time: %u",sendPacketCount,sendPacketBytes,float(sendPacketCount)/fullTime,float(sendPacketBytes)/fullTime,uint32(fullTime));
+ sLog.outDetail("Send last min packets count: " I64FMTD " bytes: " I64FMTD " avr.count/sec: %f avr.bytes/sec: %f",sendLastPacketCount,sendLastPacketBytes,float(sendLastPacketCount)/minTime,float(sendLastPacketBytes)/minTime);
+
+ lastTime = cur_time;
+ sendLastPacketCount = 1;
+ sendLastPacketBytes = packet->wpos(); // wpos is real written size
+ }
+#endif // !MANGOS_DEBUG
+
+ if (m_Socket->SendPacket (*packet) == -1)
+ {
+ m_Socket->CloseSocket ();
+ }
+}
+
+/// Add an incoming packet to the queue
+void WorldSession::QueuePacket(WorldPacket* new_packet)
+{
+ _recvQueue.add(new_packet);
+}
+
+/// Logging helper for unexpected opcodes
+void WorldSession::logUnexpectedOpcode(WorldPacket* packet, const char *reason)
+{
+ sLog.outError( "SESSION: received unexpected opcode %s (0x%.4X) %s",
+ LookupOpcodeName(packet->GetOpcode()),
+ packet->GetOpcode(),
+ reason);
+}
+
+/// Update the WorldSession (triggered by World update)
+bool WorldSession::Update(uint32 /*diff*/)
+{
+ if (m_Socket)
+ if (m_Socket->IsClosed ())
+ {
+ m_Socket->RemoveReference ();
+ m_Socket = NULL;
+ }
+
+ WorldPacket *packet;
+
+ ///- Retrieve packets from the receive queue and call the appropriate handlers
+ /// \todo Is there a way to consolidate the OpcondeHandlerTable and the g_worldOpcodeNames to only maintain 1 list?
+ /// answer : there is a way, but this is better, because it would use redundant RAM
+ while (!_recvQueue.empty())
+ {
+ packet = _recvQueue.next();
+
+ /*#if 1
+ sLog.outError( "MOEP: %s (0x%.4X)",
+ LookupOpcodeName(packet->GetOpcode()),
+ packet->GetOpcode());
+ #endif*/
+
+ if(packet->GetOpcode() >= NUM_MSG_TYPES)
+ {
+ sLog.outError( "SESSION: received non-existed opcode %s (0x%.4X)",
+ LookupOpcodeName(packet->GetOpcode()),
+ packet->GetOpcode());
+ }
+ else
+ {
+ OpcodeHandler& opHandle = opcodeTable[packet->GetOpcode()];
+ switch (opHandle.status)
+ {
+ case STATUS_LOGGEDIN:
+ if(!_player)
+ {
+ // skip STATUS_LOGGEDIN opcode unexpected errors if player logout sometime ago - this can be network lag delayed packets
+ if(!m_playerRecentlyLogout)
+ logUnexpectedOpcode(packet, "the player has not logged in yet");
+ }
+ else if(_player->IsInWorld())
+ (this->*opHandle.handler)(*packet);
+ // lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer
+ break;
+ case STATUS_TRANSFER_PENDING:
+ if(!_player)
+ logUnexpectedOpcode(packet, "the player has not logged in yet");
+ else if(_player->IsInWorld())
+ logUnexpectedOpcode(packet, "the player is still in world");
+ else
+ (this->*opHandle.handler)(*packet);
+ break;
+ case STATUS_AUTHED:
+ m_playerRecentlyLogout = false;
+ (this->*opHandle.handler)(*packet);
+ break;
+ case STATUS_NEVER:
+ sLog.outError( "SESSION: received not allowed opcode %s (0x%.4X)",
+ LookupOpcodeName(packet->GetOpcode()),
+ packet->GetOpcode());
+ break;
+ }
+ }
+
+ delete packet;
+ }
+
+ ///- If necessary, log the player out
+ time_t currTime = time(NULL);
+ if (!m_Socket || (ShouldLogOut(currTime) && !m_playerLoading))
+ LogoutPlayer(true);
+
+ if (!m_Socket)
+ return false; //Will remove this session from the world session map
+
+ return true;
+}
+
+/// %Log the player out
+void WorldSession::LogoutPlayer(bool Save)
+{
+ // finish pending transfers before starting the logout
+ while(_player && _player->IsBeingTeleported())
+ HandleMoveWorldportAckOpcode();
+
+ m_playerLogout = true;
+
+ if (_player)
+ {
+ ///- If the player just died before logging out, make him appear as a ghost
+ //FIXME: logout must be delayed in case lost connection with client in time of combat
+ if (_player->GetDeathTimer())
+ {
+ _player->getHostilRefManager().deleteReferences();
+ _player->BuildPlayerRepop();
+ _player->RepopAtGraveyard();
+ }
+ else if (!_player->getAttackers().empty())
+ {
+ _player->CombatStop();
+ _player->getHostilRefManager().setOnlineOfflineState(false);
+ _player->RemoveAllAurasOnDeath();
+
+ // build set of player who attack _player or who have pet attacking of _player
+ std::set<Player*> aset;
+ for(Unit::AttackerSet::const_iterator itr = _player->getAttackers().begin(); itr != _player->getAttackers().end(); ++itr)
+ {
+ Unit* owner = (*itr)->GetOwner(); // including player controlled case
+ if(owner)
+ {
+ if(owner->GetTypeId()==TYPEID_PLAYER)
+ aset.insert((Player*)owner);
+ }
+ else
+ if((*itr)->GetTypeId()==TYPEID_PLAYER)
+ aset.insert((Player*)(*itr));
+ }
+
+ _player->SetPvPDeath(!aset.empty());
+ _player->KillPlayer();
+ _player->BuildPlayerRepop();
+ _player->RepopAtGraveyard();
+
+ // give honor to all attackers from set like group case
+ for(std::set<Player*>::const_iterator itr = aset.begin(); itr != aset.end(); ++itr)
+ (*itr)->RewardHonor(_player,aset.size());
+
+ // give bg rewards and update counters like kill by first from attackers
+ // this can't be called for all attackers.
+ if(!aset.empty())
+ if(BattleGround *bg = _player->GetBattleGround())
+ bg->HandleKillPlayer(_player,*aset.begin());
+ }
+ else if(_player->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
+ {
+ // this will kill character by SPELL_AURA_SPIRIT_OF_REDEMPTION
+ _player->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT);
+ //_player->SetDeathPvP(*); set at SPELL_AURA_SPIRIT_OF_REDEMPTION apply time
+ _player->KillPlayer();
+ _player->BuildPlayerRepop();
+ _player->RepopAtGraveyard();
+ }
+
+ ///- Remove player from battleground (teleport to entrance)
+ if(_player->InBattleGround())
+ _player->LeaveBattleground();
+
+ for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ {
+ if(int32 bgTypeId = _player->GetBattleGroundQueueId(i))
+ {
+ _player->RemoveBattleGroundQueueId(bgTypeId);
+ sBattleGroundMgr.m_BattleGroundQueues[ bgTypeId ].RemovePlayer(_player->GetGUID(), true);
+ }
+ }
+
+ ///- Reset the online field in the account table
+ // no point resetting online in character table here as Player::SaveToDB() will set it to 1 since player has not been removed from world at this stage
+ //No SQL injection as AccountID is uint32
+ loginDatabase.PExecute("UPDATE account SET online = 0 WHERE id = '%u'", GetAccountId());
+
+ ///- If the player is in a guild, update the guild roster and broadcast a logout message to other guild members
+ Guild *guild = objmgr.GetGuildById(_player->GetGuildId());
+ if(guild)
+ {
+ guild->LoadPlayerStatsByGuid(_player->GetGUID());
+ guild->UpdateLogoutTime(_player->GetGUID());
+
+ WorldPacket data(SMSG_GUILD_EVENT, (1+1+12+8)); // name limited to 12 in character table.
+ data<<(uint8)GE_SIGNED_OFF;
+ data<<(uint8)1;
+ data<<_player->GetName();
+ data<<_player->GetGUID();
+ guild->BroadcastPacket(&data);
+ }
+
+ ///- Remove pet
+ _player->RemovePet(NULL,PET_SAVE_AS_CURRENT, true);
+
+ ///- empty buyback items and save the player in the database
+ // some save parts only correctly work in case player present in map/player_lists (pets, etc)
+ if(Save)
+ {
+ uint32 eslot;
+ for(int j = BUYBACK_SLOT_START; j < BUYBACK_SLOT_END; j++)
+ {
+ eslot = j - BUYBACK_SLOT_START;
+ _player->SetUInt64Value(PLAYER_FIELD_VENDORBUYBACK_SLOT_1+eslot*2,0);
+ _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1+eslot,0);
+ _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1+eslot,0);
+ }
+ _player->SaveToDB();
+ }
+
+ ///- Leave all channels before player delete...
+ _player->CleanupChannels();
+
+ ///- If the player is in a group (or invited), remove him. If the group if then only 1 person, disband the group.
+ _player->UninviteFromGroup();
+
+ // remove player from the group if he is:
+ // a) in group; b) not in raid group; c) logging out normally (not being kicked or disconnected)
+ if(_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && m_Socket)
+ _player->RemoveFromGroup();
+
+ ///- Remove the player from the world
+ // the player may not be in the world when logging out
+ // e.g if he got disconnected during a transfer to another map
+ // calls to GetMap in this case may cause crashes
+ if(_player->IsInWorld()) MapManager::Instance().GetMap(_player->GetMapId(), _player)->Remove(_player, false);
+ // RemoveFromWorld does cleanup that requires the player to be in the accessor
+ ObjectAccessor::Instance().RemoveObject(_player);
+
+ ///- Send update to group
+ if(_player->GetGroup())
+ _player->GetGroup()->SendUpdate();
+
+ ///- Broadcast a logout message to the player's friends
+ sSocialMgr.SendFriendStatus(_player, FRIEND_OFFLINE, _player->GetGUIDLow(), "", true);
+
+ ///- Delete the player object
+ _player->CleanupsBeforeDelete(); // do some cleanup before deleting to prevent crash at crossreferences to already deleted data
+
+ delete _player;
+ _player = NULL;
+
+ ///- Send the 'logout complete' packet to the client
+ WorldPacket data( SMSG_LOGOUT_COMPLETE, 0 );
+ SendPacket( &data );
+
+ ///- Since each account can only have one online character at any given time, ensure all characters for active account are marked as offline
+ //No SQL injection as AccountId is uint32
+ CharacterDatabase.PExecute("UPDATE characters SET online = 0 WHERE account = '%u'", GetAccountId());
+ sLog.outDebug( "SESSION: Sent SMSG_LOGOUT_COMPLETE Message" );
+ }
+
+ m_playerLogout = false;
+ m_playerRecentlyLogout = true;
+ LogoutRequest(0);
+}
+
+/// Kick a player out of the World
+void WorldSession::KickPlayer()
+{
+ if (m_Socket)
+ {
+ m_Socket->CloseSocket ();
+ }
+}
+
+/// Cancel channeling handler
+
+void WorldSession::SendAreaTriggerMessage(const char* Text, ...)
+{
+ va_list ap;
+ char szStr [1024];
+ szStr[0] = '\0';
+
+ va_start(ap, Text);
+ vsnprintf( szStr, 1024, Text, ap );
+ va_end(ap);
+
+ uint32 length = strlen(szStr)+1;
+ WorldPacket data(SMSG_AREA_TRIGGER_MESSAGE, 4+length);
+ data << length;
+ data << szStr;
+ SendPacket(&data);
+}
+
+void WorldSession::SendNotification(const char *format,...)
+{
+ if(format)
+ {
+ va_list ap;
+ char szStr [1024];
+ szStr[0] = '\0';
+ va_start(ap, format);
+ vsnprintf( szStr, 1024, format, ap );
+ va_end(ap);
+
+ WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1));
+ data << szStr;
+ SendPacket(&data);
+ }
+}
+
+void WorldSession::SendNotification(int32 string_id,...)
+{
+ char const* format = GetMangosString(string_id);
+ if(format)
+ {
+ va_list ap;
+ char szStr [1024];
+ szStr[0] = '\0';
+ va_start(ap, format);
+ vsnprintf( szStr, 1024, format, ap );
+ va_end(ap);
+
+ WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1));
+ data << szStr;
+ SendPacket(&data);
+ }
+}
+
+const char * WorldSession::GetMangosString( int32 entry )
+{
+ return objmgr.GetMangosString(entry,GetSessionDbLocaleIndex());
+}
+
+void WorldSession::Handle_NULL( WorldPacket& recvPacket )
+{
+ sLog.outError( "SESSION: received unhandled opcode %s (0x%.4X)",
+ LookupOpcodeName(recvPacket.GetOpcode()),
+ recvPacket.GetOpcode());
+}
+
+void WorldSession::Handle_EarlyProccess( WorldPacket& recvPacket )
+{
+ sLog.outError( "SESSION: received opcode %s (0x%.4X) that must be proccessed in WorldSocket::OnRead",
+ LookupOpcodeName(recvPacket.GetOpcode()),
+ recvPacket.GetOpcode());
+}
+
+void WorldSession::Handle_ServerSide( WorldPacket& recvPacket )
+{
+ sLog.outError( "SESSION: received sever-side opcode %s (0x%.4X)",
+ LookupOpcodeName(recvPacket.GetOpcode()),
+ recvPacket.GetOpcode());
+}
+
+void WorldSession::Handle_Depricated( WorldPacket& recvPacket )
+{
+ sLog.outError( "SESSION: received depricated opcode %s (0x%.4X)",
+ LookupOpcodeName(recvPacket.GetOpcode()),
+ recvPacket.GetOpcode());
+}
+
+void WorldSession::SendAuthWaitQue(uint32 position)
+ {
+ if(position == 0)
+ {
+ WorldPacket packet( SMSG_AUTH_RESPONSE, 1 );
+ packet << uint8( AUTH_OK );
+ SendPacket(&packet);
+ }
+ else
+ {
+ WorldPacket packet( SMSG_AUTH_RESPONSE, 5 );
+ packet << uint8( AUTH_WAIT_QUEUE );
+ packet << uint32 (position);
+ SendPacket(&packet);
+ }
+ }
+
+
|