feat(Core/Packets): Port packet handling from TrinityCore (#5617)

* feat(Core/Packets): Port packet handling from TrinityCore

* 1

* 2

* 3

* 1

* 2

* #3670

* 3

* 1

* codestyle

* fix msvc warnings
This commit is contained in:
Kargatum
2021-05-22 05:10:46 +07:00
committed by GitHub
parent 537ebe87aa
commit 63a273507c
29 changed files with 2768 additions and 1868 deletions

View File

@@ -0,0 +1,37 @@
/*
* 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>
*/
#include "Packet.h"
#include "Errors.h"
WorldPackets::Packet::Packet(WorldPacket&& worldPacket) : _worldPacket(std::move(worldPacket))
{
}
WorldPackets::ServerPacket::ServerPacket(OpcodeServer opcode, size_t initialSize /*= 200*/) : Packet(WorldPacket(opcode, initialSize))
{
}
void WorldPackets::ServerPacket::Read()
{
ASSERT(!"Read not implemented for server packets.");
}
WorldPackets::ClientPacket::ClientPacket(OpcodeClient expectedOpcode, WorldPacket&& packet) : Packet(std::move(packet))
{
ASSERT(GetOpcode() == expectedOpcode);
}
WorldPackets::ClientPacket::ClientPacket(WorldPacket&& packet)
: Packet(std::move(packet))
{
}
WorldPacket const* WorldPackets::ClientPacket::Write()
{
ASSERT(!"Write not allowed for client packets.");
// Shut up some compilers
return nullptr;
}

View File

@@ -0,0 +1,59 @@
/*
* 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 PacketBaseWorld_h__
#define PacketBaseWorld_h__
#include "WorldPacket.h"
namespace WorldPackets
{
class AC_GAME_API Packet
{
public:
Packet(WorldPacket&& worldPacket);
virtual ~Packet() = default;
Packet(Packet const& right) = delete;
Packet& operator=(Packet const& right) = delete;
virtual WorldPacket const* Write() = 0;
virtual void Read() = 0;
WorldPacket const* GetRawPacket() const { return &_worldPacket; }
size_t GetSize() const { return _worldPacket.size(); }
protected:
WorldPacket _worldPacket;
};
class AC_GAME_API ServerPacket : public Packet
{
public:
ServerPacket(OpcodeServer opcode, size_t initialSize = 200);
void Read() final;
void Clear() { _worldPacket.clear(); }
WorldPacket&& Move() { return std::move(_worldPacket); }
void ShrinkToFit() { _worldPacket.shrink_to_fit(); }
OpcodeServer GetOpcode() const { return OpcodeServer(_worldPacket.GetOpcode()); }
};
class AC_GAME_API ClientPacket : public Packet
{
public:
ClientPacket(WorldPacket&& packet);
ClientPacket(OpcodeClient expectedOpcode, WorldPacket&& packet);
WorldPacket const* Write() final;
OpcodeClient GetOpcode() const { return OpcodeClient(_worldPacket.GetOpcode()); }
};
}
#endif // PacketBaseWorld_h__

View File

@@ -0,0 +1,9 @@
/*
* 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 AllPackets_h__
#define AllPackets_h__
#endif // AllPackets_h__

View File

@@ -0,0 +1,59 @@
/*
* 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>
*/
#include "PacketUtilities.h"
//#include "Hyperlinks.h"
#include "Errors.h"
#include <utf8.h>
#include <sstream>
#include <array>
WorldPackets::InvalidStringValueException::InvalidStringValueException(std::string const& value) :
ByteBufferInvalidValueException("string", value.c_str()) { }
WorldPackets::InvalidUtf8ValueException::InvalidUtf8ValueException(std::string const& value) :
InvalidStringValueException(value) { }
WorldPackets::InvalidHyperlinkException::InvalidHyperlinkException(std::string const& value) :
InvalidStringValueException(value) { }
WorldPackets::IllegalHyperlinkException::IllegalHyperlinkException(std::string const& value) :
InvalidStringValueException(value) { }
bool WorldPackets::Strings::Utf8::Validate(std::string const& value)
{
if (!utf8::is_valid(value.begin(), value.end()))
throw InvalidUtf8ValueException(value);
return true;
}
//bool WorldPackets::Strings::Hyperlinks::Validate(std::string const& value)
//{
// if (!Warhead::Hyperlinks::CheckAllLinks(value))
// throw InvalidHyperlinkException(value);
//
// return true;
//}
bool WorldPackets::Strings::NoHyperlinks::Validate(std::string const& value)
{
if (value.find('|') != std::string::npos)
throw IllegalHyperlinkException(value);
return true;
}
WorldPackets::PacketArrayMaxCapacityException::PacketArrayMaxCapacityException(std::size_t requestedSize, std::size_t sizeLimit)
{
std::ostringstream builder;
builder << "Attempted to read more array elements from packet " << requestedSize << " than allowed " << sizeLimit;
message().assign(builder.str());
}
void WorldPackets::CheckCompactArrayMaskOverflow(std::size_t index, std::size_t limit)
{
ASSERT(index < limit, "Attempted to insert " SZFMTD " values into CompactArray but it can only hold " SZFMTD, index, limit);
}

View File

@@ -0,0 +1,286 @@
/*
* 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 PacketUtilities_h__
#define PacketUtilities_h__
#include "ByteBuffer.h"
#include "Tuples.h"
#include <string_view>
namespace WorldPackets
{
class InvalidStringValueException : public ByteBufferInvalidValueException
{
public:
InvalidStringValueException(std::string const& value);
std::string const& GetInvalidValue() const { return _value; }
private:
std::string _value;
};
class InvalidUtf8ValueException : public InvalidStringValueException
{
public:
InvalidUtf8ValueException(std::string const& value);
};
class InvalidHyperlinkException : public InvalidStringValueException
{
public:
InvalidHyperlinkException(std::string const& value);
};
class IllegalHyperlinkException : public InvalidStringValueException
{
public:
IllegalHyperlinkException(std::string const& value);
};
namespace Strings
{
struct RawBytes { static bool Validate(std::string const& /*value*/) { return true; } };
template<std::size_t MaxBytesWithoutNullTerminator>
struct ByteSize { static bool Validate(std::string const& value) { return value.size() <= MaxBytesWithoutNullTerminator; } };
struct Utf8 { static bool Validate(std::string const& value); };
//struct Hyperlinks { static bool Validate(std::string const& value); };
struct NoHyperlinks { static bool Validate(std::string const& value); };
}
/**
* Utility class for automated prevention of invalid strings in client packets
*/
template<std::size_t MaxBytesWithoutNullTerminator, typename... Validators>
class String
{
using ValidatorList = std::conditional_t<!acore::has_type<Strings::RawBytes, std::tuple<Validators...>>::value,
std::tuple<Strings::ByteSize<MaxBytesWithoutNullTerminator>, Strings::Utf8, Validators...>,
std::tuple<Strings::ByteSize<MaxBytesWithoutNullTerminator>, Validators...>>;
public:
bool empty() const { return _storage.empty(); }
char const* c_str() const { return _storage.c_str(); }
operator std::string_view() const { return _storage; }
operator std::string&() { return _storage; }
operator std::string const&() const { return _storage; }
std::string&& Move() { return std::move(_storage); }
friend ByteBuffer& operator>>(ByteBuffer& data, String& value)
{
value._storage = data.ReadCString(false);
value.Validate();
return data;
}
private:
bool Validate() const
{
return ValidateNth(std::make_index_sequence<std::tuple_size_v<ValidatorList>>{});
}
template<std::size_t... indexes>
bool ValidateNth(std::index_sequence<indexes...>) const
{
return (std::tuple_element_t<indexes, ValidatorList>::Validate(_storage) && ...);
}
std::string _storage;
};
class PacketArrayMaxCapacityException : public ByteBufferException
{
public:
PacketArrayMaxCapacityException(std::size_t requestedSize, std::size_t sizeLimit);
};
/**
* Utility class for automated prevention of loop counter spoofing in client packets
*/
template<typename T, std::size_t N = 1000 /*select a sane default limit*/>
class Array
{
typedef std::vector<T> storage_type;
typedef typename storage_type::value_type value_type;
typedef typename storage_type::size_type size_type;
typedef typename storage_type::reference reference;
typedef typename storage_type::const_reference const_reference;
typedef typename storage_type::iterator iterator;
typedef typename storage_type::const_iterator const_iterator;
public:
Array() : _limit(N) { }
Array(size_type limit) : _limit(limit) { }
iterator begin() { return _storage.begin(); }
const_iterator begin() const { return _storage.begin(); }
iterator end() { return _storage.end(); }
const_iterator end() const { return _storage.end(); }
size_type size() const { return _storage.size(); }
bool empty() const { return _storage.empty(); }
reference operator[](size_type i) { return _storage[i]; }
const_reference operator[](size_type i) const { return _storage[i]; }
void resize(size_type newSize)
{
if (newSize > _limit)
{
throw PacketArrayMaxCapacityException(newSize, _limit);
}
_storage.resize(newSize);
}
void reserve(size_type newSize)
{
if (newSize > _limit)
{
throw PacketArrayMaxCapacityException(newSize, _limit);
}
_storage.reserve(newSize);
}
void push_back(value_type const& value)
{
if (_storage.size() >= _limit)
{
throw PacketArrayMaxCapacityException(_storage.size() + 1, _limit);
}
_storage.push_back(value);
}
void push_back(value_type&& value)
{
if (_storage.size() >= _limit)
{
throw PacketArrayMaxCapacityException(_storage.size() + 1, _limit);
}
_storage.push_back(std::forward<value_type>(value));
}
private:
storage_type _storage;
size_type _limit;
};
void CheckCompactArrayMaskOverflow(std::size_t index, std::size_t limit);
template <typename T>
class CompactArray
{
public:
CompactArray() : _mask(0) { }
CompactArray(CompactArray const& right)
: _mask(right._mask), _contents(right._contents) { }
CompactArray(CompactArray&& right)
: _mask(right._mask), _contents(std::move(right._contents))
{
right._mask = 0;
}
CompactArray& operator=(CompactArray const& right)
{
_mask = right._mask;
_contents = right._contents;
return *this;
}
CompactArray& operator=(CompactArray&& right)
{
_mask = right._mask;
right._mask = 0;
_contents = std::move(right._contents);
return *this;
}
uint32 GetMask() const { return _mask; }
T const& operator[](std::size_t index) const { return _contents.at(index); }
std::size_t GetSize() const { return _contents.size(); }
void Insert(std::size_t index, T const& value)
{
CheckCompactArrayMaskOverflow(index, sizeof(_mask) * 8);
_mask |= 1 << index;
if (_contents.size() <= index)
{
_contents.resize(index + 1);
}
_contents[index] = value;
}
void Clear()
{
_mask = 0;
_contents.clear();
}
bool operator==(CompactArray const& r) const
{
if (_mask != r._mask)
{
return false;
}
return _contents == r._contents;
}
bool operator!=(CompactArray const& r) const { return !(*this == r); }
private:
uint32 _mask;
std::vector<T> _contents;
};
template <typename T>
ByteBuffer& operator<<(ByteBuffer& data, CompactArray<T> const& v)
{
uint32 mask = v.GetMask();
data << uint32(mask);
for (std::size_t i = 0; i < v.GetSize(); ++i)
{
if (mask & (1 << i))
{
data << v[i];
}
}
return data;
}
template <typename T>
ByteBuffer& operator>>(ByteBuffer& data, CompactArray<T>& v)
{
uint32 mask;
data >> mask;
for (std::size_t index = 0; mask != 0; mask >>= 1, ++index)
{
if ((mask & 1) != 0)
{
v.Insert(index, data.read<T>());
}
}
return data;
}
}
#endif // PacketUtilities_h__

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,6 @@
/*
* 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/>
* 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) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
/// \addtogroup u2w
@@ -11,12 +10,12 @@
#ifndef _OPCODES_H
#define _OPCODES_H
#include "Common.h"
#include "Define.h"
#include <string>
/// List of Opcodes
enum Opcodes
enum Opcodes : uint16
{
MSG_NULL_ACTION = 0x000,
CMSG_BOOTME = 0x001,
CMSG_DBLOOKUP = 0x002,
SMSG_DBLOOKUP = 0x003,
@@ -1330,6 +1329,15 @@ enum Opcodes
NUM_MSG_TYPES = 0x51F
};
enum OpcodeMisc : uint16
{
NUM_OPCODE_HANDLERS = NUM_MSG_TYPES,
NULL_OPCODE = 0x0000
};
typedef Opcodes OpcodeClient;
typedef Opcodes OpcodeServer;
/// Player state
enum SessionStatus
{
@@ -1350,34 +1358,63 @@ enum PacketProcessing
class WorldSession;
class WorldPacket;
#if defined(__GNUC__)
#pragma pack(1)
#else
#pragma pack(push, 1)
#endif
struct OpcodeHandler
class OpcodeHandler
{
char const* name;
SessionStatus status;
PacketProcessing packetProcessing;
void (WorldSession::*handler)(WorldPacket& recvPacket);
public:
OpcodeHandler(char const* name, SessionStatus status) : Name(name), Status(status) { }
virtual ~OpcodeHandler() { }
char const* Name;
SessionStatus Status;
};
extern OpcodeHandler opcodeTable[NUM_MSG_TYPES];
class ClientOpcodeHandler : public OpcodeHandler
{
public:
ClientOpcodeHandler(char const* name, SessionStatus status, PacketProcessing processing)
: OpcodeHandler(name, status), ProcessingPlace(processing) { }
#if defined(__GNUC__)
#pragma pack()
#else
#pragma pack(pop)
#endif
virtual void Call(WorldSession* session, WorldPacket& packet) const = 0;
PacketProcessing ProcessingPlace;
};
class ServerOpcodeHandler : public OpcodeHandler
{
public:
ServerOpcodeHandler(char const* name, SessionStatus status)
: OpcodeHandler(name, status) { }
};
class OpcodeTable
{
public:
OpcodeTable();
OpcodeTable(OpcodeTable const&) = delete;
OpcodeTable& operator=(OpcodeTable const&) = delete;
~OpcodeTable();
void Initialize();
ClientOpcodeHandler const* operator[](Opcodes index) const
{
return _internalTableClient[index];
}
private:
template<typename Handler, Handler HandlerFunction>
void ValidateAndSetClientOpcode(OpcodeClient opcode, char const* name, SessionStatus status, PacketProcessing processing);
void ValidateAndSetServerOpcode(OpcodeServer opcode, char const* name, SessionStatus status);
ClientOpcodeHandler* _internalTableClient[NUM_OPCODE_HANDLERS];
};
extern OpcodeTable opcodeTable;
/// Lookup opcode name for human understandable logging
inline const char* LookupOpcodeName(uint16 id)
{
if (id >= NUM_MSG_TYPES)
return "Received unknown opcode, it's more than max!";
return opcodeTable[id].name;
}
std::string GetOpcodeNameForLogging(Opcodes opcode);
#endif
/// @}

View File

@@ -0,0 +1,74 @@
/*
* 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 _WORLDPACKET_H_
#define _WORLDPACKET_H_
#include "Common.h"
#include "Opcodes.h"
#include "ByteBuffer.h"
#include "Duration.h"
class WorldPacket : public ByteBuffer
{
public:
// just container for later use
WorldPacket() : ByteBuffer(0), m_opcode(NULL_OPCODE) { }
explicit WorldPacket(uint16 opcode, size_t res = 200) :
ByteBuffer(res), m_opcode(opcode) { }
WorldPacket(WorldPacket&& packet) noexcept :
ByteBuffer(std::move(packet)), m_opcode(packet.m_opcode) { }
WorldPacket(WorldPacket&& packet, TimePoint receivedTime) :
ByteBuffer(std::move(packet)), m_opcode(packet.m_opcode), m_receivedTime(receivedTime) { }
WorldPacket(WorldPacket const& right) :
ByteBuffer(right), m_opcode(right.m_opcode) { }
WorldPacket& operator=(WorldPacket const& right)
{
if (this != &right)
{
m_opcode = right.m_opcode;
ByteBuffer::operator=(right);
}
return *this;
}
WorldPacket& operator=(WorldPacket&& right) noexcept
{
if (this != &right)
{
m_opcode = right.m_opcode;
ByteBuffer::operator=(std::move(right));
}
return *this;
}
WorldPacket(uint16 opcode, MessageBuffer&& buffer) :
ByteBuffer(std::move(buffer)), m_opcode(opcode) { }
void Initialize(uint16 opcode, size_t newres = 200)
{
clear();
_storage.reserve(newres);
m_opcode = opcode;
}
[[nodiscard]] uint16 GetOpcode() const { return m_opcode; }
void SetOpcode(uint16 opcode) { m_opcode = opcode; }
[[nodiscard]] TimePoint GetReceivedTime() const { return m_receivedTime; }
protected:
uint16 m_opcode;
TimePoint m_receivedTime; // only set for a specific set of opcodes, for performance reasons.
};
#endif

View File

@@ -22,6 +22,7 @@
#include "ObjectMgr.h"
#include "Opcodes.h"
#include "OutdoorPvPMgr.h"
#include "PacketUtilities.h"
#include "Pet.h"
#include "Player.h"
#include "SavingSystem.h"
@@ -42,48 +43,49 @@
namespace
{
std::string const DefaultPlayerName = "<none>";
} // namespace
}
bool MapSessionFilter::Process(WorldPacket* packet)
{
if (packet->GetOpcode() >= NUM_MSG_TYPES)
ClientOpcodeHandler const* opHandle = opcodeTable[static_cast<OpcodeClient>(packet->GetOpcode())];
//let's check if our opcode can be really processed in Map::Update()
if (opHandle->ProcessingPlace == PROCESS_INPLACE)
return true;
OpcodeHandler const& opHandle = opcodeTable[packet->GetOpcode()];
if (opHandle.packetProcessing == PROCESS_INPLACE)
return true;
if (opHandle.packetProcessing == PROCESS_THREADUNSAFE)
//we do not process thread-unsafe packets
if (opHandle->ProcessingPlace == PROCESS_THREADUNSAFE)
return false;
Player* player = m_pSession->GetPlayer();
if (!player)
return false;
//in Map::Update() we do not process packets where player is not in world!
return player->IsInWorld();
}
//we should process ALL packets when player is not in world/logged in
//OR packet handler is not thread-safe!
bool WorldSessionFilter::Process(WorldPacket* packet)
{
if (packet->GetOpcode() >= NUM_MSG_TYPES)
ClientOpcodeHandler const* opHandle = opcodeTable[static_cast<OpcodeClient>(packet->GetOpcode())];
//check if packet handler is supposed to be safe
if (opHandle->ProcessingPlace == PROCESS_INPLACE)
return true;
OpcodeHandler const& opHandle = opcodeTable[packet->GetOpcode()];
if (opHandle.packetProcessing == PROCESS_INPLACE)
return true;
if (opHandle.packetProcessing == PROCESS_THREADUNSAFE)
//thread-unsafe packets should be processed in World::UpdateSessions()
if (opHandle->ProcessingPlace == PROCESS_THREADUNSAFE)
return true;
//no player attached? -> our client! ^^
Player* player = m_pSession->GetPlayer();
if (!player)
return true;
//lets process all packets for non-in-the-world player
return (player->IsInWorld() == false);
}
@@ -198,6 +200,8 @@ ObjectGuid::LowType WorldSession::GetGuidLow() const
/// Send a packet to the client
void WorldSession::SendPacket(WorldPacket const* packet)
{
ASSERT(packet->GetOpcode() != NULL_OPCODE);
if (!m_Socket)
return;
@@ -254,6 +258,25 @@ void WorldSession::QueuePacket(WorldPacket* new_packet)
_recvQueue.add(new_packet);
}
/// Logging helper for unexpected opcodes
void WorldSession::LogUnexpectedOpcode(WorldPacket* packet, char const* status, const char* reason)
{
LOG_ERROR("network.opcode", "Received unexpected opcode %s Status: %s Reason: %s from %s",
GetOpcodeNameForLogging(static_cast<OpcodeClient>(packet->GetOpcode())).c_str(), status, reason, GetPlayerInfo().c_str());
}
/// Logging helper for unexpected opcodes
void WorldSession::LogUnprocessedTail(WorldPacket* packet)
{
if (!sLog->ShouldLog("network.opcode", LogLevel::LOG_LEVEL_TRACE) || packet->rpos() >= packet->wpos())
return;
LOG_TRACE("network.opcode", "Unprocessed tail data (read stop at %u from %u) Opcode %s from %s",
uint32(packet->rpos()), uint32(packet->wpos()), GetOpcodeNameForLogging(static_cast<OpcodeClient>(packet->GetOpcode())).c_str(), GetPlayerInfo().c_str());
packet->print_storage();
}
/// Update the WorldSession (triggered by World update)
bool WorldSession::Update(uint32 diff, PacketFilter& updater)
{
@@ -271,7 +294,6 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
uint32 _startMSTime = getMSTime();
WorldPacket* packet = nullptr;
WorldPacket* movementPacket = nullptr;
bool deletePacket = true;
WorldPacket* firstDelayedPacket = nullptr;
uint32 processedPackets = 0;
@@ -279,96 +301,92 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
while (m_Socket && !m_Socket->IsClosed() && !_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket && _recvQueue.next(packet, updater))
{
if (packet->GetOpcode() >= NUM_MSG_TYPES)
OpcodeClient opcode = static_cast<OpcodeClient>(packet->GetOpcode());
ClientOpcodeHandler const* opHandle = opcodeTable[opcode];
try
{
LOG_ERROR("server", "WorldSession Packet filter: received non-existent opcode %s (0x%.4X)", LookupOpcodeName(packet->GetOpcode()), packet->GetOpcode());
}
else
{
OpcodeHandler& opHandle = opcodeTable[packet->GetOpcode()];
try
switch (opHandle->Status)
{
switch (opHandle.status)
case STATUS_LOGGEDIN:
if (!_player)
{
case STATUS_LOGGEDIN:
if (!_player)
{
// pussywizard: such packets were sent to do something for a character that has already logged out, skip them
}
else if (!_player->IsInWorld())
{
// pussywizard: such packets may do something important and the player is just being teleported, move to the end of the queue
// pussywizard: previously such were skipped, so leave it as it is xD proper code below if we wish to change that
// pussywizard: requeue only important packets not related to maps (PROCESS_THREADUNSAFE)
/*if (opHandle.packetProcessing == PROCESS_THREADUNSAFE)
{
if (!firstDelayedPacket)
firstDelayedPacket = packet;
deletePacket = false;
QueuePacket(packet);
}*/
}
else if (_player->IsInWorld() && AntiDOS.EvaluateOpcode(*packet, currentTime))
{
if (movementPacket)
{
HandleMovementOpcodes(*movementPacket);
delete movementPacket;
movementPacket = nullptr;
}
sScriptMgr->OnPacketReceive(this, *packet);
#ifdef ELUNA
if (!sEluna->OnPacketReceive(this, *packet))
break;
#endif
(this->*opHandle.handler)(*packet);
}
break;
case STATUS_TRANSFER:
if (_player && !_player->IsInWorld() && AntiDOS.EvaluateOpcode(*packet, currentTime))
{
if (movementPacket)
{
delete movementPacket;
movementPacket = nullptr;
}
sScriptMgr->OnPacketReceive(this, *packet);
#ifdef ELUNA
if (!sEluna->OnPacketReceive(this, *packet))
break;
#endif
(this->*opHandle.handler)(*packet);
}
break;
case STATUS_AUTHED:
if (m_inQueue) // prevent cheating
break;
if (AntiDOS.EvaluateOpcode(*packet, currentTime))
{
sScriptMgr->OnPacketReceive(this, *packet);
#ifdef ELUNA
if (!sEluna->OnPacketReceive(this, *packet))
break;
#endif
(this->*opHandle.handler)(*packet);
}
break;
case STATUS_NEVER:
break;
case STATUS_UNHANDLED:
break;
// pussywizard: such packets were sent to do something for a character that has already logged out, skip them
}
else if (!_player->IsInWorld())
{
// pussywizard: such packets may do something important and the player is just being teleported, move to the end of the queue
// pussywizard: previously such were skipped, so leave it as it is xD proper code below if we wish to change that
// pussywizard: requeue only important packets not related to maps (PROCESS_THREADUNSAFE)
/*if (opHandle.packetProcessing == PROCESS_THREADUNSAFE)
{
if (!firstDelayedPacket)
firstDelayedPacket = packet;
deletePacket = false;
QueuePacket(packet);
}*/
}
else if (_player->IsInWorld() && AntiDOS.EvaluateOpcode(*packet, currentTime))
{
sScriptMgr->OnPacketReceive(this, *packet);
#ifdef ELUNA
if (!sEluna->OnPacketReceive(this, *packet))
break;
#endif
opHandle->Call(this, *packet);
LogUnprocessedTail(packet);
}
break;
case STATUS_TRANSFER:
if (_player && !_player->IsInWorld() && AntiDOS.EvaluateOpcode(*packet, currentTime))
{
sScriptMgr->OnPacketReceive(this, *packet);
#ifdef ELUNA
if (!sEluna->OnPacketReceive(this, *packet))
break;
#endif
opHandle->Call(this, *packet);
LogUnprocessedTail(packet);
}
break;
case STATUS_AUTHED:
if (m_inQueue) // prevent cheating
break;
if (AntiDOS.EvaluateOpcode(*packet, currentTime))
{
sScriptMgr->OnPacketReceive(this, *packet);
#ifdef ELUNA
if (!sEluna->OnPacketReceive(this, *packet))
break;
#endif
opHandle->Call(this, *packet);
LogUnprocessedTail(packet);
}
break;
case STATUS_NEVER:
LOG_ERROR("network.opcode", "Received not allowed opcode %s from %s",
GetOpcodeNameForLogging(static_cast<OpcodeClient>(packet->GetOpcode())).c_str(), GetPlayerInfo().c_str());
break;
case STATUS_UNHANDLED:
LOG_DEBUG("network.opcode", "Received not handled opcode %s from %s",
GetOpcodeNameForLogging(static_cast<OpcodeClient>(packet->GetOpcode())).c_str(), GetPlayerInfo().c_str());
break;
}
catch (ByteBufferException const&)
}
catch (WorldPackets::PacketArrayMaxCapacityException const& pamce)
{
LOG_ERROR("network", "PacketArrayMaxCapacityException: %s while parsing %s from %s.",
pamce.what(), GetOpcodeNameForLogging(static_cast<OpcodeClient>(packet->GetOpcode())).c_str(), GetPlayerInfo().c_str());
}
catch (ByteBufferException const&)
{
LOG_ERROR("server", "WorldSession::Update ByteBufferException occured while parsing a packet (opcode: %u) from client %s, accountid=%i. Skipped packet.", packet->GetOpcode(), GetRemoteAddress().c_str(), GetAccountId());
if (sLog->ShouldLog("network", LogLevel::LOG_LEVEL_DEBUG))
{
LOG_ERROR("server", "WorldSession::Update ByteBufferException occured while parsing a packet (opcode: %u) from client %s, accountid=%i. Skipped packet.", packet->GetOpcode(), GetRemoteAddress().c_str(), GetAccountId());
if (sLog->ShouldLog("network", LogLevel::LOG_LEVEL_DEBUG))
{
LOG_DEBUG("network", "Dumping error causing packet:");
packet->hexlike();
}
LOG_DEBUG("network", "Dumping error causing packet:");
packet->hexlike();
}
}
@@ -377,20 +395,18 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
deletePacket = true;
if (++processedPackets >= 150) // limit (by count) packets processed in one update, prevent DDoS
#define MAX_PROCESSED_PACKETS_IN_SAME_WORLDSESSION_UPDATE 100
processedPackets++;
//process only a max amout of packets in 1 Update() call.
//Any leftover will be processed in next update
if (processedPackets > MAX_PROCESSED_PACKETS_IN_SAME_WORLDSESSION_UPDATE)
break;
if (getMSTimeDiff(_startMSTime, getMSTime()) >= 3) // limit (by time) packets processed in one update, prevent DDoS
break;
}
if (movementPacket)
{
if (_player && _player->IsInWorld())
HandleMovementOpcodes(*movementPacket);
delete movementPacket;
}
if (m_Socket && !m_Socket->IsClosed())
ProcessQueryCallbacks();
@@ -464,7 +480,7 @@ void WorldSession::HandleTeleportTimeout(bool updateInSessions)
{
if (GetPlayer()->IsBeingTeleportedFar() && GetPlayer()->GetSemaphoreTeleportFar() + sWorld->getIntConfig(CONFIG_TELEPORT_TIMEOUT_FAR) < currTime)
while (GetPlayer() && GetPlayer()->IsBeingTeleportedFar())
HandleMoveWorldportAckOpcode();
HandleMoveWorldportAck();
}
else // session update from Map::Update
{
@@ -489,7 +505,7 @@ void WorldSession::LogoutPlayer(bool save)
{
// finish pending transfers before starting the logout
while (_player && _player->IsBeingTeleportedFar())
HandleMoveWorldportAckOpcode();
HandleMoveWorldportAck();
m_playerLogout = true;
m_playerSave = save;
@@ -577,7 +593,7 @@ void WorldSession::LogoutPlayer(bool save)
// Repop at GraveYard or other player far teleport will prevent saving player because of not present map
// Teleport player immediately for correct player save
while (_player && _player->IsBeingTeleportedFar())
HandleMoveWorldportAckOpcode();
HandleMoveWorldportAck();
///- empty buyback items and save the player in the database
// some save parts only correctly work in case player present in map/player_lists (pets, etc)
@@ -703,24 +719,28 @@ char const* WorldSession::GetAcoreString(uint32 entry) const
return sObjectMgr->GetAcoreString(entry, GetSessionDbLocaleIndex());
}
void WorldSession::Handle_NULL(WorldPacket& recvPacket)
void WorldSession::Handle_NULL(WorldPacket& null)
{
LOG_ERROR("server", "SESSION: received unhandled opcode %s (0x%.4X)", LookupOpcodeName(recvPacket.GetOpcode()), recvPacket.GetOpcode());
LOG_ERROR("network.opcode", "Received unhandled opcode %s from %s",
GetOpcodeNameForLogging(static_cast<OpcodeClient>(null.GetOpcode())).c_str(), GetPlayerInfo().c_str());
}
void WorldSession::Handle_EarlyProccess(WorldPacket& recvPacket)
{
LOG_ERROR("server", "SESSION: received opcode %s (0x%.4X) that must be processed in WorldSocket::OnRead", LookupOpcodeName(recvPacket.GetOpcode()), recvPacket.GetOpcode());
LOG_ERROR("network.opcode", "Received opcode %s that must be processed in WorldSocket::ReadDataHandler from %s",
GetOpcodeNameForLogging(static_cast<OpcodeClient>(recvPacket.GetOpcode())).c_str(), GetPlayerInfo().c_str());
}
void WorldSession::Handle_ServerSide(WorldPacket& recvPacket)
{
LOG_ERROR("server", "SESSION: received server-side opcode %s (0x%.4X)", LookupOpcodeName(recvPacket.GetOpcode()), recvPacket.GetOpcode());
LOG_ERROR("network.opcode", "Received server-side opcode %s from %s",
GetOpcodeNameForLogging(static_cast<OpcodeServer>(recvPacket.GetOpcode())).c_str(), GetPlayerInfo().c_str());
}
void WorldSession::Handle_Deprecated(WorldPacket& recvPacket)
{
LOG_ERROR("server", "SESSION: received deprecated opcode %s (0x%.4X)", LookupOpcodeName(recvPacket.GetOpcode()), recvPacket.GetOpcode());
LOG_ERROR("network.opcode", "Received deprecated opcode %s from %s",
GetOpcodeNameForLogging(static_cast<OpcodeClient>(recvPacket.GetOpcode())).c_str(), GetPlayerInfo().c_str());
}
void WorldSession::SendAuthWaitQue(uint32 position)
@@ -1392,9 +1412,9 @@ bool WorldSession::DosProtection::EvaluateOpcode(WorldPacket& p, time_t time) co
if (++packetCounter.amountCounter <= maxPacketCounterAllowed)
return true;
LOG_INFO("server", "AntiDOS: Account %u, IP: %s, Ping: %u, Character %s, flooding packet (opc: %s (0x%X), count: %u)",
Session->GetAccountId(), Session->GetRemoteAddress().c_str(), Session->GetLatency(),
Session->GetPlayerName().c_str(), opcodeTable[p.GetOpcode()].name, p.GetOpcode(), packetCounter.amountCounter);
LOG_WARN("network", "AntiDOS: Account %u, IP: %s, Ping: %u, Character: %s, flooding packet (opc: %s (0x%X), count: %u)",
Session->GetAccountId(), Session->GetRemoteAddress().c_str(), Session->GetLatency(), Session->GetPlayerName().c_str(),
opcodeTable[static_cast<OpcodeClient>(p.GetOpcode())]->Name, p.GetOpcode(), packetCounter.amountCounter);
switch (_policy)
{
@@ -1402,7 +1422,7 @@ bool WorldSession::DosProtection::EvaluateOpcode(WorldPacket& p, time_t time) co
return true;
case POLICY_KICK:
{
LOG_INFO("server", "AntiDOS: Player %s kicked!", Session->GetPlayerName().c_str());
LOG_INFO("network", "AntiDOS: Player %s kicked!", Session->GetPlayerName().c_str());
Session->KickPlayer();
return false;
}
@@ -1423,7 +1443,7 @@ bool WorldSession::DosProtection::EvaluateOpcode(WorldPacket& p, time_t time) co
break;
}
LOG_INFO("server", "AntiDOS: Player automatically banned for %u seconds.", duration);
LOG_INFO("network", "AntiDOS: Player automatically banned for %u seconds.", duration);
return false;
}
default: // invalid policy

View File

@@ -19,10 +19,9 @@
#include "Common.h"
#include "DatabaseEnv.h"
#include "GossipDef.h"
#include "Opcodes.h"
#include "Packet.h"
#include "SharedDefines.h"
#include "World.h"
#include "WorldPacket.h"
#include <utility>
#include <map>
@@ -59,6 +58,10 @@ namespace lfg
struct LfgUpdateData;
}
namespace WorldPackets
{
}
enum AccountDataType
{
GLOBAL_CONFIG_CACHE = 0, // 0x01 g
@@ -392,7 +395,7 @@ public:
void ResetTimeSync();
void SendTimeSync();
public: // opcodes handlers
void Handle_NULL(WorldPacket& recvPacket); // not used
void Handle_NULL(WorldPacket& null); // not used
void Handle_EarlyProccess(WorldPacket& recvPacket); // just mark packets processed in WorldSocket::OnRead
void Handle_ServerSide(WorldPacket& recvPacket); // sever side only, can't be accepted from client
void Handle_Deprecated(WorldPacket& recvPacket); // never used anymore by client
@@ -501,7 +504,7 @@ public: // opcodes handlers
void HandleGameObjectQueryOpcode(WorldPacket& recvPacket);
void HandleMoveWorldportAckOpcode(WorldPacket& recvPacket);
void HandleMoveWorldportAckOpcode(); // for server-side calls
void HandleMoveWorldportAck(); // for server-side calls
void HandleMovementOpcodes(WorldPacket& recvPacket);
void HandleSetActiveMoverOpcode(WorldPacket& recvData);
@@ -998,6 +1001,10 @@ private:
bool recoveryItem(Item* pItem);
// logging helper
void LogUnexpectedOpcode(WorldPacket* packet, char const* status, const char* reason);
void LogUnprocessedTail(WorldPacket* packet);
// EnumData helpers
bool IsLegitCharacterForAccount(ObjectGuid guid)
{

View File

@@ -527,11 +527,10 @@ int WorldSocket::handle_input_header(void)
EndianConvertReverse(header.size);
EndianConvert(header.cmd);
if ((header.size < 4) || (header.size > 10240) || (header.cmd > 10240))
if ((header.size < 4) || (header.size > 10240) || (header.cmd > 10240))
{
Player* _player = m_Session ? m_Session->GetPlayer() : nullptr;
LOG_ERROR("server", "WorldSocket::handle_input_header(): client (account: %u, char [%s, name: %s]) sent malformed packet (size: %d, cmd: %d)",
m_Session ? m_Session->GetAccountId() : 0, _player ? _player->GetGUID().ToString().c_str() : "", _player ? _player->GetName().c_str() : "<none>", header.size, header.cmd);
LOG_ERROR("server", "WorldSocket::handle_input_header(): client (%s) sent malformed packet (size: %hd, cmd: %d)",
GetRemoteAddress().c_str(), header.size, header.cmd);
errno = EINVAL;
return -1;
@@ -706,9 +705,9 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct)
ASSERT(new_pct);
// manage memory ;)
std::unique_ptr<WorldPacket> aptr (new_pct);
std::unique_ptr<WorldPacket> aptr(new_pct);
const uint16 opcode = new_pct->GetOpcode();
OpcodeClient opcode = static_cast<OpcodeClient>(aptr->GetOpcode());
if (closing_)
return -1;
@@ -749,7 +748,9 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct)
}
catch (ByteBufferException const&)
{
LOG_ERROR("server", "WorldSocket::ProcessIncoming ByteBufferException occured while parsing an instant handled packet (opcode: %u) from client %s, accountid=%i. Disconnected client.", opcode, GetRemoteAddress().c_str(), m_Session ? m_Session->GetAccountId() : -1);
LOG_ERROR("server", "WorldSocket::ProcessIncoming ByteBufferException occured while parsing an instant handled packet (opcode: %u) from client %s, accountid=%u. Disconnected client.",
aptr->GetOpcode(), GetRemoteAddress().c_str(), m_Session ? m_Session->GetAccountId() : 0);
if (sLog->ShouldLog("network", LogLevel::LOG_LEVEL_DEBUG))
{
LOG_DEBUG("network", "Dumping error causing packet:");
@@ -761,6 +762,13 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct)
std::lock_guard<std::mutex> guard(m_SessionLock);
OpcodeHandler const* handler = opcodeTable[opcode];
if (!handler)
{
LOG_ERROR("network.opcode", "No defined handler for opcode %s sent by %s", GetOpcodeNameForLogging(static_cast<OpcodeClient>(aptr->GetOpcode())).c_str(), m_Session->GetPlayerInfo().c_str());
return -1;
}
if (m_Session != nullptr)
{
// Our Idle timer will reset on any non PING or TIME_SYNC opcodes.
@@ -776,7 +784,7 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct)
return 0;
}
LOG_ERROR("server", "WorldSocket::ProcessIncoming: Client not authed opcode = %u", uint32(opcode));
LOG_ERROR("server", "WorldSocket::ProcessIncoming: Client not authed opcode = %u", aptr->GetOpcode());
return -1;
}

View File

@@ -31,6 +31,11 @@ class ACE_Message_Block;
class WorldPacket;
class WorldSession;
namespace WorldPackets
{
class ServerPacket;
}
/// Handler that can communicate over stream sockets.
typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> WorldHandler;