Core/Session: Fixed error "received unexpected opcode CMSG_CANCEL_TRADE" when a player is logging out.

This commit is contained in:
kaelima
2012-05-26 18:15:07 +02:00
parent 5956eb9ae2
commit 624aab7d77
4 changed files with 19 additions and 21 deletions
+1 -1
View File
@@ -531,7 +531,7 @@ void WorldSession::HandleBeginTradeOpcode(WorldPacket& /*recvPacket*/)
void WorldSession::SendCancelTrade() void WorldSession::SendCancelTrade()
{ {
if (m_playerRecentlyLogout) if (PlayerRecentlyLoggedOut() || PlayerLogout())
return; return;
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
+1 -1
View File
@@ -1354,7 +1354,7 @@ enum SessionStatus
STATUS_AUTHED = 0, // Player authenticated (_player == NULL, m_playerRecentlyLogout = false or will be reset before handler call, m_GUID have garbage) STATUS_AUTHED = 0, // Player authenticated (_player == NULL, m_playerRecentlyLogout = false or will be reset before handler call, m_GUID have garbage)
STATUS_LOGGEDIN, // Player in game (_player != NULL, m_GUID == _player->GetGUID(), inWorld()) STATUS_LOGGEDIN, // Player in game (_player != NULL, m_GUID == _player->GetGUID(), inWorld())
STATUS_TRANSFER, // Player transferring to another map (_player != NULL, m_GUID == _player->GetGUID(), !inWorld()) STATUS_TRANSFER, // Player transferring to another map (_player != NULL, m_GUID == _player->GetGUID(), !inWorld())
STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT, // _player!= NULL or _player == NULL && m_playerRecentlyLogout, m_GUID store last _player guid) STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT, // _player != NULL or _player == NULL && m_playerRecentlyLogout && m_playerLogout, m_GUID store last _player guid)
STATUS_NEVER, // Opcode not accepted from client (deprecated or server side only) STATUS_NEVER, // Opcode not accepted from client (deprecated or server side only)
STATUS_UNHANDLED, // Opcode not handled yet STATUS_UNHANDLED, // Opcode not handled yet
}; };
+16 -19
View File
@@ -290,7 +290,7 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
// lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer // lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer
break; break;
case STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT: case STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT:
if (!_player && !m_playerRecentlyLogout) if (!_player && !m_playerRecentlyLogout && !m_playerLogout) // There's a short delay between _player = null and m_playerRecentlyLogout = true during logout
LogUnexpectedOpcode(packet, "STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT", LogUnexpectedOpcode(packet, "STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT",
"the player has not logged in yet and not recently logout"); "the player has not logged in yet and not recently logout");
else else
@@ -404,7 +404,7 @@ void WorldSession::LogoutPlayer(bool Save)
if (_player) if (_player)
{ {
if (uint64 lguid = GetPlayer()->GetLootGUID()) if (uint64 lguid = _player->GetLootGUID())
DoLootRelease(lguid); DoLootRelease(lguid);
///- If the player just died before logging out, make him appear as a ghost ///- If the player just died before logging out, make him appear as a ghost
@@ -518,21 +518,21 @@ void WorldSession::LogoutPlayer(bool Save)
if (_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && m_Socket) if (_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && m_Socket)
_player->RemoveFromGroup(); _player->RemoveFromGroup();
///- Send update to group and reset stored max enchanting level //! Send update to group and reset stored max enchanting level
if (_player->GetGroup()) if (_player->GetGroup())
{ {
_player->GetGroup()->SendUpdate(); _player->GetGroup()->SendUpdate();
_player->GetGroup()->ResetMaxEnchantingLevel(); _player->GetGroup()->ResetMaxEnchantingLevel();
} }
///- Broadcast a logout message to the player's friends //! Broadcast a logout message to the player's friends
sSocialMgr->SendFriendStatus(_player, FRIEND_OFFLINE, _player->GetGUIDLow(), true); sSocialMgr->SendFriendStatus(_player, FRIEND_OFFLINE, _player->GetGUIDLow(), true);
sSocialMgr->RemovePlayerSocial(_player->GetGUIDLow()); sSocialMgr->RemovePlayerSocial(_player->GetGUIDLow());
// Call script hook before deletion //! Call script hook before deletion
sScriptMgr->OnPlayerLogout(GetPlayer()); sScriptMgr->OnPlayerLogout(_player);
///- Remove the player from the world //! Remove the player from the world
// the player may not be in the world when logging out // the player may not be in the world when logging out
// e.g if he got disconnected during a transfer to another map // e.g if he got disconnected during a transfer to another map
// calls to GetMap in this case may cause crashes // calls to GetMap in this case may cause crashes
@@ -540,22 +540,19 @@ void WorldSession::LogoutPlayer(bool Save)
sLog->outChar("Account: %d (IP: %s) Logout Character:[%s] (GUID: %u) Level: %d", GetAccountId(), GetRemoteAddress().c_str(), _player->GetName(), _player->GetGUIDLow(), _player->getLevel()); sLog->outChar("Account: %d (IP: %s) Logout Character:[%s] (GUID: %u) Level: %d", GetAccountId(), GetRemoteAddress().c_str(), _player->GetName(), _player->GetGUIDLow(), _player->getLevel());
if (Map* _map = _player->FindMap()) if (Map* _map = _player->FindMap())
_map->RemovePlayerFromMap(_player, true); _map->RemovePlayerFromMap(_player, true);
SetPlayer(NULL); // deleted in Remove call
///- Send the 'logout complete' packet to the client 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); WorldPacket data(SMSG_LOGOUT_COMPLETE, 0);
SendPacket(&data); 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
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ACCOUNT_ONLINE);
stmt->setUInt32(0, GetAccountId());
CharacterDatabase.Execute(stmt);
sLog->outDebug(LOG_FILTER_NETWORKIO, "SESSION: Sent SMSG_LOGOUT_COMPLETE Message"); 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_playerLogout = false;
+1
View File
@@ -221,6 +221,7 @@ class WorldSession
bool PlayerLoading() const { return m_playerLoading; } bool PlayerLoading() const { return m_playerLoading; }
bool PlayerLogout() const { return m_playerLogout; } bool PlayerLogout() const { return m_playerLogout; }
bool PlayerLogoutWithSave() const { return m_playerLogout && m_playerSave; } bool PlayerLogoutWithSave() const { return m_playerLogout && m_playerSave; }
bool PlayerRecentlyLoggedOut() const { return m_playerRecentlyLogout; }
void SizeError(WorldPacket const& packet, uint32 size) const; void SizeError(WorldPacket const& packet, uint32 size) const;