diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 190e8d773f..9417b71409 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -108,12 +108,13 @@ bool WorldSessionFilter::Process(WorldPacket* packet) } /// WorldSession constructor -WorldSession::WorldSession(uint32 id, std::string&& name, std::shared_ptr sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter) : +WorldSession::WorldSession(uint32 id, std::string&& name, std::shared_ptr sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, WorldSession::WorldSessionMode sessionMode) : m_muteTime(mute_time), m_timeOutTime(0), AntiDOS(this), m_GUIDLow(0), _player(nullptr), + _sessionMode(sessionMode), _security(sec), _accountId(id), _accountName(std::move(name)), @@ -147,12 +148,18 @@ WorldSession::WorldSession(uint32 id, std::string&& name, std::shared_ptrGetRemoteIpAddress().to_string(); ResetTimeOutTime(false); LoginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = %u;", GetAccountId()); // One-time query } + else if (IsClientless()) + { + // Clientless sessions deliberately have no remote endpoint. + // Keep a stable diagnostic value for logs that include GetRemoteAddress(). + m_Address = ""; + } m_Socket[CONNECTION_TYPE_REALM] = sock; _instanceConnectKey.Raw = UI64LIT(0); @@ -185,7 +192,8 @@ WorldSession::~WorldSession() while (_recvQueue.next(packet)) delete packet; - LoginDatabase.PExecute("UPDATE account SET online = 0 WHERE id = %u;", GetAccountId()); // One-time query + if (IsNetworkSession()) + LoginDatabase.PExecute("UPDATE account SET online = 0 WHERE id = %u;", GetAccountId()); // One-time query } bool WorldSession::PlayerDisconnected() const @@ -223,6 +231,12 @@ ObjectGuid::LowType WorldSession::GetGUIDLow() const /// Send a packet to the client void WorldSession::SendPacket(WorldPacket const* packet, bool forced /*= false*/) { + // A clientless session intentionally has no packet transport. Outbound + // packets are a normal side effect of Player/Core code and are discarded + // without producing "non existent socket" errors. + if (IsClientless()) + return; + if (packet->GetOpcode() == NULL_OPCODE) { TC_LOG_ERROR("network.opcode", "Prevented sending of NULL_OPCODE to %s", GetPlayerInfo().c_str()); @@ -340,13 +354,16 @@ void WorldSession::LogUnprocessedTail(WorldPacket const* packet) /// Update the WorldSession (triggered by World update) bool WorldSession::Update(uint32 diff, PacketFilter& updater) { - /// Update Timeout timer. - UpdateTimeOutTime(diff); + if (IsNetworkSession()) + { + /// Update Timeout timer. + UpdateTimeOutTime(diff); - ///- Before we process anything: - /// If necessary, kick the player from the character select screen - if (IsConnectionIdle() && !HasPermission(rbac::RBAC_PERM_IGNORE_IDLE_CONNECTION)) - m_Socket[CONNECTION_TYPE_REALM]->CloseSocket(); + ///- Before we process anything: + /// If necessary, kick the player from the character select screen + if (IsConnectionIdle() && !HasPermission(rbac::RBAC_PERM_IGNORE_IDLE_CONNECTION) && m_Socket[CONNECTION_TYPE_REALM]) + m_Socket[CONNECTION_TYPE_REALM]->CloseSocket(); + } ///- Retrieve packets from the receive queue and call the appropriate handlers /// not process packets if socket already closed @@ -478,7 +495,7 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) if (m_Socket[0] && m_Socket[0]->IsOpen() && _warden) _warden->Update(); - if (!updater.ProcessUnsafe()) // <=> updater is of type MapSessionFilter + if (IsNetworkSession() && !updater.ProcessUnsafe()) // <=> updater is of type MapSessionFilter { // Send time sync packet every 5s. if (_timeSyncTimer > 0) @@ -523,8 +540,8 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) } } - if (!m_Socket[CONNECTION_TYPE_REALM]) - return false; //Will remove this session from the world session map + if (IsNetworkSession() && !m_Socket[CONNECTION_TYPE_REALM]) + return false; // Will remove this network session from the world session map } return true; diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 60bff9c95f..afb36c2c28 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -475,7 +475,14 @@ struct PacketCounter class TC_GAME_API WorldSession { public: - WorldSession(uint32 id, std::string&& name, std::shared_ptr sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter); + enum class WorldSessionMode : uint8 + { + Network, + Clientless + }; + + WorldSession(uint32 id, std::string&& name, std::shared_ptr sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, + WorldSessionMode sessionMode = WorldSessionMode::Network); ~WorldSession(); bool PlayerLoading() const { return !m_playerLoading.IsEmpty(); } @@ -513,6 +520,10 @@ class TC_GAME_API WorldSession AccountTypes GetSecurity() const { return _security; } uint32 GetAccountId() const { return _accountId; } + WorldSessionMode GetSessionMode() const { return _sessionMode; } + bool IsNetworkSession() const { return _sessionMode == WorldSessionMode::Network; } + bool IsClientless() const { return _sessionMode == WorldSessionMode::Clientless; } + bool HasNetworkTransport() const { return m_Socket[CONNECTION_TYPE_REALM] != nullptr; } Player* GetPlayer() const { return _player; } std::string const& GetPlayerName() const; std::string GetPlayerInfo() const; @@ -1353,6 +1364,7 @@ class TC_GAME_API WorldSession ObjectGuid::LowType m_GUIDLow; // set logined or recently logout player (while m_playerRecentlyLogout set) Player* _player; + WorldSessionMode _sessionMode; std::shared_ptr m_Socket[2]; std::string m_Address; // Current Remote Address // std::string m_LAddress; // Last Attempted Remote Adress - we can not set attempted ip for a non-existing session!