/// %Log the player out void WorldSession::LogoutPlayer(bool Save) { // finish pending transfers before starting the logout while (_player && _player->IsBeingTeleportedFar()) HandleMoveWorldportAckOpcode(); m_playerLogout = true; m_playerSave = Save; if (_player) { sLog.outChar("Account: %d (IP: %s) Logout Character:[%s] (guid: %u)", GetAccountId(), GetRemoteAddress().c_str(), _player->GetName() , _player->GetGUIDLow()); if (ObjectGuid lootGuid = GetPlayer()->GetLootGuid()) DoLootRelease(lootGuid); ///- 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->getHostileRefManager().deleteReferences(); _player->BuildPlayerRepop(); _player->RepopAtGraveyard(); } else if (!_player->getAttackers().empty()) { _player->CombatStop(); _player->getHostileRefManager().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(); } // drop a flag if player is carrying it if (BattleGround* bg = _player->GetBattleGround()) bg->EventPlayerLoggedOut(_player); ///- Teleport to home if the player is in an invalid instance if (!_player->m_InstanceValid && !_player->isGameMaster()) { _player->TeleportToHomebind(); // this is a bad place to call for far teleport because we need player to be in world for successful logout // maybe we should implement delayed far teleport logout? } // FG: finish pending transfers after starting the logout // this should fix players beeing able to logout and login back with full hp at death position while (_player->IsBeingTeleportedFar()) HandleMoveWorldportAckOpcode(); for (int i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) { if (BattleGroundQueueTypeId bgQueueTypeId = _player->GetBattleGroundQueueTypeId(i)) { _player->RemoveBattleGroundQueueId(bgQueueTypeId); sBattleGroundMgr.m_BattleGroundQueues[ bgQueueTypeId ].RemovePlayer(_player->GetObjectGuid(), 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 static SqlStatementID id; SqlStatement stmt = LoginDatabase.CreateStatement(id, "UPDATE account SET active_realm_id = ? WHERE id = ?"); stmt.PExecute(uint32(0), GetAccountId()); ///- If the player is in a guild, update the guild roster and broadcast a logout message to other guild members if (Guild* guild = sGuildMgr.GetGuildById(_player->GetGuildId())) { if (MemberSlot* slot = guild->GetMemberSlot(_player->GetObjectGuid())) { slot->SetMemberStats(_player); slot->UpdateLogoutTime(); } guild->BroadcastEvent(GE_SIGNED_OFF, _player->GetObjectGuid(), _player->GetName()); } ///- Remove pet _player->RemovePet(PET_SAVE_AS_CURRENT); ///- 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) _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(); ///- 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->GetObjectGuid(), true); sSocialMgr.RemovePlayerSocial(_player->GetGUIDLow()); ///- 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()) { Map* _map = _player->GetMap(); _map->Remove(_player, true); } else { _player->CleanupsBeforeDelete(); Map::DeleteFromWorld(_player); } SetPlayer(NULL); // deleted in Remove/DeleteFromWorld call ///- 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 static SqlStatementID updChars; stmt = CharacterDatabase.CreateStatement(updChars, "UPDATE characters SET online = 0 WHERE account = ?"); stmt.PExecute(GetAccountId()); DEBUG_LOG("SESSION: Sent SMSG_LOGOUT_COMPLETE Message"); } m_playerLogout = false; m_playerSave = false; m_playerRecentlyLogout = true; LogoutRequest(0); }
/// %Log the player out void WorldSession::LogoutPlayer(bool Save) { // finish pending transfers before starting the logout while(_player && _player->IsBeingTeleportedFar()) HandleMoveWorldportAckOpcode(); m_playerLogout = true; if (_player) { if (uint64 lguid = GetPlayer()->GetLootGUID()) DoLootRelease(lguid); ///- 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(); } //drop a flag if player is carrying it if(BattleGround *bg = _player->GetBattleGround()) bg->EventPlayerLoggedOut(_player); ///- Teleport to home if the player is in an invalid instance if(!_player->m_InstanceValid && !_player->isGameMaster()) { _player->TeleportTo(_player->m_homebindMapId, _player->m_homebindX, _player->m_homebindY, _player->m_homebindZ, _player->GetOrientation()); //this is a bad place to call for far teleport because we need player to be in world for successful logout //maybe we should implement delayed far teleport logout? while(_player->IsBeingTeleportedFar()) HandleMoveWorldportAckOpcode(); } for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) { if(BattleGroundQueueTypeId bgQueueTypeId = _player->GetBattleGroundQueueTypeId(i)) { _player->RemoveBattleGroundQueueId(bgQueueTypeId); sBattleGroundMgr.m_BattleGroundQueues[ bgQueueTypeId ].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(); ///- 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); sSocialMgr.RemovePlayerSocial (_player->GetGUIDLow ()); ///- 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()) _player->GetMap()->Remove(_player, false); // RemoveFromWorld does cleanup that requires the player to be in the accessor ObjectAccessor::Instance().RemoveObject(_player); ///- 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); }
/// %Log the player out void WorldSession::LogoutPlayer(bool Save) { // finish pending transfers before starting the logout while (_player && _player->IsBeingTeleportedFar()) HandleMoveWorldportAckOpcode(); m_playerLogout = true; m_playerSave = Save; if (_player) { if (uint64 lguid = GetPlayer()->GetLootGUID()) DoLootRelease(lguid); ///- 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->getHostileRefManager().deleteReferences(); _player->BuildPlayerRepop(); _player->RepopAtGraveyard(); } else if (!_player->getAttackers().empty() || _player->isCharmed()) { _player->CombatStop(); _player->getHostileRefManager().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 && owner->GetTypeId() == TYPEID_PLAYER) aset.insert(owner->ToPlayer()); 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->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); _player->KillPlayer(); _player->BuildPlayerRepop(); _player->RepopAtGraveyard(); } else if (_player->HasPendingBind()) { _player->RepopAtGraveyard(); _player->SetPendingBind(0, 0); } //drop a flag if player is carrying it if (Battleground *bg = _player->GetBattleground()) bg->EventPlayerLoggedOut(_player); ///- Teleport to home if the player is in an invalid instance if (!_player->m_InstanceValid && !_player->isGameMaster()) _player->TeleportTo(_player->m_homebindMapId, _player->m_homebindX, _player->m_homebindY, _player->m_homebindZ, _player->GetOrientation()); sOutdoorPvPMgr->HandlePlayerLeaveZone(_player, _player->GetZoneId()); for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) { if (BattlegroundQueueTypeId bgQueueTypeId = _player->GetBattlegroundQueueTypeId(i)) { _player->RemoveBattlegroundQueueId(bgQueueTypeId); sBattlegroundMgr->m_BattlegroundQueues[ bgQueueTypeId ].RemovePlayer(_player->GetGUID(), true); } } // Repop at GraveYard or other player far teleport will prevent saving player because of not present map // Teleport player immediately for correct player save while (_player->IsBeingTeleportedFar()) HandleMoveWorldportAckOpcode(); ///- If the player is in a guild, update the guild roster and broadcast a logout message to other guild members if (Guild *pGuild = sGuildMgr->GetGuildById(_player->GetGuildId())) pGuild->HandleMemberLogout(this); ///- Remove pet _player->RemovePet(NULL, PET_SLOT_ACTUAL_PET_SLOT, 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(); ///- Send update to group and reset stored max enchanting level if (_player->GetGroup()) { _player->GetGroup()->SendUpdate(); _player->GetGroup()->ResetMaxEnchantingLevel(); } ///- Broadcast a logout message to the player's friends sSocialMgr->SendFriendStatus(_player, FRIEND_OFFLINE, _player->GetGUIDLow(), true); sSocialMgr->RemovePlayerSocial (_player->GetGUIDLow ()); // Call script hook before deletion sScriptMgr->OnPlayerLogout(GetPlayer()); ///- 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 _player->CleanupsBeforeDelete(); sLogMgr->WriteLn(CHAR_LOG, "Account: %d (IP: %s) Logout Character:[%s] (GUID: %u)", GetAccountId(), GetRemoteAddress().c_str(), _player->GetName() , _player->GetGUIDLow()); Map *_map = _player->GetMap(); _map->Remove(_player, true); SetPlayer(NULL); // deleted in Remove call ///- 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(LOG_FILTER_NETWORKIO, "SESSION: Sent SMSG_LOGOUT_COMPLETE Message"); } m_playerLogout = false; m_playerSave = false; m_playerRecentlyLogout = true; LogoutRequest(0); }
/// %Log the player out void WorldSession::LogoutPlayer(bool Save) { if (!_player) return; if (_player->IsMounted()) _player->Unmount(); // PlayerBot mod: log out all playerbots owned by this character //while(!m_playerBots.empty()) // LogoutPlayerBot(m_playerBots.begin()->first, Save); PlayerBotMap m_pBots; uint8 m_botCount = 0; for(PlayerBotMap::const_iterator itr = GetPlayerBotsBegin(); itr != GetPlayerBotsEnd(); ++itr) { Player *bot = itr->second; (m_pBots)[itr->first] = bot; ++m_botCount; } // Create a solo bind for player if player is currently in group in instance with all bots Group *m_Group = _player->GetGroup(); bool rebound = false; if(m_botCount > 0 && m_Group && m_botCount == m_Group->GetMembersCount()-1) if (InstanceSave *save = sInstanceSaveManager.GetInstanceSave(_player->GetInstanceId())) { _player->BindToInstance(save, false); save->SetCanReset(false); rebound = true; } for(PlayerBotMap::const_iterator itr2 = m_pBots.begin(); itr2 != m_pBots.end(); ++itr2) { Player *botPlayer = itr2->second; if (!botPlayer) continue; LogoutPlayerBot(botPlayer->GetGUID(), Save); } if (rebound) _player->m_InstanceValid = true; // finish pending transfers before starting the logout while (_player && _player->IsBeingTeleportedFar()) HandleMoveWorldportAckOpcode(); m_playerLogout = true; m_playerSave = Save; if (_player) { sLFGMgr.Leave(_player); GetPlayer()->GetSession()->SendLfgUpdateParty(LFG_UPDATETYPE_REMOVED_FROM_QUEUE); GetPlayer()->GetSession()->SendLfgUpdatePlayer(LFG_UPDATETYPE_REMOVED_FROM_QUEUE); GetPlayer()->GetSession()->SendLfgUpdateSearch(false); if (uint64 lguid = GetPlayer()->GetLootGUID()) DoLootRelease(lguid); ///- 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->getHostileRefManager().deleteReferences(); _player->BuildPlayerRepop(); _player->RepopAtGraveyard(); } else if (!_player->getAttackers().empty()) { _player->CombatStop(); _player->getHostileRefManager().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(owner->ToPlayer()); } 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->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); //_player->SetDeathPvP(*); set at SPELL_AURA_SPIRIT_OF_REDEMPTION apply time _player->KillPlayer(); _player->BuildPlayerRepop(); _player->RepopAtGraveyard(); } //drop a flag if player is carrying it if (BattleGround *bg = _player->GetBattleGround()) bg->EventPlayerLoggedOut(_player); ///- Teleport to home if the player is in an invalid instance if ((!_player->m_InstanceValid && !_player->isGameMaster()) || (_player->IsPlayerbot() && _player->GetMap()->IsDungeon())) _player->TeleportTo(_player->m_homebindMapId, _player->m_homebindX, _player->m_homebindY, _player->m_homebindZ, _player->GetOrientation()); sOutdoorPvPMgr.HandlePlayerLeaveZone(_player,_player->GetZoneId()); for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) { if (BattleGroundQueueTypeId bgQueueTypeId = _player->GetBattleGroundQueueTypeId(i)) { _player->RemoveBattleGroundQueueId(bgQueueTypeId); sBattleGroundMgr.m_BattleGroundQueues[ bgQueueTypeId ].RemovePlayer(_player->GetGUID(), true); } } // Repop at GraveYard or other player far teleport will prevent saving player because of not present map // Teleport player immediately for correct player save while (_player->IsBeingTeleportedFar()) HandleMoveWorldportAckOpcode(); ///- 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->SetMemberStats(_player->GetGUID()); guild->UpdateLogoutTime(_player->GetGUID()); guild->BroadcastEvent(GE_SIGNED_OFF, _player->GetGUID(), 1, _player->GetName(), "", ""); } ///- 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->IsPlayerbot() && _player->GetGroup())) _player->RemoveFromGroup(); ///- Send update to group and reset stored max enchanting level if (_player->GetGroup()) { _player->GetGroup()->SendUpdate(); _player->GetGroup()->ResetMaxEnchantingLevel(); } ///- Broadcast a logout message to the player's friends sSocialMgr.SendFriendStatus(_player, FRIEND_OFFLINE, _player->GetGUIDLow(), true); sSocialMgr.RemovePlayerSocial (_player->GetGUIDLow ()); ///- 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 _player->CleanupsBeforeDelete(); sLog.outChar("Account: %d (IP: %s) Logout Character:[%s] (GUID: %u)", GetAccountId(), GetRemoteAddress().c_str(), _player->GetName() ,_player->GetGUIDLow()); Map* _map = _player->GetMap(); uint32 guid = _player->GetGUIDLow(); _map->Remove(_player, true); SetPlayer(NULL); // deleted in Remove call ///- 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'", guid); sLog.outDebug("SESSION: Sent SMSG_LOGOUT_COMPLETE Message"); } //Hook for OnLogout Event sScriptMgr.OnLogout(_player); m_playerLogout = false; m_playerSave = false; m_playerRecentlyLogout = true; LogoutRequest(0); }
void WorldSession::HandleLogoutRequestOpcode( WorldPacket & /*recv_data*/ ) { DEBUG_LOG( "WORLD: Recvd CMSG_LOGOUT_REQUEST Message, security - %u", GetSecurity() ); if (ObjectGuid lootGuid = GetPlayer()->GetLootGuid()) DoLootRelease(lootGuid); uint8 reason = 0; Player* plr = GetPlayer(); if (!plr) { LogoutPlayer(false); return; } if (plr->isInCombat()) reason = 1; else if (plr->m_movementInfo.HasMovementFlag(MovementFlags(MOVEFLAG_FALLING | MOVEFLAG_FALLINGFAR))) // is jumping or falling reason = 3; else if (plr->duel || plr->HasAura(9454)) // is dueling or frozen by GM via freeze command reason = 2; if (reason) { WorldPacket data( SMSG_LOGOUT_RESPONSE, 1+4 ) ; data << reason; data << uint32(0); SendPacket( &data ); LogoutRequest(0); return; } //instant logout in taverns/cities or on taxi or for admins, gm's, mod's if its enabled in mangosd.conf if (GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) || GetPlayer()->IsTaxiFlying() || GetSecurity() >= (AccountTypes)sWorld.getConfig(CONFIG_UINT32_INSTANT_LOGOUT)) { WorldPacket data(SMSG_LOGOUT_RESPONSE, 1 + 4); data << reason; data << uint32(16777216); SendPacket(&data); LogoutPlayer(true); return; } // not set flags if player can't free move to prevent lost state at logout cancel if (GetPlayer()->CanFreeMove()) { float height = GetPlayer()->GetMap()->GetHeight(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY(), GetPlayer()->GetPositionZ()); if ((GetPlayer()->GetPositionZ() < height + 0.1f) && !(GetPlayer()->IsInWater())) GetPlayer()->SetStandState(UNIT_STAND_STATE_SIT); GetPlayer()->SetRoot(true); GetPlayer()->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); } WorldPacket data(SMSG_LOGOUT_RESPONSE, 1 + 4); data << uint8(0); data << uint32(0); SendPacket( &data ); LogoutRequest(time(NULL)); }
/// %Log the player out void WorldSession::LogoutPlayer(bool save) { // finish pending transfers before starting the logout while (_player && _player->IsBeingTeleportedFar()) HandleMoveWorldportAckOpcode(); m_playerLogout = true; m_playerSave = save; if (_player) { if (uint64 lguid = _player->GetLootGUID()) DoLootRelease(lguid); ///- 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->getHostileRefManager().deleteReferences(); _player->BuildPlayerRepop(); _player->RepopAtGraveyard(); } else if (_player->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION)) { // this will kill character by SPELL_AURA_SPIRIT_OF_REDEMPTION _player->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); _player->KillPlayer(); _player->BuildPlayerRepop(); _player->RepopAtGraveyard(); } else if (_player->HasPendingBind()) { _player->RepopAtGraveyard(); _player->SetPendingBind(0, 0); } //drop a flag if player is carrying it if (Battleground* bg = _player->GetBattleground()) bg->EventPlayerLoggedOut(_player); ///- Teleport to home if the player is in an invalid instance if (!_player->m_InstanceValid && !_player->isGameMaster()) _player->TeleportTo(_player->m_homebindMapId, _player->m_homebindX, _player->m_homebindY, _player->m_homebindZ, _player->GetOrientation()); sOutdoorPvPMgr->HandlePlayerLeaveZone(_player, _player->GetZoneId()); for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) { if (BattlegroundQueueTypeId bgQueueTypeId = _player->GetBattlegroundQueueTypeId(i)) { _player->RemoveBattlegroundQueueId(bgQueueTypeId); BattlegroundQueue& queue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId); queue.RemovePlayer(_player->GetGUID(), true); } } // Repop at GraveYard or other player far teleport will prevent saving player because of not present map // Teleport player immediately for correct player save while (_player->IsBeingTeleportedFar()) HandleMoveWorldportAckOpcode(); ///- If the player is in a guild, update the guild roster and broadcast a logout message to other guild members if (Guild* guild = sGuildMgr->GetGuildById(_player->GetGuildId())) guild->HandleMemberLogout(this); ///- 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(); //! Send update to group and reset stored max enchanting level if (_player->GetGroup()) { _player->GetGroup()->SendUpdate(); _player->GetGroup()->ResetMaxEnchantingLevel(); } //! Broadcast a logout message to the player's friends sSocialMgr->SendFriendStatus(_player, FRIEND_OFFLINE, _player->GetGUIDLow(), true); sSocialMgr->RemovePlayerSocial(_player->GetGUIDLow()); //! Call script hook before deletion sScriptMgr->OnPlayerLogout(_player); //! 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 _player->CleanupsBeforeDelete(); sLog->outInfo(LOG_FILTER_CHARACTER, "Account: %d (IP: %s) Logout Character:[%s] (GUID: %u) Level: %d", GetAccountId(), GetRemoteAddress().c_str(), _player->GetName().c_str(), _player->GetGUIDLow(), _player->getLevel()); if (Map* _map = _player->FindMap()) _map->RemovePlayerFromMap(_player, true); SetPlayer(NULL); //! Pointer already deleted during RemovePlayerFromMap //! Send the 'logout complete' packet to the client //! Client will respond by sending 3x CMSG_CANCEL_TRADE, which we currently dont handle WorldPacket data(SMSG_LOGOUT_COMPLETE, 0); SendPacket(&data); sLog->outDebug(LOG_FILTER_NETWORKIO, "SESSION: Sent SMSG_LOGOUT_COMPLETE Message"); //! Since each account can only have one online character at any given time, ensure all characters for active account are marked as offline PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ACCOUNT_ONLINE); stmt->setUInt32(0, GetAccountId()); CharacterDatabase.Execute(stmt); } m_playerLogout = false; m_playerSave = false; m_playerRecentlyLogout = true; LogoutRequest(0); }
/// %Log the player out void WorldSession::LogoutPlayer(bool Save) { // finish pending transfers before starting the logout while (_player && _player->IsBeingTeleportedFar()) HandleMoveWorldportAckOpcode(); m_playerLogout = true; m_playerSave = Save; if (_player) { #ifdef BUILD_PLAYERBOT // Log out all player bots owned by this toon if (_player->GetPlayerbotMgr()) _player->GetPlayerbotMgr()->LogoutAllBots(); #endif sLog.outChar("Account: %d (IP: %s) Logout Character:[%s] (guid: %u)", GetAccountId(), GetRemoteAddress().c_str(), _player->GetName(), _player->GetGUIDLow()); if (Loot* loot = sLootMgr.GetLoot(_player)) loot->Release(_player); if (_player->GetDeathTimer()) { _player->getHostileRefManager().deleteReferences(); _player->BuildPlayerRepop(); _player->RepopAtGraveyard(); } 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(); } else if (_player->isInCombat()) _player->CombatStop(true, true); // drop a flag if player is carrying it if (BattleGround* bg = _player->GetBattleGround()) bg->EventPlayerLoggedOut(_player); ///- Teleport to home if the player is in an invalid instance if (!_player->m_InstanceValid && !_player->isGameMaster()) { _player->TeleportToHomebind(); // this is a bad place to call for far teleport because we need player to be in world for successful logout // maybe we should implement delayed far teleport logout? } // FG: finish pending transfers after starting the logout // this should fix players beeing able to logout and login back with full hp at death position while (_player->IsBeingTeleportedFar()) HandleMoveWorldportAckOpcode(); for (int i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) { if (BattleGroundQueueTypeId bgQueueTypeId = _player->GetBattleGroundQueueTypeId(i)) { _player->RemoveBattleGroundQueueId(bgQueueTypeId); sBattleGroundMgr.m_BattleGroundQueues[ bgQueueTypeId ].RemovePlayer(_player->GetObjectGuid(), 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 static SqlStatementID id; #ifdef BUILD_PLAYERBOT if (!_player->GetPlayerbotAI()) { // Unmodded core code below SqlStatement stmt = LoginDatabase.CreateStatement(id, "UPDATE account SET active_realm_id = ? WHERE id = ?"); stmt.PExecute(uint32(0), GetAccountId()); } #else SqlStatement stmt = LoginDatabase.CreateStatement(id, "UPDATE account SET active_realm_id = ? WHERE id = ?"); stmt.PExecute(uint32(0), GetAccountId()); #endif ///- If the player is in a guild, update the guild roster and broadcast a logout message to other guild members if (Guild* guild = sGuildMgr.GetGuildById(_player->GetGuildId())) { if (MemberSlot* slot = guild->GetMemberSlot(_player->GetObjectGuid())) { slot->SetMemberStats(_player); slot->UpdateLogoutTime(); } guild->BroadcastEvent(GE_SIGNED_OFF, _player->GetObjectGuid(), _player->GetName()); } ///- Remove pet _player->RemovePet(PET_SAVE_AS_CURRENT); ///- 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) _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 && !m_Socket->IsClosed()) _player->RemoveFromGroup(); ///- Send update to group if (Group* group = _player->GetGroup()) group->UpdatePlayerOnlineStatus(_player, false); ///- Broadcast a logout message to the player's friends sSocialMgr.SendFriendStatus(_player, FRIEND_OFFLINE, _player->GetObjectGuid(), true); sSocialMgr.RemovePlayerSocial(_player->GetGUIDLow()); #ifdef BUILD_PLAYERBOT // Remember player GUID for update SQL below uint32 guid = _player->GetGUIDLow(); #endif ///- 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()) { Map* _map = _player->GetMap(); _map->Remove(_player, true); } else { _player->CleanupsBeforeDelete(); Map::DeleteFromWorld(_player); } SetPlayer(nullptr); // deleted in Remove/DeleteFromWorld call ///- Send the 'logout complete' packet to the client WorldPacket data(SMSG_LOGOUT_COMPLETE, 0); SendPacket(data); static SqlStatementID updChars; #ifdef BUILD_PLAYERBOT // Set for only character instead of accountid // Different characters can be alive as bots SqlStatement stmt = CharacterDatabase.CreateStatement(updChars, "UPDATE characters SET online = 0 WHERE guid = ?"); stmt.PExecute(guid); #else ///- 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 stmt = CharacterDatabase.CreateStatement(updChars, "UPDATE characters SET online = 0 WHERE account = ?"); stmt.PExecute(GetAccountId()); #endif DEBUG_LOG("SESSION: Sent SMSG_LOGOUT_COMPLETE Message"); } m_playerLogout = false; m_playerSave = false; m_playerRecentlyLogout = true; LogoutRequest(0); }
//----------------------------------------------------------------------------- void WorldSession::LogoutPlayer() { if (_player) { // Save the player _player->SaveToDB(); // Issue a message saying we left the world if (!_player->m_gmInvisible) { std::string outstring = "|c8fff9920"; outstring.append( _player->GetName()); outstring.append( " has left the world.|r" ); sWorld.SendWorldText( outstring.c_str() ); } _player->ClearHate(); // Remove ourself from a group if (_player->IsInGroup()) { _player->UnSetInGroup(); Group *group; group = objmgr.GetGroupByLeader(_player->GetGroupLeader()); if (group->RemoveMember(_player->GetGUID()) > 1) group->SendUpdate(); else { group->Disband(); objmgr.RemoveGroup(group); delete group; } } // Remove us from world if (_player->IsInWorld()) { sLog.outDebug( "SESSION: removing player from world" ); _player->RemoveFromMap(); } objmgr.RemoveObject_Free (_player); ////////////////////////////////////////////////// // Set Time of Logout for Player, will be used for RESTSTATE calculation std::stringstream ss; ss << "UPDATE characters SET timestamp = " << (uint32)time(NULL) << ", online = 0 WHERE guid = " << _player->GetGUID(); sDatabase.Execute (ss.str().c_str()); ////////////////////////////////////////////////// _player = NULL; WorldPacket packet; packet.Initialize( SMSG_LOGOUT_COMPLETE ); SendPacket( &packet ); sLog.outDebug( "SESSION: Sent SMSG_LOGOUT_COMPLETE Message" ); } LogoutRequest(0); }
void WorldSession::LogoutPlayer(bool Save) { if (_player) { // logging out just after died if (_player->GetDeathTimer()) { _player->KillPlayer(); _player->BuildPlayerRepop(); } sDatabase.PExecute("UPDATE `character` SET `online` = 0 WHERE `guid` = '%u'", _player->GetGUID()); loginDatabase.PExecute("UPDATE `account` SET `online` = 0 WHERE `id` = '%u'", GetAccountId()); if (_player->IsInGroup()) { _player->UnSetInGroup(); Group *group; group = objmgr.GetGroupByLeader(_player->GetGroupLeader()); if(group!=NULL) { if (group->RemoveMember(_player->GetGUID()) > 1) group->SendUpdate(); else { group->Disband(); objmgr.RemoveGroup(group); delete group; } } } Guild *guild; guild = objmgr.GetGuildById(_player->GetGuildId()); if(guild) { guild->LoadPlayerStatsByGuid(_player->GetGUID()); } _player->UnsummonPet(); _player->Uncharm(); _player->UnsummonTotem(); ObjectAccessor::Instance().RemovePlayer(_player); MapManager::Instance().GetMap(_player->GetMapId())->Remove(_player, false); 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(); } delete _player; _player = 0; WorldPacket packet; packet.Initialize( SMSG_LOGOUT_COMPLETE ); SendPacket( &packet ); sLog.outDebug( "SESSION: Sent SMSG_LOGOUT_COMPLETE Message" ); } LogoutRequest(0); }
void WorldSession::LogoutPlayer(bool Save) { if (_player) { std::stringstream ss; ss << "UPDATE characters SET online = 0 WHERE guid = " << _player->GetGUID(); #ifndef __NO_PLAYERS_ARRAY__ bool found = false; uint32 loop = 0; std::stringstream ss2; ss2.rdbuf()->str(""); ss2 << _player->GetGUID(); long long int guid = atoi(ss2.str().c_str()); // Remove the player from the player list... for (loop = 0; loop < NumActivePlayers; loop++) { if (found) { ActivePlayers[loop] = ActivePlayers[loop+1]; } else if (ActivePlayers[loop] == guid) { found = true; ActivePlayers[loop] = ActivePlayers[loop+1]; } } NumActivePlayers--; #endif //__NO_PLAYERS_ARRAY__ // Remove ourself from a group if (_player->IsInGroup()) { _player->UnSetInGroup(); Group *group; group = objmgr.GetGroupByLeader(_player->GetGroupLeader()); if(group!=NULL) { if (group->RemoveMember(_player->GetGUID()) > 1) group->SendUpdate(); else { group->Disband(); objmgr.RemoveGroup(group); delete group; } } } // Remove us from world if (_player->IsInWorld()) { sLog.outDebug( "SESSION: removing player from world" ); #ifndef ENABLE_GRID_SYSTEM _player->RemoveFromMap(); #else MapManager::Instance().GetMap(_player->GetMapId())->RemoveFromMap(_player); ObjectAccessor::Instance().RemovePlayer(_player); #endif } #ifndef ENABLE_GRID_SYSTEM objmgr.RemoveObject(_player); #else MapManager::Instance().GetMap(_player->GetMapId())->Remove(_player, false); #endif if(Save) { // Save the player /* Removed this to help prevent Character coruption while the server still dies alot. ReEnable when fixed */ _player->SaveToDB(); } delete _player; _player = 0; WorldPacket packet; packet.Initialize( SMSG_LOGOUT_COMPLETE ); SendPacket( &packet ); sLog.outDebug( "SESSION: Sent SMSG_LOGOUT_COMPLETE Message" ); } LogoutRequest(0); }
/// %Log the player out void WorldSession::LogoutPlayer(bool save) { // finish pending transfers before starting the logout while (_player && _player->IsBeingTeleportedFar()) HandleMoveWorldportAckOpcode(); m_playerLogout = true; m_playerSave = save; if (_player) { if (uint64 lguid = _player->GetLootGUID()) DoLootRelease(lguid); ///- 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->getHostileRefManager().deleteReferences(); _player->BuildPlayerRepop(); _player->RepopAtGraveyard(); } else if (_player->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION)) { // this will kill character by SPELL_AURA_SPIRIT_OF_REDEMPTION _player->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); _player->KillPlayer(); _player->BuildPlayerRepop(); _player->RepopAtGraveyard(); } else if (_player->HasPendingBind()) { _player->RepopAtGraveyard(); _player->SetPendingBind(0, 0); } // pussywizard: leave whole bg on logout (character stays ingame when necessary) // pussywizard: GetBattleground() checked inside _player->LeaveBattleground(); // pussywizard: checked first time if (!_player->IsBeingTeleportedFar() && !_player->m_InstanceValid && !_player->IsGameMaster()) _player->RepopAtGraveyard(); sOutdoorPvPMgr->HandlePlayerLeaveZone(_player, _player->GetZoneId()); // pussywizard: remove from battleground queues on logout for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) if (BattlegroundQueueTypeId bgQueueTypeId = _player->GetBattlegroundQueueTypeId(i)) { _player->RemoveBattlegroundQueueId(bgQueueTypeId); sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId).RemovePlayer(_player->GetGUID(), false, i); // track if player logs out after invited to join BG if(_player->IsInvitedForBattlegroundInstance() && sWorld->getBoolConfig(CONFIG_BATTLEGROUND_TRACK_DESERTERS)) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_DESERTER_TRACK); stmt->setUInt32(0, _player->GetGUIDLow()); stmt->setUInt8(1, BG_DESERTION_TYPE_INVITE_LOGOUT); CharacterDatabase.Execute(stmt); } } ///- If the player is in a guild, update the guild roster and broadcast a logout message to other guild members if (Guild* guild = sGuildMgr->GetGuildById(_player->GetGuildId())) guild->HandleMemberLogout(this); ///- Remove pet _player->RemovePet(NULL, PET_SAVE_AS_CURRENT); // pussywizard: on logout remove auras that are removed at map change (before saving to db) // there are some positive auras from boss encounters that can be kept by logging out and logging in after boss is dead, and may be used on next bosses _player->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP); ///- 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() && !_player->GetGroup()->isLFGGroup() && m_Socket) _player->RemoveFromGroup(); // pussywizard: checked second time after being removed from a group if (!_player->IsBeingTeleportedFar() && !_player->m_InstanceValid && !_player->IsGameMaster()) _player->RepopAtGraveyard(); // Repop at GraveYard or other player far teleport will prevent saving player because of not present map // Teleport player immediately for correct player save while (_player && _player->IsBeingTeleportedFar()) HandleMoveWorldportAckOpcode(); ///- 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) SavingSystemMgr::InsertToSavingSkipListIfNeeded(_player->GetNextSave()); // pussywizard 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(false, true); } ///- Leave all channels before player delete... _player->CleanupChannels(); //! Send update to group and reset stored max enchanting level if (_player->GetGroup()) { _player->GetGroup()->SendUpdate(); _player->GetGroup()->ResetMaxEnchantingLevel(); } //! Broadcast a logout message to the player's friends if (AccountMgr::IsGMAccount(GetSecurity())) // pussywizard: only for non-gms sSocialMgr->SendFriendStatus(_player, FRIEND_OFFLINE, _player->GetGUIDLow(), true); sSocialMgr->RemovePlayerSocial(_player->GetGUIDLow()); //! Call script hook before deletion sScriptMgr->OnPlayerLogout(_player); sLog->outChar("Account: %d (IP: %s) Logout Character:[%s] (GUID: %u) Level: %d", GetAccountId(), GetRemoteAddress().c_str(), _player->GetName().c_str(), _player->GetGUIDLow(), _player->getLevel()); //! 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 _player->CleanupsBeforeDelete(); if (Map* _map = _player->FindMap()) { _map->RemovePlayerFromMap(_player, true); _map->AfterPlayerUnlinkFromMap(); } SetPlayer(NULL); // pointer already deleted //! Send the 'logout complete' packet to the client //! Client will respond by sending 3x CMSG_CANCEL_TRADE, which we currently dont handle WorldPacket data(SMSG_LOGOUT_COMPLETE, 0); SendPacket(&data); #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDebug(LOG_FILTER_NETWORKIO, "SESSION: Sent SMSG_LOGOUT_COMPLETE Message"); #endif //! Since each account can only have one online character at any given time, ensure all characters for active account are marked as offline PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ACCOUNT_ONLINE); stmt->setUInt32(0, GetAccountId()); CharacterDatabase.Execute(stmt); } m_playerLogout = false; m_playerSave = false; LogoutRequest(0); }