diff options
Diffstat (limited to 'src/game/WorldSession.cpp')
-rw-r--r-- | src/game/WorldSession.cpp | 1022 |
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); + } + } + + |