feat(Core): replace ACE network with Boost.Asio (#6574)

This commit is contained in:
Kargatum
2021-07-16 15:43:56 +07:00
committed by GitHub
parent 7449496bb5
commit 8568c4fb33
64 changed files with 3242 additions and 4712 deletions

View File

@@ -31,7 +31,7 @@ bool WorldPackets::Strings::Utf8::Validate(std::string const& value)
//bool WorldPackets::Strings::Hyperlinks::Validate(std::string const& value)
//{
// if (!Warhead::Hyperlinks::CheckAllLinks(value))
// if (!Acore::Hyperlinks::CheckAllLinks(value))
// throw InvalidHyperlinkException(value);
//
// return true;

View File

@@ -1,17 +1,18 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2008-2021 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*/
#include "PacketLog.h"
#include "Config.h"
#include "IpAddress.h"
#include "Timer.h"
#include "WorldPacket.h"
#pragma pack(push, 1)
// Packet logging structures in PKT 3.1 format
// Packet logging structures in PKT 3.1 format
struct LogHeader
{
char Signature[3];
@@ -27,11 +28,19 @@ struct LogHeader
struct PacketHeader
{
char Direction[4];
// used to uniquely identify a connection
struct OptionalData
{
uint8 SocketIPBytes[16];
uint32 SocketPort;
};
uint32 Direction;
uint32 ConnectionId;
uint32 ArrivalTicks;
uint32 OptionalDataSize;
uint32 Length;
OptionalData OptionalData;
uint32 Opcode;
};
@@ -45,7 +54,9 @@ PacketLog::PacketLog() : _file(nullptr)
PacketLog::~PacketLog()
{
if (_file)
{
fclose(_file);
}
_file = nullptr;
}
@@ -60,9 +71,10 @@ void PacketLog::Initialize()
{
std::string logsDir = sConfigMgr->GetOption<std::string>("LogsDir", "");
if (!logsDir.empty())
if ((logsDir.at(logsDir.length() - 1) != '/') && (logsDir.at(logsDir.length() - 1) != '\\'))
logsDir.push_back('/');
if (!logsDir.empty() && (logsDir.at(logsDir.length() - 1) != '/') && (logsDir.at(logsDir.length() - 1) != '\\'))
{
logsDir.push_back('/');
}
std::string logname = sConfigMgr->GetOption<std::string>("PacketLogFile", "");
if (!logname.empty())
@@ -80,23 +92,42 @@ void PacketLog::Initialize()
header.SniffStartTicks = getMSTime();
header.OptionalDataSize = 0;
fwrite(&header, sizeof(header), 1, _file);
if (CanLogPacket())
{
fwrite(&header, sizeof(header), 1, _file);
}
}
}
void PacketLog::LogPacket(WorldPacket const& packet, Direction direction)
void PacketLog::LogPacket(WorldPacket const& packet, Direction direction, boost::asio::ip::address const& addr, uint16 port)
{
std::lock_guard<std::mutex> lock(_logPacketLock);
PacketHeader header;
*reinterpret_cast<uint32*>(header.Direction) = direction == CLIENT_TO_SERVER ? 0x47534d43 : 0x47534d53;
header.Direction = direction == CLIENT_TO_SERVER ? 0x47534d43 : 0x47534d53;
header.ConnectionId = 0;
header.ArrivalTicks = getMSTime();
header.OptionalDataSize = 0;
header.Length = packet.size() + 4;
header.OptionalDataSize = sizeof(header.OptionalData);
memset(header.OptionalData.SocketIPBytes, 0, sizeof(header.OptionalData.SocketIPBytes));
if (addr.is_v4())
{
auto bytes = addr.to_v4().to_bytes();
memcpy(header.OptionalData.SocketIPBytes, bytes.data(), bytes.size());
}
else if (addr.is_v6())
{
auto bytes = addr.to_v6().to_bytes();
memcpy(header.OptionalData.SocketIPBytes, bytes.data(), bytes.size());
}
header.OptionalData.SocketPort = port;
header.Length = packet.size() + sizeof(header.Opcode);
header.Opcode = packet.GetOpcode();
fwrite(&header, sizeof(header), 1, _file);
if (!packet.empty())
{
fwrite(packet.contents(), 1, packet.size(), _file);

View File

@@ -8,6 +8,7 @@
#define ACORE_PACKETLOG_H
#include "Common.h"
#include <boost/asio/ip/address.hpp>
#include <mutex>
enum Direction
@@ -18,7 +19,7 @@ enum Direction
class WorldPacket;
class PacketLog
class AC_GAME_API PacketLog
{
private:
PacketLog();
@@ -31,7 +32,7 @@ public:
void Initialize();
bool CanLogPacket() const { return (_file != nullptr); }
void LogPacket(WorldPacket const& packet, Direction direction);
void LogPacket(WorldPacket const& packet, Direction direction, boost::asio::ip::address const& addr, uint16 port);
private:
FILE* _file;

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef __SERVERPKTHDR_H__
#define __SERVERPKTHDR_H__
#include "Log.h"
#pragma pack(push, 1)
struct ServerPktHeader
{
/**
* size is the length of the payload _plus_ the length of the opcode
*/
ServerPktHeader(uint32 size, uint16 cmd) : size(size)
{
uint8 headerIndex=0;
if (isLargePacket())
{
LOG_DEBUG("network", "initializing large server to client packet. Size: %u, cmd: %u", size, cmd);
header[headerIndex++] = 0x80 | (0xFF & (size >> 16));
}
header[headerIndex++] = 0xFF &(size >> 8);
header[headerIndex++] = 0xFF & size;
header[headerIndex++] = 0xFF & cmd;
header[headerIndex++] = 0xFF & (cmd >> 8);
}
uint8 getHeaderLength()
{
// cmd = 2 bytes, size= 2||3bytes
return 2 + (isLargePacket() ? 3 : 2);
}
bool isLargePacket() const
{
return size > 0x7FFF;
}
const uint32 size;
uint8 header[5];
};
#pragma pack(pop)
#endif

View File

@@ -8,7 +8,7 @@
\ingroup u2w
*/
#include "WorldSocket.h" // must be first to make ACE happy with ACE includes in it
#include "WorldSession.h"
#include "AccountMgr.h"
#include "BattlegroundMgr.h"
#include "Common.h"
@@ -25,6 +25,7 @@
#include "PacketUtilities.h"
#include "Pet.h"
#include "Player.h"
#include "QueryHolder.h"
#include "SavingSystem.h"
#include "ScriptMgr.h"
#include "SocialMgr.h"
@@ -34,9 +35,8 @@
#include "WardenWin.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "QueryHolder.h"
#include "zlib.h"
#include "WorldSocket.h"
#include <zlib.h>
#ifdef ELUNA
#include "LuaEngine.h"
@@ -91,7 +91,8 @@ bool WorldSessionFilter::Process(WorldPacket* packet)
}
/// WorldSession constructor
WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime) :
WorldSession::WorldSession(uint32 id, std::string&& name, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion,
time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime) :
m_muteTime(mute_time),
m_timeOutTime(0),
_lastAuctionListItemsMSTime(0),
@@ -103,6 +104,7 @@ WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8
_security(sec),
_skipQueue(skipQueue),
_accountId(id),
_accountName(std::move(name)),
m_expansion(expansion),
m_total_time(TotalTime),
_logoutTime(0),
@@ -125,7 +127,6 @@ WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8
{
memset(m_Tutorials, 0, sizeof(m_Tutorials));
_warden = nullptr;
_offlineTime = 0;
_kicked = false;
_shouldSetOfflineInDB = true;
@@ -135,10 +136,9 @@ WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8
if (sock)
{
m_Address = sock->GetRemoteAddress();
sock->AddReference();
m_Address = sock->GetRemoteIpAddress().to_string();
ResetTimeOutTime(false);
LoginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = %u;", GetAccountId());
LoginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = %u;", GetAccountId()); // One-time query
}
}
@@ -154,17 +154,10 @@ WorldSession::~WorldSession()
/// - If have unclosed socket, close it
if (m_Socket)
{
m_Socket->CloseSocket("WorldSession destructor");
m_Socket->RemoveReference();
m_Socket->CloseSocket();
m_Socket = nullptr;
}
if (_warden)
{
delete _warden;
_warden = nullptr;
}
///- empty incoming packet queue
WorldPacket* packet = nullptr;
while (_recvQueue.next(packet))
@@ -257,9 +250,7 @@ void WorldSession::SendPacket(WorldPacket const* packet)
#endif
LOG_TRACE("network.opcode", "S->C: %s %s", GetPlayerInfo().c_str(), GetOpcodeNameForLogging(static_cast<OpcodeServer>(packet->GetOpcode())).c_str());
if (m_Socket->SendPacket(*packet) == -1)
m_Socket->CloseSocket("m_Socket->SendPacket(*packet) == -1");
m_Socket->SendPacket(*packet);
}
/// Add an incoming packet to the queue
@@ -290,26 +281,28 @@ void WorldSession::LogUnprocessedTail(WorldPacket* packet)
/// Update the WorldSession (triggered by World update)
bool WorldSession::Update(uint32 diff, PacketFilter& updater)
{
if (updater.ProcessUnsafe())
{
UpdateTimeOutTime(diff);
///- Before we process anything:
/// If necessary, kick the player because the client didn't send anything for too long
/// (or they've been idling in character select)
if (sWorld->getBoolConfig(CONFIG_CLOSE_IDLE_CONNECTIONS) && IsConnectionIdle() && m_Socket)
m_Socket->CloseSocket();
/// If necessary, kick the player because the client didn't send anything for too long
/// (or they've been idling in character select)
if (sWorld->getBoolConfig(CONFIG_CLOSE_IDLE_CONNECTIONS) && IsConnectionIdle() && m_Socket)
m_Socket->CloseSocket("Client didn't send anything for too long");
}
if (updater.ProcessUnsafe())
UpdateTimeOutTime(diff);
HandleTeleportTimeout(updater.ProcessUnsafe());
uint32 _startMSTime = getMSTime();
///- Retrieve packets from the receive queue and call the appropriate handlers
/// not process packets if socket already closed
WorldPacket* packet = nullptr;
//! Delete packet after processing by default
bool deletePacket = true;
WorldPacket* firstDelayedPacket = nullptr;
std::vector<WorldPacket*> requeuePackets;
uint32 processedPackets = 0;
time_t currentTime = time(nullptr);
while (m_Socket && !m_Socket->IsClosed() && !_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket && _recvQueue.next(packet, updater))
while (m_Socket && _recvQueue.next(packet, updater))
{
OpcodeClient opcode = static_cast<OpcodeClient>(packet->GetOpcode());
ClientOpcodeHandler const* opHandle = opcodeTable[opcode];
@@ -412,38 +405,9 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
//Any leftover will be processed in next update
if (processedPackets > MAX_PROCESSED_PACKETS_IN_SAME_WORLDSESSION_UPDATE)
break;
if (getMSTimeDiff(_startMSTime, getMSTime()) >= 3) // limit (by time) packets processed in one update, prevent DDoS
break;
}
if (m_Socket && !m_Socket->IsClosed())
ProcessQueryCallbacks();
if (updater.ProcessUnsafe())
{
if (m_Socket && !m_Socket->IsClosed() && _warden)
{
_warden->Update(diff);
}
time_t currTime = time(nullptr);
if (ShouldLogOut(currTime) && !m_playerLoading)
{
LogoutPlayer(true);
}
if (m_Socket && m_Socket->IsClosed())
{
m_Socket->RemoveReference();
m_Socket = nullptr;
}
if (!m_Socket)
{
return false;
}
}
_recvQueue.readd(requeuePackets.begin(), requeuePackets.end());
if (!updater.ProcessUnsafe()) // <=> updater is of type MapSessionFilter
{
@@ -461,29 +425,60 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
}
}
ProcessQueryCallbacks();
//check if we are safe to proceed with logout
//logout procedure should happen only in World::UpdateSessions() method!!!
if (updater.ProcessUnsafe())
{
if (m_Socket && m_Socket->IsOpen() && _warden)
{
_warden->Update(diff);
}
if (ShouldLogOut(currentTime) && !m_playerLoading)
{
LogoutPlayer(true);
}
if (m_Socket && !m_Socket->IsOpen())
{
if (GetPlayer() && _warden)
_warden->Update(diff);
m_Socket = nullptr;
}
if (!m_Socket)
{
return false;
}
}
return true;
}
bool WorldSession::HandleSocketClosed()
{
if (m_Socket && m_Socket->IsClosed() && !IsKicked() && GetPlayer() && !PlayerLogout() && GetPlayer()->m_taxi.empty() && GetPlayer()->IsInWorld() && !World::IsStopped())
if (m_Socket && !m_Socket->IsOpen() && !IsKicked() && GetPlayer() && !PlayerLogout() && GetPlayer()->m_taxi.empty() && GetPlayer()->IsInWorld() && !World::IsStopped())
{
m_Socket->RemoveReference();
m_Socket = nullptr;
GetPlayer()->TradeCancel(false);
return true;
}
return false;
}
bool WorldSession::IsSocketClosed() const {
return !m_Socket || m_Socket->IsClosed();
bool WorldSession::IsSocketClosed() const
{
return !m_Socket || !m_Socket->IsOpen();
}
void WorldSession::HandleTeleportTimeout(bool updateInSessions)
{
// pussywizard: handle teleport ack timeout
if (m_Socket && !m_Socket->IsClosed() && GetPlayer() && GetPlayer()->IsBeingTeleported())
if (m_Socket && m_Socket->IsOpen() && GetPlayer() && GetPlayer()->IsBeingTeleported())
{
time_t currTime = time(nullptr);
if (updateInSessions) // session update from World::UpdateSessions
@@ -681,7 +676,12 @@ void WorldSession::LogoutPlayer(bool save)
void WorldSession::KickPlayer(std::string const& reason, bool setKicked)
{
if (m_Socket)
m_Socket->CloseSocket(reason);
{
LOG_INFO("network.kick", "Account: %u Character: '%s' %s kicked with reason: %s", GetAccountId(), _player ? _player->GetName().c_str() : "<none>",
_player ? _player->GetGUID().ToString().c_str() : "", reason.c_str());
m_Socket->CloseSocket();
}
if (setKicked)
SetKicked(true); // pussywizard: the session won't be left ingame for 60 seconds and to also kick offline session
@@ -947,41 +947,41 @@ void WorldSession::ReadMovementInfo(WorldPacket& data, MovementInfo* mi)
It will freeze clients that receive this player's movement info.
*/
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ROOT),
MOVEMENTFLAG_ROOT);
MOVEMENTFLAG_ROOT);
//! Cannot hover without SPELL_AURA_HOVER
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_HOVER) && !GetPlayer()->HasAuraType(SPELL_AURA_HOVER),
MOVEMENTFLAG_HOVER);
MOVEMENTFLAG_HOVER);
//! Cannot ascend and descend at the same time
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ASCENDING) && mi->HasMovementFlag(MOVEMENTFLAG_DESCENDING),
MOVEMENTFLAG_ASCENDING | MOVEMENTFLAG_DESCENDING);
MOVEMENTFLAG_ASCENDING | MOVEMENTFLAG_DESCENDING);
//! Cannot move left and right at the same time
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_LEFT) && mi->HasMovementFlag(MOVEMENTFLAG_RIGHT),
MOVEMENTFLAG_LEFT | MOVEMENTFLAG_RIGHT);
MOVEMENTFLAG_LEFT | MOVEMENTFLAG_RIGHT);
//! Cannot strafe left and right at the same time
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_STRAFE_LEFT) && mi->HasMovementFlag(MOVEMENTFLAG_STRAFE_RIGHT),
MOVEMENTFLAG_STRAFE_LEFT | MOVEMENTFLAG_STRAFE_RIGHT);
MOVEMENTFLAG_STRAFE_LEFT | MOVEMENTFLAG_STRAFE_RIGHT);
//! Cannot pitch up and down at the same time
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_PITCH_UP) && mi->HasMovementFlag(MOVEMENTFLAG_PITCH_DOWN),
MOVEMENTFLAG_PITCH_UP | MOVEMENTFLAG_PITCH_DOWN);
MOVEMENTFLAG_PITCH_UP | MOVEMENTFLAG_PITCH_DOWN);
//! Cannot move forwards and backwards at the same time
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_FORWARD) && mi->HasMovementFlag(MOVEMENTFLAG_BACKWARD),
MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_BACKWARD);
MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_BACKWARD);
//! Cannot walk on water without SPELL_AURA_WATER_WALK
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_WATERWALKING) &&
!GetPlayer()->HasAuraType(SPELL_AURA_WATER_WALK) &&
!GetPlayer()->HasAuraType(SPELL_AURA_GHOST),
MOVEMENTFLAG_WATERWALKING);
!GetPlayer()->HasAuraType(SPELL_AURA_WATER_WALK) &&
!GetPlayer()->HasAuraType(SPELL_AURA_GHOST),
MOVEMENTFLAG_WATERWALKING);
//! Cannot feather fall without SPELL_AURA_FEATHER_FALL
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_FALLING_SLOW) && !GetPlayer()->HasAuraType(SPELL_AURA_FEATHER_FALL),
MOVEMENTFLAG_FALLING_SLOW);
MOVEMENTFLAG_FALLING_SLOW);
/*! Cannot fly if no fly auras present. Exception is being a GM.
Note that we check for account level instead of Player::IsGameMaster() because in some
@@ -990,14 +990,14 @@ void WorldSession::ReadMovementInfo(WorldPacket& data, MovementInfo* mi)
*/
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY) && GetSecurity() == SEC_PLAYER && !GetPlayer()->m_mover->HasAuraType(SPELL_AURA_FLY) && !GetPlayer()->m_mover->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED),
MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY);
MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY);
//! Cannot fly and fall at the same time
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_DISABLE_GRAVITY) && mi->HasMovementFlag(MOVEMENTFLAG_FALLING),
MOVEMENTFLAG_FALLING);
MOVEMENTFLAG_FALLING);
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_SPLINE_ENABLED) &&
(!GetPlayer()->movespline->Initialized() || GetPlayer()->movespline->Finalized()), MOVEMENTFLAG_SPLINE_ENABLED);
(!GetPlayer()->movespline->Initialized() || GetPlayer()->movespline->Finalized()), MOVEMENTFLAG_SPLINE_ENABLED);
#undef REMOVE_VIOLATING_FLAGS
}
@@ -1040,7 +1040,7 @@ void WorldSession::WriteMovementInfo(WorldPacket* data, MovementInfo* mi)
*data << mi->splineElevation;
}
void WorldSession::ReadAddonsInfo(WorldPacket& data)
void WorldSession::ReadAddonsInfo(ByteBuffer& data)
{
if (data.rpos() + 4 > data.size())
return;
@@ -1218,7 +1218,7 @@ void WorldSession::InitWarden(SessionKey const& k, std::string const& os)
{
if (os == "Win")
{
_warden = new WardenWin();
_warden = std::make_unique<WardenWin>();
_warden->Init(this, k);
}
else if (os == "OSX")
@@ -1533,6 +1533,9 @@ uint32 WorldSession::DosProtection::GetMaxPacketCounterAllowed(uint16 opcode) co
return maxPacketCounterAllowed;
}
WorldSession::DosProtection::DosProtection(WorldSession* s) :
Session(s), _policy((Policy)sWorld->getIntConfig(CONFIG_PACKET_SPOOF_POLICY)) { }
void WorldSession::ResetTimeSync()
{
_timeSyncNextCounter = 0;

View File

@@ -230,14 +230,14 @@ struct PacketCounter
class WorldSession
{
public:
WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime);
WorldSession(uint32 id, std::string&& name, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime);
~WorldSession();
bool PlayerLoading() const { return m_playerLoading; }
bool PlayerLogout() const { return m_playerLogout; }
bool PlayerLogoutWithSave() const { return m_playerLogout && m_playerSave; }
void ReadAddonsInfo(WorldPacket& data);
void ReadAddonsInfo(ByteBuffer& data);
void SendAddonsInfo();
void ReadMovementInfo(WorldPacket& data, MovementInfo* mi);
@@ -987,7 +987,7 @@ protected:
{
friend class World;
public:
DosProtection(WorldSession* s) : Session(s), _policy((Policy)sWorld->getIntConfig(CONFIG_PACKET_SPOOF_POLICY)) { }
DosProtection(WorldSession* s);
bool EvaluateOpcode(WorldPacket& p, time_t time) const;
protected:
enum Policy
@@ -1035,20 +1035,20 @@ private:
ObjectGuid::LowType m_GUIDLow;
Player* _player;
WorldSocket* m_Socket;
std::shared_ptr<WorldSocket> m_Socket;
std::string m_Address;
// std::string m_LAddress; // Last Attempted Remote Adress - we can not set attempted ip for a non-existing session!
AccountTypes _security;
bool _skipQueue;
uint32 _accountId;
std::string _accountName;
uint8 m_expansion;
uint32 m_total_time;
typedef std::list<AddonInfo> AddonsList;
// Warden
Warden* _warden; // Remains nullptr if Warden system is not enabled by config
std::unique_ptr<Warden> _warden; // Remains nullptr if Warden system is not enabled by config
time_t _logoutTime;
bool m_inQueue; // session wait in auth.queue
@@ -1057,7 +1057,7 @@ private:
bool m_playerSave;
LocaleConstant m_sessionDbcLocale;
LocaleConstant m_sessionDbLocaleIndex;
uint32 m_latency;
std::atomic<uint32> m_latency;
AccountData m_accountData[NUM_ACCOUNT_DATA_TYPES];
uint32 m_Tutorials[MAX_ACCOUNT_TUTORIAL_VALUES];
bool m_TutorialsChanged;
@@ -1081,6 +1081,9 @@ private:
std::map<uint32, uint32> _pendingTimeSyncRequests; // key: counter. value: server time when packet with that counter was sent.
uint32 _timeSyncNextCounter;
uint32 _timeSyncTimer;
WorldSession(WorldSession const& right) = delete;
WorldSession& operator=(WorldSession const& right) = delete;
};
#endif
/// @}

File diff suppressed because it is too large Load Diff

View File

@@ -4,197 +4,121 @@
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*/
/** \addtogroup u2w User to World Communication
* @{
* \file WorldSocket.h
* \author Derex <derex101@gmail.com>
*/
#ifndef __WORLDSOCKET_H__
#define __WORLDSOCKET_H__
#ifndef _WORLDSOCKET_H
#define _WORLDSOCKET_H
#include "AuthCrypt.h"
#include "Common.h"
#include "Duration.h"
#include <ace/Message_Block.h>
#include <ace/SOCK_Stream.h>
#include <ace/Svc_Handler.h>
#include <ace/Synch_Traits.h>
#include <ace/Unbounded_Queue.h>
#include <mutex>
#include "AuthCrypt.h"
#include "ServerPktHeader.h"
#include "Socket.h"
#include "Util.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "MPSCQueue.h"
#include <boost/asio/ip/tcp.hpp>
#if !defined (ACE_LACKS_PRAGMA_ONCE)
#pragma once
#endif /* ACE_LACKS_PRAGMA_ONCE */
using boost::asio::ip::tcp;
class ACE_Message_Block;
class WorldPacket;
class WorldSession;
class EncryptablePacket : public WorldPacket
{
public:
EncryptablePacket(WorldPacket const& packet, bool encrypt) : WorldPacket(packet), _encrypt(encrypt)
{
SocketQueueLink.store(nullptr, std::memory_order_relaxed);
}
bool NeedsEncryption() const { return _encrypt; }
std::atomic<EncryptablePacket*> SocketQueueLink;
private:
bool _encrypt;
};
namespace WorldPackets
{
class ServerPacket;
}
/// Handler that can communicate over stream sockets.
typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> WorldHandler;
/**
* WorldSocket.
*
* This class is responsible for the communication with
* remote clients.
* Most methods return -1 on failure.
* The class uses reference counting.
*
* For output the class uses one buffer (64K usually) and
* a queue where it stores packet if there is no place on
* the queue. The reason this is done, is because the server
* does really a lot of small-size writes to it, and it doesn't
* scale well to allocate memory for every. When something is
* written to the output buffer the socket is not immediately
* activated for output (again for the same reason), there
* is 10ms celling (thats why there is Update() method).
* This concept is similar to TCP_CORK, but TCP_CORK
* uses 200ms celling. As result overhead generated by
* sending packets from "producer" threads is minimal,
* and doing a lot of writes with small size is tolerated.
*
* The calls to Update() method are managed by WorldSocketMgr
* and ReactorRunnable.
*
* For input, the class uses one 4096 bytes buffer on stack
* to which it does recv() calls. And then received data is
* distributed where its needed. 4096 matches pretty well the
* traffic generated by client for now.
*
* The input/output do speculative reads/writes (AKA it tryes
* to read all data available in the kernel buffer or tryes to
* write everything available in userspace buffer),
* which is ok for using with Level and Edge Triggered IO
* notification.
*
*/
class WorldSocket : public WorldHandler
#pragma pack(push, 1)
struct ClientPktHeader
{
uint16 size;
uint32 cmd;
bool IsValidSize() const { return size >= 4 && size < 10240; }
bool IsValidOpcode() const { return cmd < NUM_OPCODE_HANDLERS; }
};
#pragma pack(pop)
struct AuthSession;
class AC_GAME_API WorldSocket : public Socket<WorldSocket>
{
typedef Socket<WorldSocket> BaseSocket;
public:
WorldSocket (void);
virtual ~WorldSocket (void);
WorldSocket(tcp::socket&& socket);
~WorldSocket();
friend class WorldSocketMgr;
WorldSocket(WorldSocket const& right) = delete;
WorldSocket& operator=(WorldSocket const& right) = delete;
/// Check if socket is closed.
bool IsClosed (void) const;
void Start() override;
bool Update() override;
/// Close the socket.
void CloseSocket(std::string const& reason);
void SendPacket(WorldPacket const& packet);
/// Get address of connected peer.
const std::string& GetRemoteAddress (void) const;
void SetSendBufferSize(std::size_t sendBufferSize) { _sendBufferSize = sendBufferSize; }
/// Send A packet on the socket, this function is reentrant.
/// @param pct packet to send
/// @return -1 of failure
int SendPacket(const WorldPacket& pct);
protected:
void OnClose() override;
void ReadHandler() override;
bool ReadHeaderHandler();
/// Add reference to this object.
long AddReference (void);
enum class ReadDataHandlerResult
{
Ok = 0,
Error = 1,
WaitingForQuery = 2
};
/// Remove reference to this object.
long RemoveReference (void);
/// things called by ACE framework.
/// Called on open, the void* is the acceptor.
virtual int open (void*);
/// Called on failures inside of the acceptor, don't call from your code.
virtual int close (u_long);
/// Called when we can read from the socket.
virtual int handle_input (ACE_HANDLE = ACE_INVALID_HANDLE);
/// Called when the socket can write.
virtual int handle_output (ACE_HANDLE = ACE_INVALID_HANDLE);
/// Called when connection is closed or error happens.
virtual int handle_close (ACE_HANDLE = ACE_INVALID_HANDLE,
ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK);
/// Called by WorldSocketMgr/ReactorRunnable.
int Update (void);
ReadDataHandlerResult ReadDataHandler();
private:
/// Helper functions for processing incoming data.
int handle_input_header (void);
int handle_input_payload (void);
int handle_input_missing_data (void);
void CheckIpCallback(PreparedQueryResult result);
/// Help functions to mark/unmark the socket for output.
/// @param g the guard is for m_OutBufferLock, the function will release it
int cancel_wakeup_output();
int schedule_wakeup_output();
/// writes network.opcode log
/// accessing WorldSession is not threadsafe, only do it when holding _worldSessionLock
void LogOpcodeText(OpcodeClient opcode, std::unique_lock<std::mutex> const& guard) const;
/// Drain the queue if its not empty.
int handle_output_queue();
/// sends and logs network.opcode without accessing WorldSession
void SendPacketAndLogOpcode(WorldPacket const& packet);
void HandleSendAuthSession();
void HandleAuthSession(WorldPacket& recvPacket);
void HandleAuthSessionCallback(std::shared_ptr<AuthSession> authSession, PreparedQueryResult result);
void LoadSessionPermissionsCallback(PreparedQueryResult result);
void SendAuthResponseError(uint8 code);
/// process one incoming packet.
/// @param new_pct received packet, note that you need to delete it.
int ProcessIncoming (WorldPacket* new_pct);
bool HandlePing(WorldPacket& recvPacket);
/// Called by ProcessIncoming() on CMSG_AUTH_SESSION.
int HandleAuthSession (WorldPacket& recvPacket);
std::array<uint8, 4> _authSeed;
AuthCrypt _authCrypt;
/// Called by ProcessIncoming() on CMSG_PING.
int HandlePing (WorldPacket& recvPacket);
TimePoint _LastPingTime;
uint32 _OverSpeedPings;
private:
/// Time in which the last ping was received
SystemTimePoint m_LastPingTime;
std::mutex _worldSessionLock;
WorldSession* _worldSession;
bool _authed;
/// Keep track of over-speed pings, to prevent ping flood.
uint32 m_OverSpeedPings;
/// Address of the remote peer
std::string m_Address;
/// Class used for managing encryption of the headers
AuthCrypt m_Crypt;
/// Mutex lock to protect m_Session
std::mutex m_SessionLock;
/// Session to which received packets are routed
WorldSession* m_Session;
/// here are stored the fragments of the received data
WorldPacket* m_RecvWPct;
/// This block actually refers to m_RecvWPct contents,
/// which allows easy and safe writing to it.
/// It wont free memory when its deleted. m_RecvWPct takes care of freeing.
ACE_Message_Block m_RecvPct;
/// Fragment of the received header.
ACE_Message_Block m_Header;
/// Mutex for protecting output related data.
std::mutex m_OutBufferLock;
/// Buffer used for writing output.
ACE_Message_Block* m_OutBuffer;
/// Size of the m_OutBuffer.
size_t m_OutBufferSize;
/// True if the socket is registered with the reactor for output
bool m_OutActive;
std::array<uint8, 4> m_Seed;
MessageBuffer _headerBuffer;
MessageBuffer _packetBuffer;
MPSCQueue<EncryptablePacket, &EncryptablePacket::SocketQueueLink> _bufferQueue;
std::size_t _sendBufferSize;
QueryCallbackProcessor _queryProcessor;
std::string _ipCountry;
};
#endif /* _WORLDSOCKET_H */
/// @}
#endif

View File

@@ -1,53 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*/
/** \addtogroup u2w User to World Communication
* @{
* \file WorldSocketMgr.h
*/
#ifndef __WORLDSOCKETACCEPTOR_H_
#define __WORLDSOCKETACCEPTOR_H_
#include "Common.h"
#include "WorldSocket.h"
#include <ace/Acceptor.h>
#include <ace/SOCK_Acceptor.h>
class WorldSocketAcceptor : public ACE_Acceptor<WorldSocket, ACE_SOCK_Acceptor>
{
public:
WorldSocketAcceptor(void) { }
virtual ~WorldSocketAcceptor(void)
{
if (reactor())
reactor()->cancel_timer(this, 1);
}
protected:
virtual int handle_timeout(const ACE_Time_Value& /*current_time*/, const void* /*act = 0*/)
{
LOG_INFO("network", "Resuming acceptor");
reactor()->cancel_timer(this, 1);
return reactor()->register_handler(this, ACE_Event_Handler::ACCEPT_MASK);
}
virtual int handle_accept_error(void)
{
#if defined(ENFILE) && defined(EMFILE)
if (errno == ENFILE || errno == EMFILE)
{
LOG_ERROR("network", "Out of file descriptors, suspending incoming connections for 10 seconds");
reactor()->remove_handler(this, ACE_Event_Handler::ACCEPT_MASK | ACE_Event_Handler::DONT_CALL);
reactor()->schedule_timer(this, nullptr, ACE_Time_Value(10));
}
#endif
return 0;
}
};
#endif /* __WORLDSOCKETACCEPTOR_H_ */
/// @}

View File

@@ -4,345 +4,109 @@
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*/
/** \file WorldSocketMgr.cpp
* \ingroup u2w
* \author Derex <derex101@gmail.com>
*/
#include "Config.h"
#include "DatabaseEnv.h"
#include "Log.h"
#include "NetworkThread.h"
#include "ScriptMgr.h"
#include "WorldSocket.h"
#include "WorldSocketAcceptor.h"
#include "WorldSocketMgr.h"
#include <ace/ACE.h>
#include <ace/Dev_Poll_Reactor.h>
#include <ace/Log_Msg.h>
#include <ace/os_include/arpa/os_inet.h>
#include <ace/os_include/netinet/os_tcp.h>
#include <ace/os_include/sys/os_socket.h>
#include <ace/Reactor_Impl.h>
#include <ace/Reactor.h>
#include <ace/TP_Reactor.h>
#include <atomic>
#include <set>
#include <boost/system/error_code.hpp>
/**
* This is a helper class to WorldSocketMgr, that manages
* network threads, and assigning connections from acceptor thread
* to other network threads
*/
class ReactorRunnable : protected ACE_Task_Base
static void OnSocketAccept(tcp::socket&& sock, uint32 threadIndex)
{
sWorldSocketMgr.OnSocketOpen(std::forward<tcp::socket>(sock), threadIndex);
}
class WorldSocketThread : public NetworkThread<WorldSocket>
{
public:
ReactorRunnable() :
m_Reactor(0),
m_Connections(0),
m_ThreadId(-1)
void SocketAdded(std::shared_ptr<WorldSocket> sock) override
{
ACE_Reactor_Impl* imp;
#if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL)
imp = new ACE_Dev_Poll_Reactor();
imp->max_notify_iterations (128);
imp->restart (1);
#else
imp = new ACE_TP_Reactor();
imp->max_notify_iterations (128);
#endif
m_Reactor = new ACE_Reactor (imp, 1);
}
~ReactorRunnable() override
{
Stop();
Wait();
delete m_Reactor;
}
void Stop()
{
m_Reactor->end_reactor_event_loop();
}
int Start()
{
if (m_ThreadId != -1)
return -1;
return (m_ThreadId = activate());
}
void Wait() { ACE_Task_Base::wait(); }
long Connections()
{
return m_Connections;
}
int AddSocket (WorldSocket* sock)
{
std::lock_guard<std::mutex> guard(m_NewSockets_Lock);
++m_Connections;
sock->AddReference();
sock->reactor (m_Reactor);
m_NewSockets.insert (sock);
sock->SetSendBufferSize(sWorldSocketMgr.GetApplicationSendBufferSize());
sScriptMgr->OnSocketOpen(sock);
return 0;
}
ACE_Reactor* GetReactor()
void SocketRemoved(std::shared_ptr<WorldSocket> sock) override
{
return m_Reactor;
sScriptMgr->OnSocketClose(sock);
}
protected:
void AddNewSockets()
{
std::lock_guard<std::mutex> guard(m_NewSockets_Lock);
if (m_NewSockets.empty())
return;
for (SocketSet::const_iterator i = m_NewSockets.begin(); i != m_NewSockets.end(); ++i)
{
WorldSocket* sock = (*i);
if (sock->IsClosed())
{
sScriptMgr->OnSocketClose(sock, true);
sock->RemoveReference();
--m_Connections;
}
else
m_Sockets.insert (sock);
}
m_NewSockets.clear();
}
int svc() override
{
LOG_DEBUG("network", "Network Thread Starting");
ASSERT(m_Reactor);
SocketSet::iterator i, t;
while (!m_Reactor->reactor_event_loop_done())
{
// dont be too smart to move this outside the loop
// the run_reactor_event_loop will modify interval
ACE_Time_Value interval (0, 10000);
if (m_Reactor->run_reactor_event_loop (interval) == -1)
break;
AddNewSockets();
for (i = m_Sockets.begin(); i != m_Sockets.end();)
{
if ((*i)->Update() == -1)
{
t = i;
++i;
(*t)->CloseSocket("svc()");
sScriptMgr->OnSocketClose((*t), false);
(*t)->RemoveReference();
--m_Connections;
m_Sockets.erase (t);
}
else
++i;
}
}
LOG_DEBUG("network", "Network Thread exits");
return 0;
}
private:
typedef std::atomic<int> AtomicInt;
typedef std::set<WorldSocket*> SocketSet;
ACE_Reactor* m_Reactor;
AtomicInt m_Connections;
int m_ThreadId;
SocketSet m_Sockets;
SocketSet m_NewSockets;
std::mutex m_NewSockets_Lock;
};
WorldSocketMgr::WorldSocketMgr() :
m_NetThreads(0),
m_NetThreadsCount(0),
m_SockOutKBuff(-1),
m_SockOutUBuff(65536),
m_UseNoDelay(true),
m_Acceptor (0)
BaseSocketMgr(), _socketSystemSendBufferSize(-1), _socketApplicationSendBufferSize(65536), _tcpNoDelay(true)
{
}
WorldSocketMgr::~WorldSocketMgr()
{
delete [] m_NetThreads;
delete m_Acceptor;
}
WorldSocketMgr* WorldSocketMgr::instance()
WorldSocketMgr& WorldSocketMgr::Instance()
{
static WorldSocketMgr instance;
return &instance;
return instance;
}
int
WorldSocketMgr::StartReactiveIO (uint16 port, const char* address)
bool WorldSocketMgr::StartWorldNetwork(Acore::Asio::IoContext& ioContext, std::string const& bindIp, uint16 port, int threadCount)
{
m_UseNoDelay = sConfigMgr->GetOption<bool> ("Network.TcpNodelay", true);
_tcpNoDelay = sConfigMgr->GetOption<bool>("Network.TcpNodelay", true);
int num_threads = sConfigMgr->GetOption<int32> ("Network.Threads", 1);
if (num_threads <= 0)
{
LOG_ERROR("network", "Network.Threads is wrong in your config file");
return -1;
}
m_NetThreadsCount = static_cast<size_t> (num_threads + 1);
m_NetThreads = new ReactorRunnable[m_NetThreadsCount];
LOG_INFO("network", "Max allowed socket connections %d", ACE::max_handles());
int const max_connections = ACORE_MAX_LISTEN_CONNECTIONS;
LOG_DEBUG("network", "Max allowed socket connections %d", max_connections);
// -1 means use default
m_SockOutKBuff = sConfigMgr->GetOption<int32> ("Network.OutKBuff", -1);
_socketSystemSendBufferSize = sConfigMgr->GetOption<int32>("Network.OutKBuff", -1);
_socketApplicationSendBufferSize = sConfigMgr->GetOption<int32>("Network.OutUBuff", 65536);
m_SockOutUBuff = sConfigMgr->GetOption<int32> ("Network.OutUBuff", 65536);
if (m_SockOutUBuff <= 0)
if (_socketApplicationSendBufferSize <= 0)
{
LOG_ERROR("network", "Network.OutUBuff is wrong in your config file");
return -1;
return false;
}
m_Acceptor = new WorldSocketAcceptor;
if (!BaseSocketMgr::StartNetwork(ioContext, bindIp, port, threadCount))
return false;
ACE_INET_Addr listen_addr (port, address);
if (m_Acceptor->open(listen_addr, m_NetThreads[0].GetReactor(), ACE_NONBLOCK) == -1)
{
LOG_ERROR("network", "Failed to open acceptor, check if the port is free");
return -1;
}
for (size_t i = 0; i < m_NetThreadsCount; ++i)
m_NetThreads[i].Start();
return 0;
}
int
WorldSocketMgr::StartNetwork (uint16 port, const char* address)
{
if (!sLog->ShouldLog("network", LogLevel::LOG_LEVEL_DEBUG))
ACE_Log_Msg::instance()->priority_mask(LM_ERROR, ACE_Log_Msg::PROCESS);
if (StartReactiveIO(port, address) == -1)
return -1;
_acceptor->AsyncAcceptWithCallback<&OnSocketAccept>();
sScriptMgr->OnNetworkStart();
return 0;
return true;
}
void
WorldSocketMgr::StopNetwork()
void WorldSocketMgr::StopNetwork()
{
if (m_Acceptor)
{
m_Acceptor->close();
}
if (m_NetThreadsCount != 0)
{
for (size_t i = 0; i < m_NetThreadsCount; ++i)
m_NetThreads[i].Stop();
}
Wait();
BaseSocketMgr::StopNetwork();
sScriptMgr->OnNetworkStop();
}
void
WorldSocketMgr::Wait()
{
if (m_NetThreadsCount != 0)
{
for (size_t i = 0; i < m_NetThreadsCount; ++i)
m_NetThreads[i].Wait();
}
}
int
WorldSocketMgr::OnSocketOpen (WorldSocket* sock)
void WorldSocketMgr::OnSocketOpen(tcp::socket&& sock, uint32 threadIndex)
{
// set some options here
if (m_SockOutKBuff >= 0)
if (_socketSystemSendBufferSize >= 0)
{
if (sock->peer().set_option (SOL_SOCKET,
SO_SNDBUF,
(void*) & m_SockOutKBuff,
sizeof (int)) == -1 && errno != ENOTSUP)
boost::system::error_code err;
sock.set_option(boost::asio::socket_base::send_buffer_size(_socketSystemSendBufferSize), err);
if (err && err != boost::system::errc::not_supported)
{
LOG_ERROR("network", "WorldSocketMgr::OnSocketOpen set_option SO_SNDBUF");
return -1;
LOG_ERROR("network", "WorldSocketMgr::OnSocketOpen sock.set_option(boost::asio::socket_base::send_buffer_size) err = %s", err.message().c_str());
return;
}
}
static const int ndoption = 1;
// Set TCP_NODELAY.
if (m_UseNoDelay)
if (_tcpNoDelay)
{
if (sock->peer().set_option (ACE_IPPROTO_TCP,
TCP_NODELAY,
(void*)&ndoption,
sizeof (int)) == -1)
boost::system::error_code err;
sock.set_option(boost::asio::ip::tcp::no_delay(true), err);
if (err)
{
LOG_ERROR("network", "WorldSocketMgr::OnSocketOpen: peer().set_option TCP_NODELAY errno = %s", ACE_OS::strerror (errno));
return -1;
LOG_ERROR("network", "WorldSocketMgr::OnSocketOpen sock.set_option(boost::asio::ip::tcp::no_delay) err = %s", err.message().c_str());
return;
}
}
sock->m_OutBufferSize = static_cast<size_t> (m_SockOutUBuff);
// we skip the Acceptor Thread
size_t min = 1;
ASSERT(m_NetThreadsCount >= 1);
for (size_t i = 1; i < m_NetThreadsCount; ++i)
if (m_NetThreads[i].Connections() < m_NetThreads[min].Connections())
min = i;
return m_NetThreads[min].AddSocket (sock);
BaseSocketMgr::OnSocketOpen(std::forward<tcp::socket>(sock), threadIndex);
}
NetworkThread<WorldSocket>* WorldSocketMgr::CreateThreads() const
{
return new WorldSocketThread[GetNetworkThreadCount()];
}

View File

@@ -4,58 +4,43 @@
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*/
/** \addtogroup u2w User to World Communication
* @{
* \file WorldSocketMgr.h
* \author Derex <derex101@gmail.com>
*/
#ifndef __WORLDSOCKETMGR_H
#define __WORLDSOCKETMGR_H
#include "Common.h"
#include "SocketMgr.h"
class WorldSocket;
class ReactorRunnable;
class ACE_Event_Handler;
/// Manages all sockets connected to peers and network threads
class WorldSocketMgr
class AC_GAME_API WorldSocketMgr : public SocketMgr<WorldSocket>
{
public:
friend class WorldSocket;
typedef SocketMgr<WorldSocket> BaseSocketMgr;
static WorldSocketMgr* instance();
public:
static WorldSocketMgr& Instance();
/// Start network, listen at address:port .
int StartNetwork(uint16 port, const char* address);
bool StartWorldNetwork(Acore::Asio::IoContext& ioContext, std::string const& bindIp, uint16 port, int networkThreads);
/// Stops all network threads, It will wait for all running threads .
void StopNetwork();
void StopNetwork() override;
/// Wait untill all network threads have "joined" .
void Wait();
void OnSocketOpen(tcp::socket&& sock, uint32 threadIndex) override;
private:
int OnSocketOpen(WorldSocket* sock);
std::size_t GetApplicationSendBufferSize() const { return _socketApplicationSendBufferSize; }
int StartReactiveIO(uint16 port, const char* address);
private:
protected:
WorldSocketMgr();
virtual ~WorldSocketMgr();
ReactorRunnable* m_NetThreads;
size_t m_NetThreadsCount;
NetworkThread<WorldSocket>* CreateThreads() const override;
int m_SockOutKBuff;
int m_SockOutUBuff;
bool m_UseNoDelay;
class WorldSocketAcceptor* m_Acceptor;
private:
int32 _socketSystemSendBufferSize;
int32 _socketApplicationSendBufferSize;
bool _tcpNoDelay;
};
#define sWorldSocketMgr WorldSocketMgr::instance()
#define sWorldSocketMgr WorldSocketMgr::Instance()
#endif
/// @}