feat(Core/PacketIO): Implement STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT (#15059)

Co-authored-by: Vladimir Merzliakov <29081+VladimirMangos@users.noreply.github.com>
Co-authored-by: megamage <35114+megamage@users.noreply.github.com>
Co-authored-by: Shauren <shauren.trinity@gmail.com>
Co-authored-by: Giacomo Pozzoni <giacomopoz@gmail.com>
This commit is contained in:
Kitzunu
2023-02-17 09:11:11 +01:00
committed by GitHub
parent f84848a95b
commit d7e701a1cb
6 changed files with 62 additions and 24 deletions

View File

@@ -481,6 +481,10 @@ void WorldSession::HandlePlayerLogoutOpcode(WorldPackets::Character::PlayerLogou
void WorldSession::HandleLogoutCancelOpcode(WorldPackets::Character::LogoutCancel& /*logoutCancel*/)
{
// Player have already logged out serverside, too late to cancel
if (!GetPlayer())
return;
SetLogoutStartTime(0);
SendPacket(WorldPackets::Character::LogoutCancelAck().Write());

View File

@@ -527,7 +527,7 @@ void WorldSession::HandleBeginTradeOpcode(WorldPacket& /*recvPacket*/)
void WorldSession::SendCancelTrade()
{
if (PlayerLogout())
if (PlayerRecentlyLoggedOut() || PlayerLogout())
return;
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
@@ -535,7 +535,9 @@ void WorldSession::SendCancelTrade()
void WorldSession::HandleCancelTradeOpcode(WorldPacket& /*recvPacket*/)
{
_player->TradeCancel(true);
// sended also after LOGOUT COMPLETE
if (_player) // needed because STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT
_player->TradeCancel(true);
}
void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket)

View File

@@ -206,7 +206,7 @@ void OpcodeTable::Initialize()
/*0x04B*/ DEFINE_HANDLER(CMSG_LOGOUT_REQUEST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLogoutRequestOpcode );
/*0x04C*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_LOGOUT_RESPONSE, STATUS_NEVER);
/*0x04D*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_LOGOUT_COMPLETE, STATUS_NEVER);
/*0x04E*/ DEFINE_HANDLER(CMSG_LOGOUT_CANCEL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLogoutCancelOpcode );
/*0x04E*/ DEFINE_HANDLER(CMSG_LOGOUT_CANCEL, STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT, PROCESS_THREADUNSAFE, &WorldSession::HandleLogoutCancelOpcode);
/*0x04F*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_LOGOUT_CANCEL_ACK, STATUS_NEVER);
/*0x050*/ DEFINE_HANDLER(CMSG_NAME_QUERY, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleNameQueryOpcode );
/*0x051*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_NAME_QUERY_RESPONSE, STATUS_NEVER);
@@ -412,7 +412,7 @@ void OpcodeTable::Initialize()
/*0x119*/ DEFINE_HANDLER(CMSG_IGNORE_TRADE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleIgnoreTradeOpcode );
/*0x11A*/ DEFINE_HANDLER(CMSG_ACCEPT_TRADE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAcceptTradeOpcode );
/*0x11B*/ DEFINE_HANDLER(CMSG_UNACCEPT_TRADE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleUnacceptTradeOpcode );
/*0x11C*/ DEFINE_HANDLER(CMSG_CANCEL_TRADE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCancelTradeOpcode );
/*0x11C*/ DEFINE_HANDLER(CMSG_CANCEL_TRADE, STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT, PROCESS_THREADUNSAFE, &WorldSession::HandleCancelTradeOpcode);
/*0x11D*/ DEFINE_HANDLER(CMSG_SET_TRADE_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetTradeItemOpcode );
/*0x11E*/ DEFINE_HANDLER(CMSG_CLEAR_TRADE_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleClearTradeItemOpcode );
/*0x11F*/ DEFINE_HANDLER(CMSG_SET_TRADE_GOLD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetTradeGoldOpcode );

View File

@@ -1353,9 +1353,10 @@ typedef Opcodes OpcodeServer;
/// Player state
enum SessionStatus
{
STATUS_AUTHED = 0, // Player authenticated (_player == nullptr, m_GUID has garbage)
STATUS_AUTHED = 0, // Player authenticated (_player == nullptr, m_playerRecentlyLogout = false or will be reset before handler call, m_GUID have garbage)
STATUS_LOGGEDIN, // Player in game (_player != nullptr, m_GUID == _player->GetGUID(), inWorld())
STATUS_TRANSFER, // Player transferring to another map (_player != nullptr, m_GUID == _player->GetGUID(), !inWorld())
STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT, // _player != nullptr or _player == nullptr && m_playerRecentlyLogout && m_playerLogout, m_GUID store last _player guid)
STATUS_NEVER, // Opcode not accepted from client (deprecated or server side only)
STATUS_UNHANDLED, // Opcode not handled yet
};

View File

@@ -121,6 +121,7 @@ WorldSession::WorldSession(uint32 id, std::string&& name, std::shared_ptr<WorldS
m_inQueue(false),
m_playerLoading(false),
m_playerLogout(false),
m_playerRecentlyLogout(false),
m_playerSave(false),
m_sessionDbcLocale(sWorld->GetDefaultDbcLocale()),
m_sessionDbLocaleIndex(locale),
@@ -326,28 +327,48 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
case STATUS_LOGGEDIN:
if (!_player)
{
// pussywizard: such packets were sent to do something for a character that has already logged out, skip them
}
else if (!_player->IsInWorld())
{
// pussywizard: such packets may do something important and the player is just being teleported, move to the end of the queue
// pussywizard: previously such were skipped, so leave it as it is xD proper code below if we wish to change that
// pussywizard: requeue only important packets not related to maps (PROCESS_THREADUNSAFE)
/*if (opHandle.packetProcessing == PROCESS_THREADUNSAFE)
// skip STATUS_LOGGEDIN opcode unexpected errors if player logout sometime ago - this can be network lag delayed packets
//! If player didn't log out a while ago, it means packets are being sent while the server does not recognize
//! the client to be in world yet. We will re-add the packets to the bottom of the queue and process them later.
if (!m_playerRecentlyLogout)
{
if (!firstDelayedPacket)
firstDelayedPacket = packet;
requeuePackets.push_back(packet);
deletePacket = false;
QueuePacket(packet);
}*/
}
else if (_player->IsInWorld() && AntiDOS.EvaluateOpcode(*packet, currentTime))
{
if (!sScriptMgr->CanPacketReceive(this, *packet))
{
break;
LOG_DEBUG("network", "Re-enqueueing packet with opcode %s with with status STATUS_LOGGEDIN. "
"Player {} is currently not in world yet.", GetOpcodeNameForLogging(static_cast<OpcodeClient>(packet->GetOpcode())), GetPlayerInfo());
}
}
else if (_player->IsInWorld())
{
if (AntiDOS.EvaluateOpcode(*packet, currentTime))
{
if (!sScriptMgr->CanPacketReceive(this, *packet))
{
break;
}
opHandle->Call(this, *packet);
LogUnprocessedTail(packet);
}
else
processedPackets = MAX_PROCESSED_PACKETS_IN_SAME_WORLDSESSION_UPDATE; // break out of packet processing loop
}
// lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer
break;
case STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT:
if (!_player && !m_playerRecentlyLogout) // There's a short delay between _player = null and m_playerRecentlyLogout = true during logout
{
LogUnexpectedOpcode(packet, "STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT",
"the player has not logged in yet and not recently logout");
}
else if (AntiDOS.EvaluateOpcode(*packet, currentTime))
{
// not expected _player or must checked in packet hanlder
if (!sScriptMgr->CanPacketReceive(this, *packet))
break;
opHandle->Call(this, *packet);
LogUnprocessedTail(packet);
@@ -373,6 +394,11 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
if (m_inQueue) // prevent cheating
break;
// some auth opcodes can be recieved before STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT opcodes
// however when we recieve CMSG_CHAR_ENUM we are surely no longer during the logout process.
if (packet->GetOpcode() == CMSG_CHAR_ENUM)
m_playerRecentlyLogout = false;
if (AntiDOS.EvaluateOpcode(*packet, currentTime))
{
if (!sScriptMgr->CanPacketReceive(this, *packet))
@@ -714,6 +740,7 @@ void WorldSession::LogoutPlayer(bool save)
m_playerLogout = false;
m_playerSave = false;
m_playerRecentlyLogout = true;
SetLogoutStartTime(0);
}
@@ -1261,6 +1288,8 @@ void WorldSession::SendAddonsInfo()
void WorldSession::SetPlayer(Player* player)
{
_player = player;
// set m_GUID that can be used while player loggined and later until m_playerRecentlyLogout not reset
if (_player)
m_GUIDLow = _player->GetGUID().GetCounter();
}

View File

@@ -334,6 +334,7 @@ public:
bool PlayerLoading() const { return m_playerLoading; }
bool PlayerLogout() const { return m_playerLogout; }
bool PlayerRecentlyLoggedOut() const { return m_playerRecentlyLogout; }
bool PlayerLogoutWithSave() const { return m_playerLogout && m_playerSave; }
void ReadAddonsInfo(ByteBuffer& data);
@@ -1139,7 +1140,7 @@ private:
// characters who failed on Player::BuildEnumData shouldn't login
GuidSet _legitCharacters;
ObjectGuid::LowType m_GUIDLow;
ObjectGuid::LowType m_GUIDLow; // set logined or recently logout player (while m_playerRecentlyLogout set)
Player* _player;
std::shared_ptr<WorldSocket> m_Socket;
std::string m_Address;
@@ -1160,6 +1161,7 @@ private:
bool m_inQueue; // session wait in auth.queue
bool m_playerLoading; // code processed in LoginPlayer
bool m_playerLogout; // code processed in LogoutPlayer
bool m_playerRecentlyLogout;
bool m_playerSave;
LocaleConstant m_sessionDbcLocale;
LocaleConstant m_sessionDbLocaleIndex;