aboutsummaryrefslogtreecommitdiff
path: root/src/game/WorldSession.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/game/WorldSession.cpp')
-rw-r--r--src/game/WorldSession.cpp1022
1 files changed, 511 insertions, 511 deletions
diff --git a/src/game/WorldSession.cpp b/src/game/WorldSession.cpp
index 9a0265b823a..b8a3f9ec74e 100644
--- a/src/game/WorldSession.cpp
+++ b/src/game/WorldSession.cpp
@@ -1,511 +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(), -1, true);
-
- // 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);
- }
- }
-
-
+/*
+ * 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(), -1, true);
+
+ // 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);
+ }
+ }
+
+