From 44892168c1af5ec388062d498994e7f93e49fb82 Mon Sep 17 00:00:00 2001 From: Ovahlord Date: Sat, 8 Mar 2025 22:37:23 +0100 Subject: [PATCH] Core/Networking: ported missing socket related changes from master branch --- src/common/Threading/MPSCQueue.h | 2 +- src/server/game/Server/WorldPacket.h | 1 + src/server/game/Server/WorldSession.cpp | 11 +- src/server/game/Server/WorldSession.h | 2 +- src/server/game/Server/WorldSocket.cpp | 365 +++++++++++-------- src/server/game/Server/WorldSocket.h | 5 +- src/server/game/Server/WorldSocketMgr.cpp | 4 +- src/server/game/World/World.cpp | 7 +- src/server/game/World/World.h | 1 + src/server/shared/Networking/SocketMgr.h | 3 + src/server/worldserver/worldserver.conf.dist | 11 +- 11 files changed, 238 insertions(+), 174 deletions(-) diff --git a/src/common/Threading/MPSCQueue.h b/src/common/Threading/MPSCQueue.h index a364bac09d..f5c903da22 100644 --- a/src/common/Threading/MPSCQueue.h +++ b/src/common/Threading/MPSCQueue.h @@ -99,7 +99,7 @@ class MPSCQueueIntrusive public: MPSCQueueIntrusive() : _dummyPtr(reinterpret_cast(_dummy.data())), _head(_dummyPtr), _tail(_dummyPtr) { - // _dummy is constructed from raw byte array and is intentionally left uninitialized (it might not be default constructible) + // _dummy is constructed from aligned_storage and is intentionally left uninitialized (it might not be default constructible) // so we init only its IntrusiveLink here Atomic* dummyNext = new (&(_dummyPtr->*IntrusiveLink)) Atomic(); dummyNext->store(nullptr, std::memory_order_relaxed); diff --git a/src/server/game/Server/WorldPacket.h b/src/server/game/Server/WorldPacket.h index 4615e95be6..761b803905 100644 --- a/src/server/game/Server/WorldPacket.h +++ b/src/server/game/Server/WorldPacket.h @@ -93,6 +93,7 @@ class WorldPacket : public ByteBuffer ConnectionType GetConnection() const { return _connection; } std::chrono::steady_clock::time_point GetReceivedTime() const { return m_receivedTime; } + void SetReceiveTime(std::chrono::steady_clock::time_point receivedTime) { m_receivedTime = receivedTime; } protected: uint16 m_opcode; diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 9b9a1592ff..ce20d2249f 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -150,7 +150,7 @@ WorldSession::WorldSession(uint32 id, std::string&& name, uint32 battlenetAccoun if (sock) { m_Address = sock->GetRemoteIpAddress().to_string(); - ResetTimeOutTime(); + ResetTimeOutTime(false); LoginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = %u;", GetAccountId()); // One-time query } @@ -753,9 +753,12 @@ char const* WorldSession::GetTrinityString(uint32 entry) const return sObjectMgr->GetTrinityString(entry, GetSessionDbLocaleIndex()); } -void WorldSession::ResetTimeOutTime() +void WorldSession::ResetTimeOutTime(bool onlyActive) { - m_timeOutTime = int32(sWorld->getIntConfig(CONFIG_SOCKET_TIMEOUTTIME)); + if (GetPlayer()) + m_timeOutTime = GameTime::GetGameTime() + time_t(sWorld->getIntConfig(CONFIG_SOCKET_TIMEOUTTIME_ACTIVE)); + else if (!onlyActive) + m_timeOutTime = GameTime::GetGameTime() + time_t(sWorld->getIntConfig(CONFIG_SOCKET_TIMEOUTTIME)); } void WorldSession::Handle_NULL(WorldPacket& null) @@ -1247,7 +1250,7 @@ void WorldSession::InitializeSessionCallback(CharacterDatabaseQueryHolder const& SendAuthWaitQue(0); SetInQueue(false); - ResetTimeOutTime(); + ResetTimeOutTime(false); SendAddonsInfo(); SendClientCacheVersion(sWorld->getIntConfig(CONFIG_CLIENTCACHE_VERSION)); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index c9efd5c196..ea100648d8 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -641,7 +641,7 @@ class TC_GAME_API WorldSession m_timeOutTime -= int32(diff); } - void ResetTimeOutTime(); + void ResetTimeOutTime(bool onlyActive); bool IsConnectionIdle() const { diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index 574c815c4e..a12a3829e5 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -43,8 +43,7 @@ std::string const WorldSocket::ClientConnectionInitialize("WORLD OF WARCRAFT CON WorldSocket::WorldSocket(tcp::socket&& socket) : Socket(std::move(socket)), _type(CONNECTION_TYPE_REALM), _authSeed(rand32()), _OverSpeedPings(0), _worldSession(nullptr), - _authed(false), _compressionStream(nullptr), _sendBufferSize(4096), - _initialized(false) + _authed(false), _compressionStream(nullptr), _sendBufferSize(4096) { _headerBuffer.Resize(2); } @@ -89,7 +88,7 @@ void WorldSocket::CheckIpCallback(PreparedQueryResult result) } } - AsyncRead(); + AsyncReadWithCallback(&WorldSocket::InitializeHandler); MessageBuffer initializer; ServerPktHeader header(ServerConnectionInitialize.size(), 0); @@ -100,6 +99,94 @@ void WorldSocket::CheckIpCallback(PreparedQueryResult result) QueuePacket(std::move(initializer)); } +void WorldSocket::InitializeHandler(boost::system::error_code error, std::size_t transferedBytes) +{ + if (error) + { + CloseSocket(); + return; + } + + GetReadBuffer().WriteCompleted(transferedBytes); + + if (!IsOpen()) + return; + + MessageBuffer& packet = GetReadBuffer(); + if (packet.GetActiveSize() > 0) + { + if (_headerBuffer.GetRemainingSpace() > 0) + { + // need to receive the header + std::size_t readHeaderSize = std::min(packet.GetActiveSize(), _headerBuffer.GetRemainingSpace()); + _headerBuffer.Write(packet.GetReadPointer(), readHeaderSize); + packet.ReadCompleted(readHeaderSize); + + if (_headerBuffer.GetRemainingSpace() > 0) + { + // Couldn't receive the whole header this time. + ASSERT(packet.GetActiveSize() == 0); + AsyncReadWithCallback(&WorldSocket::InitializeHandler); + return; + } + + // We just received nice new header + if (!ReadHeaderHandler(false)) + { + CloseSocket(); + return; + } + } + + // We have full read header, now check the data payload + if (_packetBuffer.GetRemainingSpace() > 0) + { + // need more data in the payload + std::size_t readDataSize = std::min(packet.GetActiveSize(), _packetBuffer.GetRemainingSpace()); + _packetBuffer.Write(packet.GetReadPointer(), readDataSize); + packet.ReadCompleted(readDataSize); + + if (_packetBuffer.GetRemainingSpace() > 0) + { + // Couldn't receive the whole data this time. + ASSERT(packet.GetActiveSize() == 0); + AsyncReadWithCallback(&WorldSocket::InitializeHandler); + return; + } + } + + std::string initializer(reinterpret_cast(_packetBuffer.GetReadPointer()), std::min(_packetBuffer.GetActiveSize(), ClientConnectionInitialize.length())); + if (initializer != ClientConnectionInitialize) + { + CloseSocket(); + return; + } + + _compressionStream = new z_stream(); + _compressionStream->zalloc = (alloc_func)nullptr; + _compressionStream->zfree = (free_func)nullptr; + _compressionStream->opaque = (voidpf)nullptr; + _compressionStream->avail_in = 0; + _compressionStream->next_in = nullptr; + int32 z_res = deflateInit(_compressionStream, sWorld->getIntConfig(CONFIG_COMPRESSION)); + if (z_res != Z_OK) + { + TC_LOG_ERROR("network", "Can't initialize packet compression (zlib: deflateInit) Error code: %i (%s)", z_res, zError(z_res)); + CloseSocket(); + return; + } + + _headerBuffer.Resize(sizeof(ClientPktHeader)); + _headerBuffer.Reset(); + _packetBuffer.Reset(); + HandleSendAuthSession(); + AsyncRead(); + return; + } + + AsyncReadWithCallback(&WorldSocket::InitializeHandler); +} + bool WorldSocket::Update() { EncryptablePacket* queued; @@ -194,7 +281,7 @@ void WorldSocket::ReadHandler() } // We just received nice new header - if (!ReadHeaderHandler()) + if (!ReadHeaderHandler(true)) { CloseSocket(); return; @@ -239,26 +326,26 @@ void WorldSocket::SetWorldSession(WorldSession* session) _authed = true; } -bool WorldSocket::ReadHeaderHandler() +bool WorldSocket::ReadHeaderHandler(bool initialized) { - ASSERT(_headerBuffer.GetActiveSize() == (_initialized ? sizeof(ClientPktHeader) : 2)); + ASSERT(_headerBuffer.GetActiveSize() == (initialized ? sizeof(ClientPktHeader) : 2)); _authCrypt.DecryptRecv(_headerBuffer.GetReadPointer(), _headerBuffer.GetActiveSize()); ClientPktHeader* header = reinterpret_cast(_headerBuffer.GetReadPointer()); EndianConvertReverse(header->size); - if (_initialized) + if (initialized) EndianConvert(header->cmd); - if (!header->IsValidSize() || (_initialized && !header->IsValidOpcode())) + if (!header->IsValidSize() || (initialized && !header->IsValidOpcode())) { TC_LOG_ERROR("network", "WorldSocket::ReadHeaderHandler(): client %s sent malformed packet (size: %hu, cmd: %u)", GetRemoteIpAddress().to_string().c_str(), header->size, header->cmd); return false; } - if (_initialized) + if (initialized) header->size -= sizeof(header->cmd); _packetBuffer.Resize(header->size); @@ -328,168 +415,132 @@ struct AccountInfo WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler() { - if (_initialized) + ClientPktHeader* header = reinterpret_cast(_headerBuffer.GetReadPointer()); + OpcodeClient opcode = static_cast(header->cmd); + + WorldPacket packet(opcode, std::move(_packetBuffer), GetConnectionType()); + + if (sPacketLog->CanLogPacket()) + sPacketLog->LogPacket(packet, CLIENT_TO_SERVER, GetRemoteIpAddress(), GetRemotePort()); + + std::unique_lock sessionGuard(_worldSessionLock, std::defer_lock); + + switch (opcode) { - ClientPktHeader* header = reinterpret_cast(_headerBuffer.GetReadPointer()); - OpcodeClient opcode = static_cast(header->cmd); - - - WorldPacket packet(opcode, std::move(_packetBuffer), GetConnectionType()); - WorldPacket* packetToQueue; - - if (sPacketLog->CanLogPacket()) - sPacketLog->LogPacket(packet, CLIENT_TO_SERVER, GetRemoteIpAddress(), GetRemotePort()); - - std::unique_lock sessionGuard(_worldSessionLock, std::defer_lock); - - switch (opcode) + case CMSG_PING: { - case CMSG_PING: + LogOpcodeText(opcode, sessionGuard); + WorldPackets::Auth::Ping ping(std::move(packet)); + if (!ping.ReadNoThrow()) { - LogOpcodeText(opcode, sessionGuard); - WorldPackets::Auth::Ping ping(std::move(packet)); - if (!ping.ReadNoThrow()) - { - TC_LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client %s sent malformed CMSG_PING", GetRemoteIpAddress().to_string().c_str()); - return ReadDataHandlerResult::Error; - } - if (!HandlePing(ping)) - return ReadDataHandlerResult::Error; - return ReadDataHandlerResult::Ok; + TC_LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client %s sent malformed CMSG_PING", GetRemoteIpAddress().to_string().c_str()); + return ReadDataHandlerResult::Error; } - case CMSG_AUTH_SESSION: + if (!HandlePing(ping)) + return ReadDataHandlerResult::Error; + return ReadDataHandlerResult::Ok; + } + case CMSG_AUTH_SESSION: + { + LogOpcodeText(opcode, sessionGuard); + if (_authed) { - LogOpcodeText(opcode, sessionGuard); - if (_authed) - { - // locking just to safely log offending user is probably overkill but we are disconnecting him anyway - if (sessionGuard.try_lock()) - TC_LOG_ERROR("network", "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_SESSION from %s", _worldSession->GetPlayerInfo().c_str()); - return ReadDataHandlerResult::Error; - } + // locking just to safely log offending user is probably overkill but we are disconnecting him anyway + if (sessionGuard.try_lock()) + TC_LOG_ERROR("network", "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_SESSION from %s", _worldSession->GetPlayerInfo().c_str()); + return ReadDataHandlerResult::Error; + } - std::shared_ptr authSession = std::make_shared(std::move(packet)); - if (!authSession->ReadNoThrow()) - { - TC_LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client %s sent malformed CMSG_AUTH_SESSION", GetRemoteIpAddress().to_string().c_str()); - return ReadDataHandlerResult::Error; - } - HandleAuthSession(authSession); - return ReadDataHandlerResult::WaitingForQuery; - } - case CMSG_AUTH_CONTINUED_SESSION: + std::shared_ptr authSession = std::make_shared(std::move(packet)); + if (!authSession->ReadNoThrow()) { - LogOpcodeText(opcode, sessionGuard); - if (_authed) - { - // locking just to safely log offending user is probably overkill but we are disconnecting him anyway - if (sessionGuard.try_lock()) - TC_LOG_ERROR("network", "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_CONTINUED_SESSION from %s", _worldSession->GetPlayerInfo().c_str()); - return ReadDataHandlerResult::Error; - } - - std::shared_ptr authSession = std::make_shared(std::move(packet)); - if (!authSession->ReadNoThrow()) - { - TC_LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client %s sent malformed CMSG_AUTH_CONTINUED_SESSION", GetRemoteIpAddress().to_string().c_str()); - return ReadDataHandlerResult::Error; - } - HandleAuthContinuedSession(authSession); - return ReadDataHandlerResult::WaitingForQuery; + TC_LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client %s sent malformed CMSG_AUTH_SESSION", GetRemoteIpAddress().to_string().c_str()); + return ReadDataHandlerResult::Error; } - case CMSG_LOG_DISCONNECT: - LogOpcodeText(opcode, sessionGuard); - packet.rfinish(); // contains uint32 disconnectReason; - return ReadDataHandlerResult::Ok; - case CMSG_ENABLE_NAGLE: - LogOpcodeText(opcode, sessionGuard); - if (_worldSession) - _worldSession->HandleEnableNagleAlgorithm(); - return ReadDataHandlerResult::Ok; - case CMSG_KEEP_ALIVE: // todo: handle this packet in the same way of CMSG_TIME_SYNC_RESP - LogOpcodeText(opcode, sessionGuard); - return ReadDataHandlerResult::Ok; - case CMSG_CONNECT_TO_FAILED: + HandleAuthSession(authSession); + return ReadDataHandlerResult::WaitingForQuery; + } + case CMSG_AUTH_CONTINUED_SESSION: + { + LogOpcodeText(opcode, sessionGuard); + if (_authed) { - sessionGuard.lock(); + // locking just to safely log offending user is probably overkill but we are disconnecting him anyway + if (sessionGuard.try_lock()) + TC_LOG_ERROR("network", "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_CONTINUED_SESSION from %s", _worldSession->GetPlayerInfo().c_str()); + return ReadDataHandlerResult::Error; + } - LogOpcodeText(opcode, sessionGuard); - WorldPackets::Auth::ConnectToFailed connectToFailed(std::move(packet)); - if (!connectToFailed.ReadNoThrow()) - { - TC_LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client %s sent malformed CMSG_CONNECT_TO_FAILED", GetRemoteIpAddress().to_string().c_str()); - return ReadDataHandlerResult::Error; - } - HandleConnectToFailed(connectToFailed); + std::shared_ptr authSession = std::make_shared(std::move(packet)); + if (!authSession->ReadNoThrow()) + { + TC_LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client %s sent malformed CMSG_AUTH_CONTINUED_SESSION", GetRemoteIpAddress().to_string().c_str()); + return ReadDataHandlerResult::Error; + } + HandleAuthContinuedSession(authSession); + return ReadDataHandlerResult::WaitingForQuery; + } + case CMSG_KEEP_ALIVE: + sessionGuard.lock(); + LogOpcodeText(opcode, sessionGuard); + if (_worldSession) + { + _worldSession->ResetTimeOutTime(true); return ReadDataHandlerResult::Ok; } - case CMSG_TIME_SYNC_RESP: - packetToQueue = new WorldPacket(std::move(packet), std::chrono::steady_clock::now()); - break; - default: - packetToQueue = new WorldPacket(std::move(packet)); + TC_LOG_ERROR("network", "WorldSocket::ReadDataHandler: client {} sent CMSG_KEEP_ALIVE without being authenticated", GetRemoteIpAddress().to_string()); + return ReadDataHandlerResult::Error; + case CMSG_LOG_DISCONNECT: + LogOpcodeText(opcode, sessionGuard); + TC_LOG_DEBUG("network", "WorldSocket::ReadDataHandler: client {} sent CMSG_LOG_DISCONNECT reason {}", GetRemoteIpAddress().to_string(), packet.read()); + break; + case CMSG_ENABLE_NAGLE: + LogOpcodeText(opcode, sessionGuard); + SetNoDelay(false); + break; + case CMSG_CONNECT_TO_FAILED: + { + sessionGuard.lock(); + + LogOpcodeText(opcode, sessionGuard); + WorldPackets::Auth::ConnectToFailed connectToFailed(std::move(packet)); + if (!connectToFailed.ReadNoThrow()) + { + TC_LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client {} sent malformed CMSG_CONNECT_TO_FAILED", GetRemoteIpAddress().to_string()); + return ReadDataHandlerResult::Error; + } + HandleConnectToFailed(connectToFailed); + break; + } + default: + { + if (opcode == CMSG_TIME_SYNC_RESP) + packet.SetReceiveTime(std::chrono::steady_clock::now()); + + sessionGuard.lock(); + + LogOpcodeText(opcode, sessionGuard); + + if (!_worldSession) + { + TC_LOG_ERROR("network.opcode", "ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); + return ReadDataHandlerResult::Error; + } + + ClientOpcodeHandler const* handler = opcodeTable[opcode]; + if (!handler) + { + TC_LOG_ERROR("network.opcode", "No defined handler for opcode %s sent by %s", GetOpcodeNameForLogging(static_cast(packet.GetOpcode())).c_str(), _worldSession->GetPlayerInfo()); break; + } + + // Our Idle timer will reset on any non PING opcodes on login screen, allowing us to catch people idling. + _worldSession->ResetTimeOutTime(false); + + // Copy the packet to the heap before enqueuing + _worldSession->QueuePacket(new WorldPacket(std::move(packet))); + break; } - - sessionGuard.lock(); - - LogOpcodeText(opcode, sessionGuard); - - if (!_worldSession) - { - TC_LOG_ERROR("network.opcode", "ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); - delete packetToQueue; - return ReadDataHandlerResult::Error; - } - - // prevent invalid memory access/crash with custom opcodes - if (uint16(opcode) >= NUM_OPCODE_HANDLERS) - { - CloseSocket(); - return ReadDataHandlerResult::Error; - } - - OpcodeHandler const* handler = opcodeTable[opcode]; - if (!handler) - { - TC_LOG_ERROR("network.opcode", "No defined handler for opcode %s sent by %s", GetOpcodeNameForLogging(static_cast(packet.GetOpcode())).c_str(), _worldSession->GetPlayerInfo().c_str()); - return ReadDataHandlerResult::Error; - } - - // Our Idle timer will reset on any non PING opcodes. - // Catches people idling on the login screen and any lingering ingame connections. - _worldSession->ResetTimeOutTime(); - - // Copy the packet to the heap before enqueuing - _worldSession->QueuePacket(packetToQueue); - } - else - { - std::string initializer(reinterpret_cast(_packetBuffer.GetReadPointer()), std::min(_packetBuffer.GetActiveSize(), ClientConnectionInitialize.length())); - if (initializer != ClientConnectionInitialize) - { - CloseSocket(); - return ReadDataHandlerResult::Error; - } - - _compressionStream = new z_stream(); - _compressionStream->zalloc = (alloc_func)nullptr; - _compressionStream->zfree = (free_func)nullptr; - _compressionStream->opaque = (voidpf)nullptr; - _compressionStream->avail_in = 0; - _compressionStream->next_in = nullptr; - int32 z_res = deflateInit(_compressionStream, sWorld->getIntConfig(CONFIG_COMPRESSION)); - if (z_res != Z_OK) - { - TC_LOG_ERROR("network", "Can't initialize packet compression (zlib: deflateInit) Error code: %i (%s)", z_res, zError(z_res)); - CloseSocket(); - return ReadDataHandlerResult::Error; - } - - _initialized = true; - _headerBuffer.Resize(sizeof(ClientPktHeader)); - _packetBuffer.Reset(); - HandleSendAuthSession(); } return ReadDataHandlerResult::Ok; diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h index 1b97c0aad4..e04e15445e 100644 --- a/src/server/game/Server/WorldSocket.h +++ b/src/server/game/Server/WorldSocket.h @@ -105,7 +105,7 @@ public: protected: void OnClose() override; void ReadHandler() override; - bool ReadHeaderHandler(); + bool ReadHeaderHandler(bool initialized); enum class ReadDataHandlerResult { @@ -118,6 +118,7 @@ protected: private: void CheckIpCallback(PreparedQueryResult result); + void InitializeHandler(boost::system::error_code error, std::size_t transferedBytes); /// writes network.opcode log /// accessing WorldSession is not threadsafe, only do it when holding _worldSessionLock @@ -155,8 +156,6 @@ private: MPSCQueue _bufferQueue; std::size_t _sendBufferSize; - bool _initialized; - QueryCallbackProcessor _queryProcessor; std::string _ipCountry; }; diff --git a/src/server/game/Server/WorldSocketMgr.cpp b/src/server/game/Server/WorldSocketMgr.cpp index 0c2a029ea1..4f651893e3 100644 --- a/src/server/game/Server/WorldSocketMgr.cpp +++ b/src/server/game/Server/WorldSocketMgr.cpp @@ -94,13 +94,13 @@ bool WorldSocketMgr::StartWorldNetwork(Trinity::Asio::IoContext& ioContext, std: if (!instanceAcceptor->Bind()) { TC_LOG_ERROR("network", "StartNetwork failed to bind instance socket acceptor"); + delete instanceAcceptor; return false; } _instanceAcceptor = instanceAcceptor; - _acceptor->SetSocketFactory(std::bind(&BaseSocketMgr::GetSocketForAccept, this)); - _instanceAcceptor->SetSocketFactory(std::bind(&BaseSocketMgr::GetSocketForAccept, this)); + _instanceAcceptor->SetSocketFactory([this]() { return GetSocketForAccept(); }); _acceptor->AsyncAcceptWithCallback<&OnSocketAccept>(); _instanceAcceptor->AsyncAcceptWithCallback<&OnSocketAccept>(); diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index fc605d6e9d..5527781660 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -481,7 +481,7 @@ bool World::RemoveQueuedPlayer(WorldSession* sess) if (*iter == sess) { sess->SetInQueue(false); - sess->ResetTimeOutTime(); + sess->ResetTimeOutTime(false); iter = m_QueuedPlayer.erase(iter); found = true; // removing queued session break; @@ -798,7 +798,10 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_PORT_INSTANCE] = sConfigMgr->GetIntDefault("InstanceServerPort", 8086); } - m_int_configs[CONFIG_SOCKET_TIMEOUTTIME] = sConfigMgr->GetIntDefault("SocketTimeOutTime", 900000); + // Config values are in "milliseconds" but we handle SocketTimeOut only as "seconds" so divide by 1000 + m_int_configs[CONFIG_SOCKET_TIMEOUTTIME] = sConfigMgr->GetIntDefault("SocketTimeOutTime", 900000) / 1000; + m_int_configs[CONFIG_SOCKET_TIMEOUTTIME_ACTIVE] = sConfigMgr->GetIntDefault("SocketTimeOutTimeActive", 60000) / 1000; + m_int_configs[CONFIG_SESSION_ADD_DELAY] = sConfigMgr->GetIntDefault("SessionAddDelay", 10000); m_float_configs[CONFIG_GROUP_XP_DISTANCE] = sConfigMgr->GetFloatDefault("MaxGroupXPDistance", 74.0f); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index bb961944f4..63923cfc22 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -225,6 +225,7 @@ enum WorldIntConfigs : uint8 CONFIG_PORT_WORLD, CONFIG_PORT_INSTANCE, CONFIG_SOCKET_TIMEOUTTIME, + CONFIG_SOCKET_TIMEOUTTIME_ACTIVE, CONFIG_SESSION_ADD_DELAY, CONFIG_GAME_TYPE, CONFIG_REALM_ZONE, diff --git a/src/server/shared/Networking/SocketMgr.h b/src/server/shared/Networking/SocketMgr.h index f9b9b24580..b7b46ab01e 100644 --- a/src/server/shared/Networking/SocketMgr.h +++ b/src/server/shared/Networking/SocketMgr.h @@ -53,6 +53,7 @@ public: if (!acceptor->Bind()) { TC_LOG_ERROR("network", "StartNetwork failed to bind socket acceptor"); + delete acceptor; return false; } @@ -65,6 +66,8 @@ public: for (int32 i = 0; i < _threadCount; ++i) _threads[i].Start(); + _acceptor->SetSocketFactory([this]() { return GetSocketForAccept(); }); + return true; } diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index a129217137..835015db4e 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -322,10 +322,13 @@ InstanceMapLoadAllGrids = 0 SocketTimeOutTime = 900000 # -# SessionAddDelay -# Description: Time (in microseconds) that a network thread will sleep after authentication -# protocol handling before adding a connection to the world session map. -# Default: 10000 - (10 milliseconds, 0.01 second) +# SocketTimeOutTimeActive +# Description: Time (in milliseconds) after which an idle connection is dropped while +# logged into the world. +# The client sends keepalive packets every 30 seconds. Values <= 30s are not recommended. +# Default: 60000 - (1 minute) + +SocketTimeOutTimeActive = 60000 SessionAddDelay = 10000