int WorldSocket::SendPacket(const WorldPacket& pct) { ACE_GUARD_RETURN(LockType, Guard, m_OutBufferLock, -1); if (closing_) return -1; // Dump outgoing packet. sLog.outWorldPacketDump(uint32(get_handle()), pct.GetOpcode(), pct.GetOpcodeName(), &pct, false); if (iSendPacket(pct) == -1) { WorldPacket* npct; ACE_NEW_RETURN(npct, WorldPacket(pct), -1); // NOTE maybe check of the size of the queue can be good ? // to make it bounded instead of unbounded if (m_PacketQueue.enqueue_tail(npct) == -1) { delete npct; sLog.outError("WorldSocket::SendPacket: m_PacketQueue.enqueue_tail failed"); return -1; } } return 0; }
int WorldSocket::SendPacket(const WorldPacket& pkt) { std::lock_guard<std::mutex> guard(m_OutBufferLock); if (closing_) return -1; WorldPacket pct = pkt; // Dump outgoing packet. sLog.outWorldPacketDump(uint32(get_handle()), pct.GetOpcode(), pct.GetOpcodeName(), &pct, false); if (!sEluna->OnPacketSend(m_Session, pct)) return 0; if (iSendPacket(pct) == -1) { WorldPacket* npct; ACE_NEW_RETURN(npct, WorldPacket(pct), -1); // NOTE maybe check of the size of the queue can be good ? // to make it bounded instead of unbounded if (m_PacketQueue.enqueue_tail(npct) == -1) { delete npct; sLog.outError("WorldSocket::SendPacket: m_PacketQueue.enqueue_tail failed"); return -1; } } return 0; }
void WorldSession::Start() { Packet = WorldPacket((uint16)MSG_NULL); boost::asio::async_read(Socket, boost::asio::buffer(Packet.GetDataWithHeader(), WorldPacket::HEADER_SIZE), boost::bind(&WorldSession::HandleHeader, shared_from_this(), boost::asio::placeholders::error)); }
int WorldSocket::SendPacket(WorldPacket const& pct) { ACE_GUARD_RETURN (LockType, Guard, m_OutBufferLock, -1); if (closing_) return -1; // Dump outgoing packet. if (sPacketLog->CanLogPacket()) sPacketLog->LogPacket(pct, SERVER_TO_CLIENT); // Create a copy of the original packet; this is to avoid issues if a hook modifies it. sScriptMgr->OnPacketSend(this, WorldPacket(pct)); Flexi::ServerPktHeader header(pct.size()+2, pct.GetOpcode()); m_Crypt.EncryptSend ((uint8*)header.header, header.getHeaderLength()); if (m_OutBuffer->space() >= pct.size() + header.getHeaderLength() && msg_queue()->is_empty()) { // Put the packet on the buffer. if (m_OutBuffer->copy((char*) header.header, header.getHeaderLength()) == -1) ACE_ASSERT (false); if (!pct.empty()) if (m_OutBuffer->copy((char*) pct.contents(), pct.size()) == -1) ACE_ASSERT (false); } else { // Enqueue the packet. ACE_Message_Block* mb; ACE_NEW_RETURN(mb, ACE_Message_Block(pct.size() + header.getHeaderLength()), -1); mb->copy((char*) header.header, header.getHeaderLength()); if (!pct.empty()) mb->copy((const char*)pct.contents(), pct.size()); if (msg_queue()->enqueue_tail(mb, (ACE_Time_Value*)&ACE_Time_Value::zero) == -1) { sLog->outError("WorldSocket::SendPacket enqueue_tail failed"); mb->release(); return -1; } } return 0; }
int WorldSocket::SendPacket (const WorldPacket& pct) { ACE_GUARD_RETURN (LockType, Guard, m_OutBufferLock, -1); if (closing_) return -1; // Dump outgoing packet. if (sWorldLog.LogWorld ()) { sWorldLog.Log ("SERVER:\nSOCKET: %u\nLENGTH: %u\nOPCODE: %s (0x%.4X)\nDATA:\n", (uint32) get_handle (), pct.size (), LookupOpcodeName (pct.GetOpcode ()), pct.GetOpcode ()); uint32 p = 0; while (p < pct.size ()) { for (uint32 j = 0; j < 16 && p < pct.size (); j++) sWorldLog.Log ("%.2X ", const_cast<WorldPacket&>(pct)[p++]); sWorldLog.Log ("\n"); } sWorldLog.Log ("\n\n"); } if (iSendPacket (pct) == -1) { WorldPacket* npct; ACE_NEW_RETURN (npct, WorldPacket (pct), -1); // NOTE maybe check of the size of the queue can be good ? // to make it bounded instead of unbounded if (m_PacketQueue.enqueue_tail (npct) == -1) { delete npct; sLog.outError ("WorldSocket::SendPacket: m_PacketQueue.enqueue_tail failed"); return -1; } } return 0; }
int WorldSocket::handle_input_header (void) { ACE_ASSERT(m_RecvWPct == NULL); if (m_Crypt.IsInitialized()) { uint8* clientHeader = (uint8*)m_WorldHeader.rd_ptr(); WorldClientPktHeader& header = *((WorldClientPktHeader*)clientHeader); m_Crypt.DecryptRecv(clientHeader, 4); uint32 value = *(uint32*)clientHeader; uint32 opcode = value & 0xFFF; uint16 size = (uint16)((value & ~(uint32)0xFFF) >> 12); header.size = size + 4; header.cmd = opcode; if ((header.size < 4) || (header.size > 10240) || (header.cmd >= 0xFFFF)) { sLog->outError(LOG_FILTER_GENERAL,"WorldSocket::handle_input_header: client sent malformed packet size = %d , cmd = %d", header.size, header.cmd); errno = EINVAL; return -1; } header.size -= 4; ACE_NEW_RETURN(m_RecvWPct, WorldPacket (PacketFilter::DropHighBytes(Opcodes(header.cmd)), header.size), -1); if (header.size > 0) { m_RecvWPct->resize(header.size); m_RecvPct.base ((char*) m_RecvWPct->contents(), m_RecvWPct->size()); } else { ACE_ASSERT(m_RecvPct.space() == 0); } }
int WorldSocket::handle_input_header (void) { ACE_ASSERT (m_RecvWPct == NULL); ACE_ASSERT (m_Header.length() == sizeof(Flexi::ClientPktHeader)); m_Crypt.DecryptRecv ((uint8*) m_Header.rd_ptr(), sizeof(Flexi::ClientPktHeader)); Flexi::ClientPktHeader& header = *((Flexi::ClientPktHeader*) m_Header.rd_ptr()); EndianConvertReverse(header.size); EndianConvert(header.cmd); if (header.size < 2) { Player* _player = m_Session ? m_Session->GetPlayer() : NULL; sLog->outError("WorldSocket::handle_input_header(): client (account: %u, char [GUID: %u, name: %s]) sent malformed packet (size: %d, cmd: %d)", m_Session ? m_Session->GetAccountId() : 0, _player ? _player->GetGUIDLow() : 0, _player ? _player->GetName() : "<none>", header.size, header.cmd); errno = EINVAL; return -1; } header.size -= 2; ACE_NEW_RETURN (m_RecvWPct, WorldPacket ((uint16) header.cmd, header.size), -1); if (header.size > 0) { m_RecvWPct->resize (header.size); m_RecvPct.base ((char*) m_RecvWPct->contents(), m_RecvWPct->size()); } else { ACE_ASSERT(m_RecvPct.space() == 0); } return 0; }
int WorldSocket::handle_input_header (void) { ACE_ASSERT(m_RecvWPct == NULL); if (m_Crypt.IsInitialized()) { ACE_ASSERT(m_WorldHeader.length() == sizeof(WorldClientPktHeader)); uint8* uintHeader = (uint8*)m_WorldHeader.rd_ptr(); m_Crypt.DecryptRecv(uintHeader, sizeof(WorldClientPktHeader)); WorldClientPktHeader& header = *(WorldClientPktHeader*)uintHeader; uint32 value = *(uint32*)uintHeader; header.cmd = value & 0x1FFF; header.size = ((value & ~(uint32)0x1FFF) >> 13); if (header.size > 10236) { Player* _player = m_Session ? m_Session->GetPlayer() : NULL; TC_LOG_ERROR("network", "WorldSocket::handle_input_header(): client (account: %u, char [GUID: %u, name: %s]) sent malformed packet (size: %d, cmd: %d)", m_Session ? m_Session->GetAccountId() : 0, _player ? _player->GetGUIDLow() : 0, _player ? _player->GetName().c_str() : "<none>", header.size, header.cmd); errno = EINVAL; return -1; } uint16 opcodeNumber = PacketFilter::DropHighBytes(header.cmd); ACE_NEW_RETURN(m_RecvWPct, WorldPacket(clientOpcodeTable.GetOpcodeByNumber(opcodeNumber), header.size), -1); m_RecvWPct->SetReceivedOpcode(opcodeNumber); if (header.size > 0) { m_RecvWPct->resize(header.size); m_RecvPct.base ((char*) m_RecvWPct->contents(), m_RecvWPct->size()); } else ACE_ASSERT(m_RecvPct.space() == 0); }
int WorldSocket::handle_input_header (void) { ACE_ASSERT (m_RecvWPct == NULL); ACE_ASSERT (m_Header.length () == sizeof (ClientPktHeader)); m_Crypt.DecryptRecv ((ACE_UINT8*) m_Header.rd_ptr (), sizeof (ClientPktHeader)); ClientPktHeader& header = *((ClientPktHeader*) m_Header.rd_ptr ()); EndianConvertReverse(header.size); EndianConvert(header.cmd); if ((header.size < 4) || (header.size > 10240) || (header.cmd < 0) || (header.cmd > 10240) ) { sLog.outError ("WorldSocket::handle_input_header: client sent mailformed packet size = %d , cmd = %d", header.size, header.cmd); errno = EINVAL; return -1; } header.size -= 4; ACE_NEW_RETURN (m_RecvWPct, WorldPacket ((uint16) header.cmd, header.size), -1); if(header.size > 0) { m_RecvWPct->resize (header.size); m_RecvPct.base ((char*) m_RecvWPct->contents (), m_RecvWPct->size ()); } else { ACE_ASSERT(m_RecvPct.space() == 0); } return 0; }
int PoolSocket::handle_input_header (void) { ACE_ASSERT (m_RecvWPct == NULL); ACE_ASSERT (m_Header.length() == sizeof(Flexi::ClientPktHeader)); m_Crypt.DecryptRecv ((uint8*) m_Header.rd_ptr(), sizeof(Flexi::ClientPktHeader)); Flexi::ClientPktHeader& header = *((Flexi::ClientPktHeader*) m_Header.rd_ptr()); EndianConvertReverse(header.size); EndianConvert(header.cmd); if (header.size < 2) { sLog->outError ("PoolSocket::handle_input_header()"); errno = EINVAL; return -1; } header.size -= 2; ACE_NEW_RETURN (m_RecvWPct, WorldPacket ((uint16) header.cmd, header.size), -1); if (header.size > 0) { m_RecvWPct->resize (header.size); m_RecvPct.base ((char*) m_RecvWPct->contents(), m_RecvWPct->size()); } else { ACE_ASSERT(m_RecvPct.space() == 0); } return 0; }
/// Update the WorldSession (triggered by World update) bool WorldSession::Update(uint32 diff, PacketFilter& updater) { uint32 sessionDiff = getMSTime(); uint32 nbPacket = 0; std::map<uint32, OpcodeInfo> pktHandle; // opcodeId / OpcodeInfo /// Antispam Timer update if (sWorld->getBoolConfig(CONFIG_ANTISPAM_ENABLED)) UpdateAntispamTimer(diff); /// Update Timeout timer. UpdateTimeOutTime(diff); ///- Before we process anything: /// If necessary, kick the player from the character select screen if (IsConnectionIdle()) m_Socket->CloseSocket(); ///- Retrieve packets from the receive queue and call the appropriate handlers /// not process packets if socket already closed WorldPacket* packet = NULL; //! Delete packet after processing by default bool deletePacket = true; //! To prevent infinite loop WorldPacket* firstDelayedPacket = NULL; //! If _recvQueue.peek() == firstDelayedPacket it means that in this Update call, we've processed all //! *properly timed* packets, and we're now at the part of the queue where we find //! delayed packets that were re-enqueued due to improper timing. To prevent an infinite //! loop caused by re-enqueueing the same packets over and over again, we stop updating this session //! and continue updating others. The re-enqueued packets will be handled in the next Update call for this session. uint32 processedPackets = 0; while (m_Socket && !m_Socket->IsClosed() && !_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket && _recvQueue.next(packet, updater)) { const OpcodeHandler* opHandle = opcodeTable[packet->GetOpcode()]; uint32 pktTime = getMSTime(); try { 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 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) { //! Prevent infinite loop if (!firstDelayedPacket) firstDelayedPacket = packet; //! Because checking a bool is faster than reallocating memory deletePacket = false; QueuePacket(packet); //! Log sLog->outDebug(LOG_FILTER_NETWORKIO, "Re-enqueueing packet with opcode %s with with status STATUS_LOGGEDIN. " "Player is currently not in world yet.", GetOpcodeNameForLogging(packet->GetOpcode()).c_str()); } } else if (_player->IsInWorld()) { sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); #ifdef ELUNA if (!sEluna->OnPacketReceive(this, *packet)) break; #endif (this->*opHandle->handler)(*packet); if (sLog->ShouldLog(LOG_FILTER_NETWORKIO, LOG_LEVEL_TRACE) && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); } // 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 && !m_playerLogout) // 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 { // not expected _player or must checked in packet hanlder sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); #ifdef ELUNA if (!sEluna->OnPacketReceive(this, *packet)) break; #endif (this->*opHandle->handler)(*packet); if (sLog->ShouldLog(LOG_FILTER_NETWORKIO, LOG_LEVEL_TRACE) && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); } break; case STATUS_TRANSFER: if (!_player) LogUnexpectedOpcode(packet, "STATUS_TRANSFER", "the player has not logged in yet"); else if (_player->IsInWorld()) LogUnexpectedOpcode(packet, "STATUS_TRANSFER", "the player is still in world"); else { sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); #ifdef ELUNA if (!sEluna->OnPacketReceive(this, *packet)) break; #endif (this->*opHandle->handler)(*packet); if (sLog->ShouldLog(LOG_FILTER_NETWORKIO, LOG_LEVEL_TRACE) && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); } break; case STATUS_AUTHED: // prevent cheating with skip queue wait if (m_inQueue) { LogUnexpectedOpcode(packet, "STATUS_AUTHED", "the player not pass queue yet"); 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; sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); #ifdef ELUNA if (!sEluna->OnPacketReceive(this, *packet)) break; #endif (this->*opHandle->handler)(*packet); if (sLog->ShouldLog(LOG_FILTER_NETWORKIO, LOG_LEVEL_TRACE) && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); break; case STATUS_NEVER: sLog->outError(LOG_FILTER_OPCODES, "Received not allowed opcode %s from %s", GetOpcodeNameForLogging(packet->GetOpcode()).c_str() , GetPlayerName(false).c_str()); break; case STATUS_UNHANDLED: sLog->outError(LOG_FILTER_OPCODES, "Received not handled opcode %s from %s", GetOpcodeNameForLogging(packet->GetOpcode()).c_str() , GetPlayerName(false).c_str()); break; } } catch(ByteBufferException &) { sLog->outError(LOG_FILTER_NETWORKIO, "WorldSession::Update ByteBufferException occured while parsing a packet (opcode: %u) from client %s, accountid=%i. Skipped packet.", packet->GetOpcode(), GetRemoteAddress().c_str(), GetAccountId()); packet->hexlike(); } nbPacket++; std::map<uint32, OpcodeInfo>::iterator itr = pktHandle.find(packet->GetOpcode()); if (itr == pktHandle.end()) pktHandle.insert(std::make_pair(packet->GetOpcode(), OpcodeInfo(1, getMSTime() - pktTime))); else { OpcodeInfo& data = (*itr).second; data.nbPkt += 1; data.totalTime += getMSTime() - pktTime; } if (deletePacket) delete packet; #define MAX_PROCESSED_PACKETS_IN_SAME_WORLDSESSION_UPDATE 500 processedPackets++; //process only a max amout of packets in 1 Update() call. //Any leftover will be processed in next update if (processedPackets > MAX_PROCESSED_PACKETS_IN_SAME_WORLDSESSION_UPDATE) break; } if (m_Socket && !m_Socket->IsClosed() && _warden) _warden->Update(); ProcessQueryCallbacks(); //check if we are safe to proceed with logout //logout procedure should happen only in World::UpdateSessions() method!!! if (updater.ProcessLogout()) { time_t currTime = time(NULL); ///- If necessary, log the player out if (ShouldLogOut(currTime) && !m_playerLoading) LogoutPlayer(true); if (m_Socket && GetPlayer() && _warden) _warden->Update(); ///- Cleanup socket pointer if need if (m_Socket && m_Socket->IsClosed()) { m_Socket->RemoveReference(); m_Socket = NULL; } if (!m_Socket) return false; //Will remove this session from the world session map } sessionDiff = getMSTime() - sessionDiff; if (sessionDiff > 50) { sLog->outInfo(LOG_FILTER_PLAYER, "Session of account [%u] take more than 50 ms to execute (%u ms)", GetAccountId(), sessionDiff); for (auto itr : pktHandle) sLog->outInfo(LOG_FILTER_PLAYER, "Session of account -----> %u %s (%u ms)", itr.second.nbPkt, GetOpcodeNameForLogging((Opcodes)itr.first).c_str(), itr.second.totalTime); } return true; }
/// Update the WorldSession (triggered by World update) bool WorldSession::Update(uint32 diff, PacketFilter& updater) { /// Update Timeout timer. UpdateTimeOutTime(diff); ///- Before we process anything: /// If necessary, kick the player from the character select screen if (IsConnectionIdle()) m_Socket->CloseSocket(); ///- Retrieve packets from the receive queue and call the appropriate handlers /// not process packets if socket already closed WorldPacket* packet = NULL; //! Delete packet after processing by default bool deletePacket = true; //! To prevent infinite loop WorldPacket* firstDelayedPacket = NULL; //! If _recvQueue.peek() == firstDelayedPacket it means that in this Update call, we've processed all //! *properly timed* packets, and we're now at the part of the queue where we find //! delayed packets that were re-enqueued due to improper timing. To prevent an infinite //! loop caused by re-enqueueing the same packets over and over again, we stop updating this session //! and continue updating others. The re-enqueued packets will be handled in the next Update call for this session. uint32 processedPackets = 0; while (m_Socket && !m_Socket->IsClosed() && !_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket && _recvQueue.next(packet, updater)) { if (!AntiDOS.EvaluateOpcode(*packet)) KickPlayer(); OpcodeHandler const* opHandle = clientOpcodeTable[packet->GetOpcode()]; try { 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 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) { //! Prevent infinite loop if (!firstDelayedPacket) firstDelayedPacket = packet; //! Because checking a bool is faster than reallocating memory deletePacket = false; QueuePacket(packet); //! Log TC_LOG_DEBUG("network", "Re-enqueueing packet with opcode %s with with status STATUS_LOGGEDIN. " "Player is currently not in world yet.", GetOpcodeNameForLogging(packet->GetOpcode(), false).c_str()); } } else if (_player->IsInWorld()) { sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle->Handler)(*packet); LogUnprocessedTail(packet); } // 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 && !m_playerLogout) // 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 { // not expected _player or must checked in packet hanlder sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle->Handler)(*packet); LogUnprocessedTail(packet); } break; case STATUS_TRANSFER: if (!_player) LogUnexpectedOpcode(packet, "STATUS_TRANSFER", "the player has not logged in yet"); else if (_player->IsInWorld()) LogUnexpectedOpcode(packet, "STATUS_TRANSFER", "the player is still in world"); else { sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle->Handler)(*packet); LogUnprocessedTail(packet); } break; case STATUS_AUTHED: // prevent cheating with skip queue wait if (m_inQueue) { LogUnexpectedOpcode(packet, "STATUS_AUTHED", "the player not pass queue yet"); 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; sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle->Handler)(*packet); LogUnprocessedTail(packet); break; case STATUS_NEVER: TC_LOG_ERROR("network.opcode", "Received not allowed opcode %s from %s", GetOpcodeNameForLogging(packet->GetOpcode(), false).c_str() , GetPlayerInfo().c_str()); break; case STATUS_UNHANDLED: TC_LOG_ERROR("network.opcode", "Received not handled opcode %s from %s", GetOpcodeNameForLogging(packet->GetOpcode(), false).c_str() , GetPlayerInfo().c_str()); break; } } catch (ByteBufferException const&) { TC_LOG_ERROR("network", "WorldSession::Update ByteBufferException occured while parsing a packet (opcode: %s) from client %s, accountid=%i. Skipped packet.", GetOpcodeNameForLogging(packet->GetOpcode(), false).c_str(), GetRemoteAddress().c_str(), GetAccountId()); packet->hexlike(); } if (deletePacket) delete packet; deletePacket = true; #define MAX_PROCESSED_PACKETS_IN_SAME_WORLDSESSION_UPDATE 100 processedPackets++; //process only a max amout of packets in 1 Update() call. //Any leftover will be processed in next update if (processedPackets > MAX_PROCESSED_PACKETS_IN_SAME_WORLDSESSION_UPDATE) break; } if (m_Socket && !m_Socket->IsClosed() && _warden) _warden->Update(); ProcessQueryCallbacks(); //check if we are safe to proceed with logout //logout procedure should happen only in World::UpdateSessions() method!!! if (updater.ProcessLogout()) { time_t currTime = time(NULL); ///- If necessary, log the player out if (ShouldLogOut(currTime) && !m_playerLoading) LogoutPlayer(true); if (m_Socket && GetPlayer() && _warden) _warden->Update(); ///- Cleanup socket pointer if need if (m_Socket && m_Socket->IsClosed()) { m_Socket->RemoveReference(); m_Socket = NULL; } if (!m_Socket) return false; //Will remove this session from the world session map } return true; }
/// Update the WorldSession (triggered by World update) bool WorldSession::Update(uint32 diff, PacketFilter& updater) { /// Update Timeout timer. UpdateTimeOutTime(diff); ///- Before we process anything: /// If necessary, kick the player from the character select screen if (IsConnectionIdle()) m_Socket->CloseSocket(); ///- Retrieve packets from the receive queue and call the appropriate handlers /// not process packets if socket already closed WorldPacket* packet = NULL; //! Delete packet after processing by default bool deletePacket = true; //! To prevent infinite loop WorldPacket* firstDelayedPacket = NULL; //! If _recvQueue.peek() == firstDelayedPacket it means that in this Update call, we've processed all //! *properly timed* packets, and we're now at the part of the queue where we find //! delayed packets that were re-enqueued due to improper timing. To prevent an infinite //! loop caused by re-enqueueing the same packets over and over again, we stop updating this session //! and continue updating others. The re-enqueued packets will be handled in the next Update call for this session. //uint32 processedPackets = 0; uint32 msTime = getMSTime(); uint32 currentMSTime = msTime; //bool output = false; std::list<std::pair<uint32, uint32> > processedOpcodes; while (m_Socket && !m_Socket->IsClosed() && !_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket && _recvQueue.next(packet, updater)) { sLog->outDebug(LOG_FILTER_NETWORKIO, "SESSION: IN opcode %s (0x%.4X)", LookupOpcodeName(packet->GetOpcode()), packet->GetOpcode()); if (packet->GetOpcode() >= NUM_MSG_TYPES) { sLog->outError("SESSION: received non-existed opcode %s (0x%.4X)", LookupOpcodeName(packet->GetOpcode()), packet->GetOpcode()); sScriptMgr->OnUnknownPacketReceive(m_Socket, WorldPacket(*packet)); } else { OpcodeHandler &opHandle = opcodeTable[packet->GetOpcode()]; try { 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 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) { //! Prevent infinite loop if (!firstDelayedPacket) firstDelayedPacket = packet; //! Because checking a bool is faster than reallocating memory deletePacket = false; QueuePacket(packet); //! Log sLog->outDebug(LOG_FILTER_NETWORKIO, "Re-enqueueing packet with opcode %s (0x%.4X) with with status STATUS_LOGGEDIN. " "Player is currently not in world yet.", opHandle.name, packet->GetOpcode()); } } else if (_player->IsInWorld()) { sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle.handler)(*packet); if (sLog->IsOutDebug() && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); } // 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 && !m_playerLogout) // 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 { // not expected _player or must checked in packet handler sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle.handler)(*packet); if (sLog->IsOutDebug() && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); } break; case STATUS_TRANSFER: if (!_player) LogUnexpectedOpcode(packet, "STATUS_TRANSFER", "the player has not logged in yet"); else if (_player->IsInWorld()) LogUnexpectedOpcode(packet, "STATUS_TRANSFER", "the player is still in world"); else { sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle.handler)(*packet); if (sLog->IsOutDebug() && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); } break; case STATUS_AUTHED: // prevent cheating with skip queue wait if (m_inQueue) { LogUnexpectedOpcode(packet, "STATUS_AUTHED", "the player not pass queue yet"); 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; sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle.handler)(*packet); if (sLog->IsOutDebug() && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); break; case STATUS_NEVER: sLog->outDebug(LOG_FILTER_NETWORKIO, "SESSION (account: %u, guidlow: %u, char: %s): received not allowed opcode %s (0x%.4X)", GetAccountId(), m_GUIDLow, _player ? _player->GetName() : "<none>", LookupOpcodeName(packet->GetOpcode()), packet->GetOpcode()); break; case STATUS_UNHANDLED: sLog->outDebug(LOG_FILTER_NETWORKIO, "SESSION (account: %u, guidlow: %u, char: %s): received not handled opcode %s (0x%.4X)", GetAccountId(), m_GUIDLow, _player ? _player->GetName() : "<none>", LookupOpcodeName(packet->GetOpcode()), packet->GetOpcode()); break; } } catch (ByteBufferException &) { sLog->outError("WorldSession::Update ByteBufferException occured while parsing a packet (opcode: %u) from client %s, accountid=%i. Skipped packet.", packet->GetOpcode(), GetRemoteAddress().c_str(), GetAccountId()); if (sLog->IsOutDebug()) { sLog->outDebug(LOG_FILTER_NETWORKIO, "Dumping error causing packet:"); packet->hexlike(); } } } uint32 temporary = getMSTime(); processedOpcodes.push_back(std::pair<uint32, uint32>(packet->GetOpcode(), getMSTimeDiff(currentMSTime, temporary))); if (deletePacket) delete packet; deletePacket = true; /*#define MAX_PROCESSED_PACKETS_IN_SAME_WORLDSESSION_UPDATE 50 processedPackets++; //process only a max amout of packets in 1 Update() call. //Any leftover will be processed in next update if (processedPackets > MAX_PROCESSED_PACKETS_IN_SAME_WORLDSESSION_UPDATE) { output = true; break; } #define MAX_PROCESS_MSTIME_IN_SAME_WORLDSESSION_UPDATE 5 currentMSTime = getMSTime(); if (getUSTimeDiff(msTime, currentMSTime) > MAX_PROCESS_MSTIME_IN_SAME_WORLDSESSION_UPDATE) { output = true; break; }*/ } /*if (output) { std::stringstream ss; ss << "Session: " << GetAccountId() << " Player: " << GetPlayerName(); std::list<std::pair<uint32, uint32> >::iterator itr = processedOpcodes.begin(); for (; itr != processedOpcodes.end(); ++itr) ss << " " << itr->first << "-" << itr->second << "ms"; sLog->outPerformance(ss.str().c_str()); }*/ ProcessQueryCallbacks(); if (_newNode) { if (Player *player = GetPlayer()) { if (player->IsSaveCommited()) { WorldPacket data(NODE_MISC_DATA); data << uint32(CL_DEF_TRANSFER_TO_NODE); data << _newNode; SendPacket(&data); _newNode = 0; } } } //check if we are safe to proceed with logout //logout procedure should happen only in World::UpdateSessions() method!!! if (updater.ProcessLogout()) { time_t currTime = time(NULL); ///- If necessary, log the player out if (ShouldLogOut(currTime) && !m_playerLoading) LogoutPlayer(true); ///- Cleanup socket pointer if need if (m_Socket && m_Socket->IsClosed()) { expireTime -= expireTime > diff ? diff : expireTime; if (expireTime < diff || forceExit) { m_Socket->RemoveReference(); m_Socket = NULL; } } if (!m_Socket) return false; //Will remove this session from the world session map } return true; }
/// Update the WorldSession (triggered by World update) bool WorldSession::Update(uint32 diff, PacketFilter& updater) { /// Update Timeout timer. UpdateTimeOutTime(diff); ///- Before we process anything: /// If necessary, kick the player from the character select screen /*if (IsConnectionIdle()) m_Socket->CloseSocket();*/ ///- Retrieve packets from the receive queue and call the appropriate handlers /// not process packets if socket already closed WorldPacket *packet = NULL; while (m_Socket && !m_Socket->IsClosed() && _recvQueue.next(packet, updater)) { if (packet->GetOpcode() >= NUM_MSG_TYPES) { sLog->outError("SESSION: received non-existed opcode %s (0x%.4X)", LookupOpcodeName(packet->GetOpcode()), packet->GetOpcode()); sScriptMgr->OnUnknownPacketReceive(m_Socket, WorldPacket(*packet)); } else { OpcodeHandler &opHandle = opcodeTable[packet->GetOpcode()]; try { 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()) { sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle.handler)(*packet); if (sLog->IsOutDebug() && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); } // 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) LogUnexpectedOpcode(packet, "the player has not logged in yet and not recently logout"); else { // not expected _player or must checked in packet hanlder sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle.handler)(*packet); if (sLog->IsOutDebug() && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); // Playerbot mod: if this player has bots let the // botAI see the masters packet if(!m_playerBots.empty()) PlayerbotAI::HandleMasterIncomingPacket(*packet, *this); } break; case STATUS_TRANSFER: if (!_player) LogUnexpectedOpcode(packet, "the player has not logged in yet"); else if (_player->IsInWorld()) LogUnexpectedOpcode(packet, "the player is still in world"); else { sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle.handler)(*packet); if (sLog->IsOutDebug() && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); } break; case STATUS_AUTHED: // prevent cheating with skip queue wait if (m_inQueue) { LogUnexpectedOpcode(packet, "the player not pass queue yet"); break; } // single from authed time opcodes send in to after logout time // and before other STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT opcodes. if (packet->GetOpcode() != CMSG_SET_ACTIVE_VOICE_CHANNEL) m_playerRecentlyLogout = false; sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle.handler)(*packet); if (sLog->IsOutDebug() && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); break; case STATUS_NEVER: sLog->outError("SESSION: received not allowed opcode %s (0x%.4X)", LookupOpcodeName(packet->GetOpcode()), packet->GetOpcode()); break; case STATUS_UNHANDLED: sLog->outDebug("SESSION: received not handled opcode %s (0x%.4X)", LookupOpcodeName(packet->GetOpcode()), packet->GetOpcode()); break; } } catch(ByteBufferException &) { sLog->outError("WorldSession::Update ByteBufferException occured while parsing a packet (opcode: %u) from client %s, accountid=%i. Skipped packet.", packet->GetOpcode(), GetRemoteAddress().c_str(), GetAccountId()); if (sLog->IsOutDebug()) { sLog->outDebug("Dumping error causing packet:"); packet->hexlike(); } } } delete packet; } ProcessQueryCallbacks(); //check if we are safe to proceed with logout //logout procedure should happen only in World::UpdateSessions() method!!! if (updater.ProcessLogout()) { time_t currTime = time(NULL); ///- If necessary, log the player out if (ShouldLogOut(currTime) && !m_playerLoading) LogoutPlayer(true); //Playerbot mod - Process player bot packets //The PlayerbotAI class adds to the packet queue to simulate a real player //since Playerbots are known to the World obj only its master's //WorldSession object we need to process all master's bot's packets. for(PlayerBotMap::const_iterator itr = GetPlayerBotsBegin(); itr != GetPlayerBotsEnd(); ++itr) { Player *const botPlayer = itr->second; WorldSession *const pBotWorldSession = botPlayer->GetSession(); if(botPlayer->IsBeingTeleportedFar()) { pBotWorldSession->HandleMoveWorldportAckOpcode(); } else if(botPlayer->IsInWorld()) { WorldPacket *packet; while(pBotWorldSession->_recvQueue.next(packet)) { OpcodeHandler &opHandle = opcodeTable[packet->GetOpcode()]; (pBotWorldSession->*opHandle.handler)(*packet); delete packet; } } } ///- Cleanup socket pointer if need if (m_Socket && m_Socket->IsClosed()) { m_Socket->RemoveReference(); m_Socket = NULL; } if (!m_Socket) return false; //Will remove this session from the world session map } return true; }
bool WorldSession::UpdateClientIO(uint32 diff, PacketFilter& updater) { ///- Retrieve packets from the receive queue and call the appropriate handlers /// not process packets if socket already closed WorldPacket* packet = NULL; while (m_Socket && !m_Socket->IsClosed() && _recvQueue.next(packet, updater)) { const OpcodeHandler* opHandle = opcodeTable[packet->GetOpcode()]; // !=NULL checked in WorldSocket try { switch (opHandle->routing_node) { case ROUTE_NODE: SendPacketToNode(packet); break; case ROUTE_BOTH: SendPacketToNode(packet); case ROUTE_LOGON: { 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, "STATUS_LOGGEDIN", "the player has not logged in yet"); } else if (_player->IsInWorld()) { sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle->handler)(*packet); if (sLog->IsOutDebug() && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); } // 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) LogUnexpectedOpcode(packet, "STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT", "the player has not logged in yet and not recently logout"); else { // not expected _player or must checked in packet hanlder sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle->handler)(*packet); if (sLog->IsOutDebug() && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); } break; case STATUS_TRANSFER: if (!_player) LogUnexpectedOpcode(packet, "STATUS_TRANSFER", "the player has not logged in yet"); else if (_player->IsInWorld()) LogUnexpectedOpcode(packet, "STATUS_TRANSFER", "the player is still in world"); else { sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle->handler)(*packet); if (sLog->IsOutDebug() && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); } break; case STATUS_AUTHED: // prevent cheating with skip queue wait if (m_inQueue) { LogUnexpectedOpcode(packet, "STATUS_AUTHED", "the player not pass queue yet"); break; } // single from authed time opcodes send in to after logout time // and before other STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT opcodes. if (packet->GetOpcode() != CMSG_SET_ACTIVE_VOICE_CHANNEL) m_playerRecentlyLogout = false; sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle->handler)(*packet); if (sLog->IsOutDebug() && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); break; case STATUS_NEVER: sLog->outError("SESSION (account: %u, guidlow: %u, char: %s): received not allowed opcode %s (0x%.4X)", GetAccountId(), m_GUIDLow, _player ? _player->GetName() : "<none>", LookupOpcodeName(packet->GetOpcode()), packet->GetOpcode()); break; case STATUS_UNHANDLED: sLog->outDebug(LOG_FILTER_NETWORKIO, "SESSION (account: %u, guidlow: %u, char: %s): received not handled opcode %s (0x%.4X)", GetAccountId(), m_GUIDLow, _player ? _player->GetName() : "<none>", LookupOpcodeName(packet->GetOpcode()), packet->GetOpcode()); break; } } } } catch(ByteBufferException &) { sLog->outError("WorldSession::Update ByteBufferException occured while parsing a packet (opcode: %u) from client %s, accountid=%i. Skipped packet.", packet->GetOpcode(), GetRemoteAddress().c_str(), GetAccountId()); if (sLog->IsOutDebug()) { sLog->outDebug(LOG_FILTER_NETWORKIO, "Dumping error causing packet:"); packet->hexlike(); } } delete packet; } return true; }
/// Update the WorldSession (triggered by World update) bool WorldSession::Update(uint32 diff, PacketFilter& updater) { /// Update Timeout timer. UpdateTimeOutTime(diff); ///- Before we process anything: /// If necessary, kick the player from the character select screen if (IsConnectionIdle()) m_Socket->CloseSocket(); const uint32 opcodeMinTime = 50; uint32 opcodeStartTime; uint32 opcodeProcessTime; ///- Retrieve packets from the receive queue and call the appropriate handlers /// not process packets if socket already closed WorldPacket* packet = NULL; //! Delete packet after processing by default bool deletePacket = true; //! To prevent infinite loop WorldPacket* firstDelayedPacket = NULL; //! If _recvQueue.peek() == firstDelayedPacket it means that in this Update call, we've processed all //! *properly timed* packets, and we're now at the part of the queue where we find //! delayed packets that were re-enqueued due to improper timing. To prevent an infinite //! loop caused by re-enqueueing the same packets over and over again, we stop updating this session //! and continue updating others. The re-enqueued packets will be handled in the next Update call for this session. while (m_Socket && !m_Socket->IsClosed() && !_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket && _recvQueue.next(packet, updater)) { opcodeStartTime = getMSTime(); OpcodeHandler const* opHandle = opcodeTable[packet->GetOpcode()]; try { 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 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) { //! Prevent infinite loop if (!firstDelayedPacket) firstDelayedPacket = packet; //! Because checking a bool is faster than reallocating memory deletePacket = false; QueuePacket(packet); //! Log sLog->outDebug(LOG_FILTER_NETWORKIO, "Re-enqueueing packet with opcode %s with with status STATUS_LOGGEDIN. " "Player is currently not in world yet.", GetOpcodeNameForLogging(packet->GetOpcode()).c_str()); } } else if (_player->IsInWorld()) { sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle->Handler)(*packet); LogUnprocessedTail(packet); } // 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 && !m_playerLogout) // 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 { // not expected _player or must checked in packet hanlder sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle->Handler)(*packet); LogUnprocessedTail(packet); } break; case STATUS_TRANSFER: if (!_player) LogUnexpectedOpcode(packet, "STATUS_TRANSFER", "the player has not logged in yet"); else if (_player->IsInWorld()) LogUnexpectedOpcode(packet, "STATUS_TRANSFER", "the player is still in world"); else { sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle->Handler)(*packet); LogUnprocessedTail(packet); } break; case STATUS_AUTHED: // prevent cheating with skip queue wait if (m_inQueue) { LogUnexpectedOpcode(packet, "STATUS_AUTHED", "the player not pass queue yet"); 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; sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle->Handler)(*packet); LogUnprocessedTail(packet); break; case STATUS_NEVER: sLog->outError(LOG_FILTER_OPCODES, "Received not allowed opcode %s from %s", GetOpcodeNameForLogging(packet->GetOpcode()).c_str(), GetPlayerInfo().c_str()); break; case STATUS_UNHANDLED: sLog->outInfo(LOG_FILTER_BAD_OPCODE_HANDLER, "STATUS_UNHANDLED: %s (len: %u)", GetOpcodeNameForLogging(packet->GetOpcode()).c_str(), packet->size()); sLog->outError(LOG_FILTER_OPCODES, "Received not handled opcode %s from %s", GetOpcodeNameForLogging(packet->GetOpcode()).c_str(), GetPlayerInfo().c_str()); break; } } catch(ByteBufferException &) { sLog->outInfo(LOG_FILTER_BAD_OPCODE_HANDLER, "EXCEPTION: %s (len: %u)", GetOpcodeNameForLogging(packet->GetOpcode()).c_str(), packet->size()); sLog->outError(LOG_FILTER_NETWORKIO, "WorldSession::Update ByteBufferException occured while parsing a packet (opcode: %u) from client %s, accountid=%i. Skipped packet.", packet->GetOpcode(), GetRemoteAddress().c_str(), GetAccountId()); packet->hexlike(); } opcodeProcessTime = GetMSTimeDiffToNow(opcodeStartTime); if (opcodeProcessTime >= opcodeMinTime) { PreparedStatement *stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_SLOW_OPCODE); stmt->setUInt32(0, packet->GetOpcode()); stmt->setUInt32(1, opcodeProcessTime); WorldDatabase.Execute(stmt); } if (deletePacket) delete packet; } if (m_Socket && !m_Socket->IsClosed() && _warden) _warden->Update(); ProcessQueryCallbacks(); //check if we are safe to proceed with logout //logout procedure should happen only in World::UpdateSessions() method!!! if (updater.ProcessLogout()) { time_t currTime = time(NULL); ///- If necessary, log the player out if (ShouldLogOut(currTime) && !m_playerLoading) LogoutPlayer(true); if (m_Socket && GetPlayer() && _warden) _warden->Update(); ///- Cleanup socket pointer if need if (m_Socket && m_Socket->IsClosed()) { expireTime -= expireTime > diff ? diff : expireTime; if (expireTime < diff || forceExit) { m_Socket->RemoveReference(); m_Socket = NULL; } } if (!m_Socket) return false; //Will remove this session from the world session map } return true; }
/// Update the WorldSession (triggered by World update) bool WorldSession::Update (uint32 diff, PacketFilter& updater) { /// Update Timeout timer. UpdateTimeOutTime(diff); ///- Before we process anything: /// If necessary, kick the player from the character select screen if (IsConnectionIdle()) m_Socket->CloseSocket(); ///- Retrieve packets from the receive queue and call the appropriate handlers /// not process packets if socket already closed WorldPacket* packet; while (m_Socket && !m_Socket->IsClosed() && _recvQueue.next(packet, updater)) { /*#if 1 sLog->outError("MOEP: %s (0x%.4X)", LookupOpcodeName(packet->GetOpcode()), packet->GetOpcode()); #endif*/ sLog->outDebug(LOG_FILTER_NETWORKIO, "SESSION: Received opcode 0x%.4X (%s)", packet->GetOpcode(), packet->GetOpcode() > OPCODE_NOT_FOUND ? "nf" : LookupOpcodeName(packet->GetOpcode())); if (packet->GetOpcode() >= NUM_MSG_TYPES) { sLog->outError("SESSION: received non-existed opcode %s (0x%.4X)", LookupOpcodeName(packet->GetOpcode()), packet->GetOpcode()); sScriptMgr->OnUnknownPacketReceive(m_Socket, WorldPacket(*packet)); } else { OpcodeHandler& opHandle = opcodeTable[packet->GetOpcode()]; try { 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, "STATUS_LOGGEDIN", "the player has not logged in yet"); } else if (_player->IsInWorld()) { sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle.handler)(*packet); if (sLog->IsOutDebug() && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); } // 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) { LogUnexpectedOpcode(packet, "STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT", "the player has not logged in yet and not recently logout"); } else { // not expected _player or must checked in packet hanlder sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle.handler)(*packet); if (sLog->IsOutDebug() && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); } break; case STATUS_TRANSFER: if (!_player) LogUnexpectedOpcode(packet, "STATUS_TRANSFER", "the player has not logged in yet"); else if (_player->IsInWorld()) LogUnexpectedOpcode(packet, "STATUS_TRANSFER", "the player is still in world"); else { sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle.handler)(*packet); if (sLog->IsOutDebug() && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); } break; case STATUS_AUTHED: // prevent cheating with skip queue wait if (m_inQueue) { LogUnexpectedOpcode(packet, "STATUS_AUTHED", "the player not pass queue yet"); break; } // single from authed time opcodes send in to after logout time // and before other STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT opcodes. if (packet->GetOpcode() != CMSG_SET_ACTIVE_VOICE_CHANNEL) m_playerRecentlyLogout = false; sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle.handler)(*packet); if (sLog->IsOutDebug() && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); break; case STATUS_NEVER: if (strcmp(LookupOpcodeName(packet->GetOpcode()), "UNKNOWN") == 0) sLog->outDebug(LOG_FILTER_NETWORKIO, "received not found opcode 0x%.4X", packet->GetOpcode()); else sLog->outError("SESSION (account: %u, guidlow: %u, char: %s): received not allowed opcode %s (0x%.4X)", GetAccountId(), m_GUIDLow, _player ? _player->GetName() : "<none>", LookupOpcodeName(packet->GetOpcode()), packet->GetOpcode()); break; case STATUS_UNHANDLED: sLog->outDebug(LOG_FILTER_NETWORKIO, "SESSION (account: %u, guidlow: %u, char: %s): received not handled opcode %s (0x%.4X)", GetAccountId(), m_GUIDLow, _player ? _player->GetName() : "<none>", LookupOpcodeName(packet->GetOpcode()), packet->GetOpcode()); break; } } catch (ByteBufferException &) { sLog->outError("WorldSession::Update ByteBufferException occured while parsing a packet (opcode: %u) from client %s, accountid=%i. Skipped packet.", packet->GetOpcode(), GetRemoteAddress().c_str(), GetAccountId()); if (sLog->IsOutDebug()) { sLog->outDebug(LOG_FILTER_NETWORKIO, "Dumping error causing packet:"); packet->hexlike(); } } } delete packet; } ProcessQueryCallbacks(); //check if we are safe to proceed with logout //logout procedure should happen only in World::UpdateSessions() method!!! if (updater.ProcessLogout()) { time_t currTime = time(NULL); ///- If necessary, log the player out if (ShouldLogOut(currTime) && !m_playerLoading) LogoutPlayer(true); ///- Cleanup socket pointer if need if (m_Socket && m_Socket->IsClosed()) { m_Socket->RemoveReference(); m_Socket = NULL; } if (!m_Socket) return false; //Will remove this session from the world session map } return true; }
int WorldSocket::ProcessIncoming(WorldPacket* new_pct) { ACE_ASSERT (new_pct); // manage memory ;) ACE_Auto_Ptr<WorldPacket> aptr (new_pct); const ACE_UINT16 opcode = new_pct->GetOpcode(); if (closing_) return -1; // Dump received packet. if (sPacketLog->CanLogPacket()) sPacketLog->LogPacket(*new_pct, CLIENT_TO_SERVER); try { switch (opcode) { case CMSG_PING: return HandlePing (*new_pct); case CMSG_AUTH_SESSION: if (m_Session) { sLog->outError("WorldSocket::ProcessIncoming: Player send CMSG_AUTH_SESSION again"); return -1; } sScriptMgr->OnPacketReceive(this, WorldPacket(*new_pct)); return HandleAuthSession(*new_pct); case CMSG_KEEP_ALIVE: sLog->outStaticDebug ("CMSG_KEEP_ALIVE, size: " UI64FMTD, uint64(new_pct->size())); sScriptMgr->OnPacketReceive(this, WorldPacket(*new_pct)); return 0; default: { ACE_GUARD_RETURN (LockType, Guard, m_SessionLock, -1); if (m_Session != NULL) { // Our Idle timer will reset on any non PING opcodes. // Catches people idling on the login screen and any lingering ingame connections. m_Session->ResetTimeOutTime(); // OK, give the packet to WorldSession aptr.release(); // WARNINIG here we call it with locks held. // Its possible to cause deadlock if QueuePacket calls back m_Session->QueuePacket (new_pct); return 0; } else { sLog->outError("WorldSocket::ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); return -1; } } } } catch (ByteBufferException &) { sLog->outError("WorldSocket::ProcessIncoming ByteBufferException occured while parsing an instant handled packet (opcode: %u) from client %s, accountid=%i. Disconnected client.", opcode, GetRemoteAddress().c_str(), m_Session?m_Session->GetAccountId():-1); if (sLog->IsOutDebug()) { sLog->outDebug(LOG_FILTER_NETWORKIO, "Dumping error causing packet:"); new_pct->hexlike(); } return -1; } ACE_NOTREACHED (return 0); }
/// Update the WorldSession (triggered by World update) bool WorldSession::Update(uint32 diff, PacketFilter& updater) { /// Update Timeout timer. UpdateTimeOutTime(diff); ///- Before we process anything: /// If necessary, kick the player from the character select screen if (IsConnectionIdle()) m_Socket->CloseSocket(); ///- Retrieve packets from the receive queue and call the appropriate handlers /// not process packets if socket already closed WorldPacket* packet = NULL; //! Delete packet after processing by default bool deletePacket = true; //! To prevent infinite loop WorldPacket* firstDelayedPacket = NULL; //! If _recvQueue.peek() == firstDelayedPacket it means that in this Update call, we've processed all //! *properly timed* packets, and we're now at the part of the queue where we find //! delayed packets that were re-enqueued due to improper timing. To prevent an infinite //! loop caused by re-enqueueing the same packets over and over again, we stop updating this session //! and continue updating others. The re-enqueued packets will be handled in the next Update call for this session. while (m_Socket && !m_Socket->IsClosed() && !_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket && _recvQueue.next(packet, updater)) { if (packet->GetOpcode() >= NUM_MSG_TYPES) { sLog->outError("SESSION: received non-existed opcode %s (0x%.4X)", LookupOpcodeName(packet->GetOpcode()), packet->GetOpcode()); sScriptMgr->OnUnknownPacketReceive(m_Socket, WorldPacket(*packet)); } else { OpcodeHandler &opHandle = opcodeTable[packet->GetOpcode()]; try { 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 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) { //! Prevent infinite loop if (!firstDelayedPacket) firstDelayedPacket = packet; //! Because checking a bool is faster than reallocating memory deletePacket = false; QueuePacket(packet); //! Log sLog->outDebug(LOG_FILTER_NETWORKIO, "Re-enqueueing packet with opcode %s (0x%.4X) with with status STATUS_LOGGEDIN. " "Player is currently not in world yet.", opHandle.name, packet->GetOpcode()); } } else if (_player->IsInWorld()) { sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle.handler)(*packet); if (sLog->IsOutDebug() && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); } // 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) LogUnexpectedOpcode(packet, "STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT", "the player has not logged in yet and not recently logout"); else { // not expected _player or must checked in packet handler sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle.handler)(*packet); if (sLog->IsOutDebug() && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); } break; case STATUS_TRANSFER: if (!_player) LogUnexpectedOpcode(packet, "STATUS_TRANSFER", "the player has not logged in yet"); else if (_player->IsInWorld()) LogUnexpectedOpcode(packet, "STATUS_TRANSFER", "the player is still in world"); else { sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle.handler)(*packet); if (sLog->IsOutDebug() && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); } break; case STATUS_AUTHED: // prevent cheating with skip queue wait if (m_inQueue) { LogUnexpectedOpcode(packet, "STATUS_AUTHED", "the player not pass queue yet"); break; } // single from authed time opcodes send in to after logout time // and before other STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT opcodes. if (packet->GetOpcode() != CMSG_SET_ACTIVE_VOICE_CHANNEL) m_playerRecentlyLogout = false; sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle.handler)(*packet); if (sLog->IsOutDebug() && packet->rpos() < packet->wpos()) LogUnprocessedTail(packet); break; case STATUS_NEVER: sLog->outError("SESSION (account: %u, guidlow: %u, char: %s): received not allowed opcode %s (0x%.4X)", GetAccountId(), m_GUIDLow, _player ? _player->GetName() : "<none>", LookupOpcodeName(packet->GetOpcode()), packet->GetOpcode()); break; case STATUS_UNHANDLED: sLog->outDebug(LOG_FILTER_NETWORKIO, "SESSION (account: %u, guidlow: %u, char: %s): received not handled opcode %s (0x%.4X)", GetAccountId(), m_GUIDLow, _player ? _player->GetName() : "<none>", LookupOpcodeName(packet->GetOpcode()), packet->GetOpcode()); break; } } catch(ByteBufferException &) { sLog->outError("WorldSession::Update ByteBufferException occured while parsing a packet (opcode: %u) from client %s, accountid=%i. Skipped packet.", packet->GetOpcode(), GetRemoteAddress().c_str(), GetAccountId()); if (sLog->IsOutDebug()) { sLog->outDebug(LOG_FILTER_NETWORKIO, "Dumping error causing packet:"); packet->hexlike(); } } } if (deletePacket) delete packet; } if (m_Socket && !m_Socket->IsClosed() && _warden) _warden->Update(); ProcessQueryCallbacks(); //check if we are safe to proceed with logout //logout procedure should happen only in World::UpdateSessions() method!!! if (updater.ProcessLogout()) { time_t currTime = time(NULL); ///- If necessary, log the player out if (ShouldLogOut(currTime) && !m_playerLoading) LogoutPlayer(true); if (m_Socket && GetPlayer() && _warden) _warden->Update(); ///- Cleanup socket pointer if need if (m_Socket && m_Socket->IsClosed()) { m_Socket->RemoveReference(); m_Socket = NULL; } if (!m_Socket) return false; //Will remove this session from the world session map } return true; }
/// Update the WorldSession (triggered by World update) bool WorldSession::Update(uint32 diff, PacketFilter& updater) { /// Update Timeout timer. UpdateTimeOutTime(diff); ///- Before we process anything: /// If necessary, kick the player from the character select screen //Playerbot /* if (IsConnectionIdle()) m_Socket->CloseSocket(); */ ///- Retrieve packets from the receive queue and call the appropriate handlers /// not process packets if socket already closed WorldPacket* packet = NULL; //! Delete packet after processing by default bool deletePacket = true; //! To prevent infinite loop WorldPacket* firstDelayedPacket = NULL; //! If _recvQueue.peek() == firstDelayedPacket it means that in this Update call, we've processed all //! *properly timed* packets, and we're now at the part of the queue where we find //! delayed packets that were re-enqueued due to improper timing. To prevent an infinite //! loop caused by re-enqueueing the same packets over and over again, we stop updating this session //! and continue updating others. The re-enqueued packets will be handled in the next Update call for this session. while (m_Socket && !m_Socket->IsClosed() && !_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket && _recvQueue.next(packet, updater)) { if (packet->GetOpcode() >= NUM_MSG_TYPES) { sLog->outError(LOG_FILTER_OPCODES, "Received non-existed opcode %s from %s", GetOpcodeNameForLogging(packet->GetOpcode()).c_str() , GetPlayerInfo().c_str()); sScriptMgr->OnUnknownPacketReceive(m_Socket, WorldPacket(*packet)); } else { OpcodeHandler &opHandle = opcodeTable[packet->GetOpcode()]; try { 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 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) { //! Prevent infinite loop if (!firstDelayedPacket) firstDelayedPacket = packet; //! Because checking a bool is faster than reallocating memory deletePacket = false; QueuePacket(packet); //! Log sLog->outDebug(LOG_FILTER_NETWORKIO, "Re-enqueueing packet with opcode %s with with status STATUS_LOGGEDIN. " "Player is currently not in world yet.", GetOpcodeNameForLogging(packet->GetOpcode()).c_str()); } } else if (_player->IsInWorld()) { sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle.handler)(*packet); LogUnprocessedTail(packet); } // lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer //Playerbot mod if (_player && _player->GetPlayerbotMgr()) _player->GetPlayerbotMgr()->HandleMasterIncomingPacket(*packet); //End Playerbot mod break; case STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT: 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", "the player has not logged in yet and not recently logout"); else { // not expected _player or must checked in packet handler sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle.handler)(*packet); LogUnprocessedTail(packet); } break; case STATUS_TRANSFER: if (!_player) LogUnexpectedOpcode(packet, "STATUS_TRANSFER", "the player has not logged in yet"); else if (_player->IsInWorld()) LogUnexpectedOpcode(packet, "STATUS_TRANSFER", "the player is still in world"); else { sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle.handler)(*packet); LogUnprocessedTail(packet); } break; case STATUS_AUTHED: // prevent cheating with skip queue wait if (m_inQueue) { LogUnexpectedOpcode(packet, "STATUS_AUTHED", "the player not pass queue yet"); 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; sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (this->*opHandle.handler)(*packet); LogUnprocessedTail(packet); break; case STATUS_NEVER: sLog->outError(LOG_FILTER_OPCODES, "Received not allowed opcode %s from %s", GetOpcodeNameForLogging(packet->GetOpcode()).c_str() , GetPlayerInfo().c_str()); break; case STATUS_UNHANDLED: sLog->outDebug(LOG_FILTER_OPCODES, "Received not handled opcode %s from %s", GetOpcodeNameForLogging(packet->GetOpcode()).c_str() , GetPlayerInfo().c_str()); break; } } catch(ByteBufferException &) { sLog->outError(LOG_FILTER_GENERAL, "WorldSession::Update ByteBufferException occured while parsing a packet (opcode: %u) from client %s, accountid=%i. Skipped packet.", packet->GetOpcode(), GetRemoteAddress().c_str(), GetAccountId()); packet->hexlike(); } } if (deletePacket) delete packet; } if (m_Socket && !m_Socket->IsClosed() && _warden) _warden->Update(); ProcessQueryCallbacks(); // Playerbot mod - Process player bot packets // The PlayerbotAI class adds to the packet queue to simulate a real player // since Playerbots are known to the World obj only by its master's WorldSession object // we need to process all master's bot's packets. if (GetPlayer() && GetPlayer()->GetPlayerbotMgr()) { for (PlayerBotMap::const_iterator itr = GetPlayer()->GetPlayerbotMgr()->GetPlayerBotsBegin(); itr != GetPlayer()->GetPlayerbotMgr()->GetPlayerBotsEnd(); ++itr) { Player* const botPlayer = itr->second; if (!botPlayer) continue; WorldSession* const pBotWorldSession = botPlayer->GetSession(); if (!pBotWorldSession) continue; if (botPlayer->IsBeingTeleported()) botPlayer->GetPlayerbotAI()->HandleTeleportAck(); else if (botPlayer->IsInWorld()) { WorldPacket* packet; while (!_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket && pBotWorldSession->_recvQueue.next(packet, updater)) { OpcodeHandler& opHandle = opcodeTable[packet->GetOpcode()]; //sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet)); (pBotWorldSession->*opHandle.handler)(*packet); delete packet; } } } } //check if we are safe to proceed with logout //logout procedure should happen only in World::UpdateSessions() method!!! if (updater.ProcessLogout()) { time_t currTime = time(NULL); ///- If necessary, log the player out if (ShouldLogOut(currTime) && !m_playerLoading) LogoutPlayer(true); if (m_Socket && GetPlayer() && _warden) _warden->Update(); ///- Cleanup socket pointer if need if (m_Socket && m_Socket->IsClosed()) { m_Socket->RemoveReference(); m_Socket = NULL; } if (!m_Socket) return false; //Will remove this session from the world session map } return true; }