mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-24 14:16:31 +00:00
feat(Core): replace ACE network with Boost.Asio (#6574)
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
50
src/server/game/Server/Protocol/ServerPktHeader.h
Normal file
50
src/server/game/Server/Protocol/ServerPktHeader.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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_ */
|
||||
/// @}
|
||||
@@ -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()];
|
||||
}
|
||||
|
||||
@@ -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
|
||||
/// @}
|
||||
|
||||
Reference in New Issue
Block a user