mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-21 20:56:23 +00:00
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:
@@ -26,6 +26,7 @@
|
||||
#include "DatabaseLoader.h"
|
||||
#include "SecretMgr.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "Util.h"
|
||||
#include <ace/Dev_Poll_Reactor.h>
|
||||
#include <ace/TP_Reactor.h>
|
||||
#include <ace/ACE.h>
|
||||
|
||||
@@ -818,32 +818,6 @@ void Channel::Say(ObjectGuid guid, std::string const& what, uint32 lang)
|
||||
SendToAll(&data, pinfo.IsModerator() ? ObjectGuid::Empty : guid);
|
||||
}
|
||||
|
||||
void Channel::EveryoneSayToSelf(const char* what)
|
||||
{
|
||||
if (!what)
|
||||
return;
|
||||
|
||||
uint32 messageLength = strlen(what) + 1;
|
||||
|
||||
WorldPacket data(SMSG_MESSAGECHAT, 1 + 4 + 8 + 4 + _name.size() + 1 + 8 + 4 + messageLength + 1);
|
||||
data << (uint8)CHAT_MSG_CHANNEL;
|
||||
data << (uint32)LANG_UNIVERSAL;
|
||||
data << uint64(0); // put player guid here
|
||||
data << uint32(0);
|
||||
data << _name;
|
||||
data << uint64(0); // put player guid here
|
||||
data << messageLength;
|
||||
data << what;
|
||||
data << uint8(0);
|
||||
|
||||
for (PlayerContainer::const_iterator i = playersStore.begin(); i != playersStore.end(); ++i)
|
||||
{
|
||||
data.put(5, i->first);
|
||||
data.put(17 + _name.size() + 1, i->first);
|
||||
i->second.plrPtr->GetSession()->SendPacket(&data);
|
||||
}
|
||||
}
|
||||
|
||||
void Channel::Invite(Player const* player, std::string const& newname)
|
||||
{
|
||||
ObjectGuid guid = player->GetGUID();
|
||||
|
||||
@@ -209,7 +209,6 @@ public:
|
||||
void List(Player const* player);
|
||||
void Announce(Player const* player);
|
||||
void Say(ObjectGuid guid, std::string const& what, uint32 lang);
|
||||
void EveryoneSayToSelf(const char* what);
|
||||
void Invite(Player const* player, std::string const& newp);
|
||||
void Voice(ObjectGuid guid1, ObjectGuid guid2);
|
||||
void DeVoice(ObjectGuid guid1, ObjectGuid guid2);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#define AZEROTHCORE_CHAT_H
|
||||
|
||||
#include "SharedDefines.h"
|
||||
#include "Errors.h"
|
||||
#include "WorldSession.h"
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#include "UpdateData.h"
|
||||
#include "ByteBuffer.h"
|
||||
#include "Common.h"
|
||||
#include "Errors.h"
|
||||
#include "Log.h"
|
||||
#include "Opcodes.h"
|
||||
#include "UpdateData.h"
|
||||
#include "World.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "zlib.h"
|
||||
|
||||
@@ -538,8 +538,6 @@ void acore::LocalizedPacketDo<Builder>::operator()(Player* p)
|
||||
|
||||
i_builder(*data, loc_idx);
|
||||
|
||||
ASSERT(data->GetOpcode() != MSG_NULL_ACTION);
|
||||
|
||||
i_data_cache[cache_idx] = data;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -826,7 +826,7 @@ void WorldSession::HandlePlayerLoginOpcode(WorldPacket& recvData)
|
||||
LOG_INFO("misc", "HandlePlayerLoginOpcode B");
|
||||
break;
|
||||
}
|
||||
sess->HandleMoveWorldportAckOpcode();
|
||||
sess->HandleMoveWorldportAck();
|
||||
}
|
||||
while (sess->GetPlayer() && sess->GetPlayer()->IsInWorld() && sess->GetPlayer()->IsBeingTeleportedNear())
|
||||
{
|
||||
|
||||
@@ -36,10 +36,10 @@ void WorldSession::HandleMoveWorldportAckOpcode(WorldPacket& /*recvData*/)
|
||||
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
||||
LOG_DEBUG("network", "WORLD: got MSG_MOVE_WORLDPORT_ACK.");
|
||||
#endif
|
||||
HandleMoveWorldportAckOpcode();
|
||||
HandleMoveWorldportAck();
|
||||
}
|
||||
|
||||
void WorldSession::HandleMoveWorldportAckOpcode()
|
||||
void WorldSession::HandleMoveWorldportAck()
|
||||
{
|
||||
// ignore unexpected far teleports
|
||||
if (!GetPlayer()->IsBeingTeleportedFar())
|
||||
@@ -576,7 +576,7 @@ void WorldSession::HandleForceSpeedChangeAck(WorldPacket& recvData)
|
||||
{
|
||||
uint32 opcode = recvData.GetOpcode();
|
||||
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
||||
LOG_DEBUG("network", "WORLD: Recvd %s (%u, 0x%X) opcode", LookupOpcodeName(opcode), opcode, opcode);
|
||||
LOG_DEBUG("network", "WORLD: Recvd %s (%u, 0x%X) opcode", GetOpcodeNameForLogging(static_cast<OpcodeClient>(opcode)).c_str(), opcode, opcode);
|
||||
#endif
|
||||
|
||||
/* extract packet */
|
||||
|
||||
37
src/server/game/Server/Packet.cpp
Normal file
37
src/server/game/Server/Packet.cpp
Normal 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;
|
||||
}
|
||||
59
src/server/game/Server/Packet.h
Normal file
59
src/server/game/Server/Packet.h
Normal 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__
|
||||
9
src/server/game/Server/Packets/AllPackets.h
Normal file
9
src/server/game/Server/Packets/AllPackets.h
Normal 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__
|
||||
59
src/server/game/Server/Packets/PacketUtilities.cpp
Normal file
59
src/server/game/Server/Packets/PacketUtilities.cpp
Normal 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);
|
||||
}
|
||||
286
src/server/game/Server/Packets/PacketUtilities.h
Normal file
286
src/server/game/Server/Packets/PacketUtilities.h
Normal 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
@@ -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
|
||||
/// @}
|
||||
|
||||
74
src/server/game/Server/WorldPacket.h
Normal file
74
src/server/game/Server/WorldPacket.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -136,7 +136,6 @@ public:
|
||||
{
|
||||
messageTemplate = new WorldPacket();
|
||||
whisperGUIDpos = _builder(messageTemplate, loc_idx);
|
||||
ASSERT(messageTemplate->GetOpcode() != MSG_NULL_ACTION);
|
||||
_packetCache[loc_idx] = new std::pair<WorldPacket*, size_t>(messageTemplate, whisperGUIDpos);
|
||||
}
|
||||
else
|
||||
@@ -150,7 +149,7 @@ public:
|
||||
{
|
||||
case CHAT_MSG_MONSTER_WHISPER:
|
||||
case CHAT_MSG_RAID_BOSS_WHISPER:
|
||||
data.put<ObjectGuid>(whisperGUIDpos, player->GetGUID());
|
||||
data.put<uint64>(whisperGUIDpos, player->GetGUID().GetRawValue());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -1978,6 +1978,9 @@ void World::SetInitialWorldSettings()
|
||||
// Delete all custom channels which haven't been used for PreserveCustomChannelDuration days.
|
||||
Channel::CleanOldChannelsInDB();
|
||||
|
||||
LOG_INFO("server.loading", "Initializing Opcodes...");
|
||||
opcodeTable.Initialize();
|
||||
|
||||
LOG_INFO("server", "Starting Arena Season...");
|
||||
LOG_INFO("server", " ");
|
||||
sGameEventMgr->StartArenaSeason();
|
||||
|
||||
@@ -422,7 +422,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
data.hexlike(true);
|
||||
data.hexlike();
|
||||
player->GetSession()->SendPacket(&data);
|
||||
handler->PSendSysMessage(LANG_COMMAND_OPCODESENT, data.GetOpcode(), unit->GetName().c_str());
|
||||
return true;
|
||||
|
||||
211
src/server/shared/Packets/ByteBuffer.cpp
Normal file
211
src/server/shared/Packets/ByteBuffer.cpp
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* 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 "ByteBuffer.h"
|
||||
#include "Errors.h"
|
||||
#include "MessageBuffer.h"
|
||||
#include "Common.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include <utf8.h>
|
||||
#include <sstream>
|
||||
#include <ctime>
|
||||
|
||||
ByteBuffer::ByteBuffer(MessageBuffer&& buffer) :
|
||||
_rpos(0), _wpos(0), _storage(buffer.Move()) { }
|
||||
|
||||
ByteBufferPositionException::ByteBufferPositionException(bool add, size_t pos, size_t size, size_t valueSize)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
|
||||
ss << "Attempted to " << (add ? "put" : "get") << " value with size: "
|
||||
<< valueSize << " in ByteBuffer (pos: " << pos << " size: " << size
|
||||
<< ")";
|
||||
|
||||
message().assign(ss.str());
|
||||
}
|
||||
|
||||
ByteBufferSourceException::ByteBufferSourceException(size_t pos, size_t size, size_t valueSize)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
|
||||
ss << "Attempted to put a "
|
||||
<< (valueSize > 0 ? "NULL-pointer" : "zero-sized value")
|
||||
<< " in ByteBuffer (pos: " << pos << " size: " << size << ")";
|
||||
|
||||
message().assign(ss.str());
|
||||
}
|
||||
|
||||
ByteBufferInvalidValueException::ByteBufferInvalidValueException(char const* type, char const* value)
|
||||
{
|
||||
message().assign(acore::StringFormat("Invalid %s value (%s) found in ByteBuffer", type, value));
|
||||
}
|
||||
|
||||
ByteBuffer& ByteBuffer::operator>>(float& value)
|
||||
{
|
||||
value = read<float>();
|
||||
|
||||
if (!std::isfinite(value))
|
||||
throw ByteBufferInvalidValueException("float", "infinity");
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer& ByteBuffer::operator>>(double& value)
|
||||
{
|
||||
value = read<double>();
|
||||
|
||||
if (!std::isfinite(value))
|
||||
throw ByteBufferInvalidValueException("double", "infinity");
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string ByteBuffer::ReadCString(bool requireValidUtf8 /*= true*/)
|
||||
{
|
||||
std::string value;
|
||||
|
||||
while (rpos() < size()) // prevent crash at wrong string format in packet
|
||||
{
|
||||
char c = read<char>();
|
||||
if (c == 0)
|
||||
break;
|
||||
value += c;
|
||||
}
|
||||
|
||||
if (requireValidUtf8 && !utf8::is_valid(value.begin(), value.end()))
|
||||
throw ByteBufferInvalidValueException("string", value.c_str());
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32 ByteBuffer::ReadPackedTime()
|
||||
{
|
||||
uint32 packedDate = read<uint32>();
|
||||
tm lt = tm();
|
||||
|
||||
lt.tm_min = packedDate & 0x3F;
|
||||
lt.tm_hour = (packedDate >> 6) & 0x1F;
|
||||
//lt.tm_wday = (packedDate >> 11) & 7;
|
||||
lt.tm_mday = ((packedDate >> 14) & 0x3F) + 1;
|
||||
lt.tm_mon = (packedDate >> 20) & 0xF;
|
||||
lt.tm_year = ((packedDate >> 24) & 0x1F) + 100;
|
||||
|
||||
return uint32(mktime(<));
|
||||
}
|
||||
|
||||
void ByteBuffer::append(uint8 const* src, size_t cnt)
|
||||
{
|
||||
ASSERT(src, "Attempted to put a NULL-pointer in ByteBuffer (pos: " SZFMTD " size: " SZFMTD ")", _wpos, size());
|
||||
ASSERT(cnt, "Attempted to put a zero-sized value in ByteBuffer (pos: " SZFMTD " size: " SZFMTD ")", _wpos, size());
|
||||
ASSERT(size() < 10000000);
|
||||
|
||||
size_t const newSize = _wpos + cnt;
|
||||
|
||||
if (_storage.capacity() < newSize) // custom memory allocation rules
|
||||
{
|
||||
if (newSize < 100)
|
||||
_storage.reserve(300);
|
||||
else if (newSize < 750)
|
||||
_storage.reserve(2500);
|
||||
else if (newSize < 6000)
|
||||
_storage.reserve(10000);
|
||||
else
|
||||
_storage.reserve(400000);
|
||||
}
|
||||
|
||||
if (_storage.size() < newSize)
|
||||
_storage.resize(newSize);
|
||||
|
||||
std::memcpy(&_storage[_wpos], src, cnt);
|
||||
_wpos = newSize;
|
||||
}
|
||||
|
||||
void ByteBuffer::AppendPackedTime(time_t time)
|
||||
{
|
||||
tm lt;
|
||||
localtime_r(&time, <);
|
||||
append<uint32>((lt.tm_year - 100) << 24 | lt.tm_mon << 20 | (lt.tm_mday - 1) << 14 | lt.tm_wday << 11 | lt.tm_hour << 6 | lt.tm_min);
|
||||
}
|
||||
|
||||
void ByteBuffer::put(size_t pos, uint8 const* src, size_t cnt)
|
||||
{
|
||||
ASSERT(pos + cnt <= size(), "Attempted to put value with size: " SZFMTD " in ByteBuffer (pos: " SZFMTD " size: " SZFMTD ")", cnt, pos, size());
|
||||
ASSERT(src, "Attempted to put a NULL-pointer in ByteBuffer (pos: " SZFMTD " size: " SZFMTD ")", pos, size());
|
||||
ASSERT(cnt, "Attempted to put a zero-sized value in ByteBuffer (pos: " SZFMTD " size: " SZFMTD ")", pos, size());
|
||||
|
||||
std::memcpy(&_storage[pos], src, cnt);
|
||||
}
|
||||
|
||||
void ByteBuffer::print_storage() const
|
||||
{
|
||||
if (!sLog->ShouldLog("network", LogLevel::LOG_LEVEL_TRACE)) // optimize disabled trace output
|
||||
return;
|
||||
|
||||
std::ostringstream o;
|
||||
o << "STORAGE_SIZE: " << size();
|
||||
|
||||
for (uint32 i = 0; i < size(); ++i)
|
||||
o << read<uint8>(i) << " - ";
|
||||
|
||||
o << " ";
|
||||
|
||||
LOG_TRACE("network", "%s", o.str().c_str());
|
||||
}
|
||||
|
||||
void ByteBuffer::textlike() const
|
||||
{
|
||||
if (!sLog->ShouldLog("network", LogLevel::LOG_LEVEL_TRACE)) // optimize disabled trace output
|
||||
return;
|
||||
|
||||
std::ostringstream o;
|
||||
o << "STORAGE_SIZE: " << size();
|
||||
|
||||
for (uint32 i = 0; i < size(); ++i)
|
||||
{
|
||||
char buf[2];
|
||||
snprintf(buf, 2, "%c", read<uint8>(i));
|
||||
o << buf;
|
||||
}
|
||||
|
||||
o << " ";
|
||||
|
||||
LOG_TRACE("network", "%s", o.str().c_str());
|
||||
}
|
||||
|
||||
void ByteBuffer::hexlike() const
|
||||
{
|
||||
if (!sLog->ShouldLog("network", LogLevel::LOG_LEVEL_TRACE)) // optimize disabled trace output
|
||||
return;
|
||||
|
||||
uint32 j = 1, k = 1;
|
||||
|
||||
std::ostringstream o;
|
||||
o << "STORAGE_SIZE: " << size();
|
||||
|
||||
for (uint32 i = 0; i < size(); ++i)
|
||||
{
|
||||
char buf[4];
|
||||
snprintf(buf, 4, "%2X ", read<uint8>(i));
|
||||
|
||||
if ((i == (j * 8)) && ((i != (k * 16))))
|
||||
{
|
||||
o << "| ";
|
||||
++j;
|
||||
}
|
||||
else if (i == (k * 16))
|
||||
{
|
||||
o << "\n";
|
||||
++k;
|
||||
++j;
|
||||
}
|
||||
|
||||
o << buf;
|
||||
}
|
||||
|
||||
o << " ";
|
||||
|
||||
LOG_TRACE("network", "%s", o.str().c_str());
|
||||
}
|
||||
551
src/server/shared/Packets/ByteBuffer.h
Normal file
551
src/server/shared/Packets/ByteBuffer.h
Normal file
@@ -0,0 +1,551 @@
|
||||
/*
|
||||
* 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 _BYTEBUFFER_H
|
||||
#define _BYTEBUFFER_H
|
||||
|
||||
#include "Define.h"
|
||||
#include "ByteConverter.h"
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
|
||||
class MessageBuffer;
|
||||
|
||||
// Root of ByteBuffer exception hierarchy
|
||||
class AC_SHARED_API ByteBufferException : public std::exception
|
||||
{
|
||||
public:
|
||||
~ByteBufferException() noexcept = default;
|
||||
|
||||
char const* what() const noexcept override { return msg_.c_str(); }
|
||||
|
||||
protected:
|
||||
std::string & message() noexcept { return msg_; }
|
||||
|
||||
private:
|
||||
std::string msg_;
|
||||
};
|
||||
|
||||
class AC_SHARED_API ByteBufferPositionException : public ByteBufferException
|
||||
{
|
||||
public:
|
||||
ByteBufferPositionException(bool add, size_t pos, size_t size, size_t valueSize);
|
||||
|
||||
~ByteBufferPositionException() noexcept = default;
|
||||
};
|
||||
|
||||
class AC_SHARED_API ByteBufferSourceException : public ByteBufferException
|
||||
{
|
||||
public:
|
||||
ByteBufferSourceException(size_t pos, size_t size, size_t valueSize);
|
||||
|
||||
~ByteBufferSourceException() noexcept = default;
|
||||
};
|
||||
|
||||
class AC_SHARED_API ByteBufferInvalidValueException : public ByteBufferException
|
||||
{
|
||||
public:
|
||||
ByteBufferInvalidValueException(char const* type, char const* value);
|
||||
|
||||
~ByteBufferInvalidValueException() noexcept = default;
|
||||
};
|
||||
|
||||
class AC_SHARED_API ByteBuffer
|
||||
{
|
||||
public:
|
||||
constexpr static size_t DEFAULT_SIZE = 0x1000;
|
||||
|
||||
// constructor
|
||||
ByteBuffer() : _rpos(0), _wpos(0)
|
||||
{
|
||||
_storage.reserve(DEFAULT_SIZE);
|
||||
}
|
||||
|
||||
ByteBuffer(size_t reserve) : _rpos(0), _wpos(0)
|
||||
{
|
||||
_storage.reserve(reserve);
|
||||
}
|
||||
|
||||
ByteBuffer(ByteBuffer&& buf) noexcept :
|
||||
_rpos(buf._rpos), _wpos(buf._wpos), _storage(std::move(buf._storage))
|
||||
{
|
||||
buf._rpos = 0;
|
||||
buf._wpos = 0;
|
||||
}
|
||||
|
||||
ByteBuffer(ByteBuffer const& right) = default;
|
||||
ByteBuffer(MessageBuffer&& buffer);
|
||||
virtual ~ByteBuffer() = default;
|
||||
|
||||
ByteBuffer& operator=(ByteBuffer const& right)
|
||||
{
|
||||
if (this != &right)
|
||||
{
|
||||
_rpos = right._rpos;
|
||||
_wpos = right._wpos;
|
||||
_storage = right._storage;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer& operator=(ByteBuffer&& right) noexcept
|
||||
{
|
||||
if (this != &right)
|
||||
{
|
||||
_rpos = right._rpos;
|
||||
right._rpos = 0;
|
||||
_wpos = right._wpos;
|
||||
right._wpos = 0;
|
||||
_storage = std::move(right._storage);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
_storage.clear();
|
||||
_rpos = _wpos = 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void append(T value)
|
||||
{
|
||||
static_assert(std::is_fundamental<T>::value, "append(compound)");
|
||||
EndianConvert(value);
|
||||
append((uint8*)&value, sizeof(value));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void put(std::size_t pos, T value)
|
||||
{
|
||||
static_assert(std::is_fundamental<T>::value, "append(compound)");
|
||||
EndianConvert(value);
|
||||
put(pos, (uint8*)&value, sizeof(value));
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(bool value)
|
||||
{
|
||||
append<uint8>(value ? 1 : 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(uint8 value)
|
||||
{
|
||||
append<uint8>(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(uint16 value)
|
||||
{
|
||||
append<uint16>(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(uint32 value)
|
||||
{
|
||||
append<uint32>(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(uint64 value)
|
||||
{
|
||||
append<uint64>(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// signed as in 2e complement
|
||||
ByteBuffer& operator<<(int8 value)
|
||||
{
|
||||
append<int8>(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(int16 value)
|
||||
{
|
||||
append<int16>(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(int32 value)
|
||||
{
|
||||
append<int32>(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(int64 value)
|
||||
{
|
||||
append<int64>(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// floating points
|
||||
ByteBuffer& operator<<(float value)
|
||||
{
|
||||
append<float>(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(double value)
|
||||
{
|
||||
append<double>(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(std::string_view value)
|
||||
{
|
||||
if (size_t len = value.length())
|
||||
{
|
||||
append(reinterpret_cast<uint8 const*>(value.data()), len);
|
||||
}
|
||||
|
||||
append(static_cast<uint8>(0));
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(std::string const& str)
|
||||
{
|
||||
return operator<<(std::string_view(str));
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(char const* str)
|
||||
{
|
||||
return operator<<(std::string_view(str ? str : ""));
|
||||
}
|
||||
|
||||
ByteBuffer& operator>>(bool& value)
|
||||
{
|
||||
value = read<char>() > 0 ? true : false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer& operator>>(uint8& value)
|
||||
{
|
||||
value = read<uint8>();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer& operator>>(uint16& value)
|
||||
{
|
||||
value = read<uint16>();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer& operator>>(uint32& value)
|
||||
{
|
||||
value = read<uint32>();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer& operator>>(uint64& value)
|
||||
{
|
||||
value = read<uint64>();
|
||||
return *this;
|
||||
}
|
||||
|
||||
//signed as in 2e complement
|
||||
ByteBuffer& operator>>(int8& value)
|
||||
{
|
||||
value = read<int8>();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer& operator>>(int16& value)
|
||||
{
|
||||
value = read<int16>();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer& operator>>(int32& value)
|
||||
{
|
||||
value = read<int32>();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer& operator>>(int64& value)
|
||||
{
|
||||
value = read<int64>();
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer& operator>>(float& value);
|
||||
ByteBuffer& operator>>(double& value);
|
||||
|
||||
ByteBuffer& operator>>(std::string& value)
|
||||
{
|
||||
value = ReadCString(true);
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint8& operator[](size_t const pos)
|
||||
{
|
||||
if (pos >= size())
|
||||
{
|
||||
throw ByteBufferPositionException(false, pos, 1, size());
|
||||
}
|
||||
|
||||
return _storage[pos];
|
||||
}
|
||||
|
||||
uint8 const& operator[](size_t const pos) const
|
||||
{
|
||||
if (pos >= size())
|
||||
{
|
||||
throw ByteBufferPositionException(false, pos, 1, size());
|
||||
}
|
||||
|
||||
return _storage[pos];
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t rpos() const { return _rpos; }
|
||||
|
||||
size_t rpos(size_t rpos_)
|
||||
{
|
||||
_rpos = rpos_;
|
||||
return _rpos;
|
||||
}
|
||||
|
||||
void rfinish()
|
||||
{
|
||||
_rpos = wpos();
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t wpos() const { return _wpos; }
|
||||
|
||||
size_t wpos(size_t wpos_)
|
||||
{
|
||||
_wpos = wpos_;
|
||||
return _wpos;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void read_skip() { read_skip(sizeof(T)); }
|
||||
|
||||
void read_skip(size_t skip)
|
||||
{
|
||||
if (_rpos + skip > size())
|
||||
{
|
||||
throw ByteBufferPositionException(false, _rpos, skip, size());
|
||||
}
|
||||
|
||||
_rpos += skip;
|
||||
}
|
||||
|
||||
template <typename T> T read()
|
||||
{
|
||||
T r = read<T>(_rpos);
|
||||
_rpos += sizeof(T);
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T> T read(size_t pos) const
|
||||
{
|
||||
if (pos + sizeof(T) > size())
|
||||
{
|
||||
throw ByteBufferPositionException(false, pos, sizeof(T), size());
|
||||
}
|
||||
|
||||
T val = *((T const*)&_storage[pos]);
|
||||
EndianConvert(val);
|
||||
return val;
|
||||
}
|
||||
|
||||
void read(uint8* dest, size_t len)
|
||||
{
|
||||
if (_rpos + len > size())
|
||||
{
|
||||
throw ByteBufferPositionException(false, _rpos, len, size());
|
||||
}
|
||||
|
||||
std::memcpy(dest, &_storage[_rpos], len);
|
||||
_rpos += len;
|
||||
}
|
||||
|
||||
template <size_t Size>
|
||||
void read(std::array<uint8, Size>& arr)
|
||||
{
|
||||
read(arr.data(), Size);
|
||||
}
|
||||
|
||||
void readPackGUID(uint64& guid)
|
||||
{
|
||||
if (rpos() + 1 > size())
|
||||
{
|
||||
throw ByteBufferPositionException(false, _rpos, 1, size());
|
||||
}
|
||||
|
||||
guid = 0;
|
||||
|
||||
uint8 guidmark = 0;
|
||||
(*this) >> guidmark;
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
if (guidmark & (uint8(1) << i))
|
||||
{
|
||||
if (rpos() + 1 > size())
|
||||
{
|
||||
throw ByteBufferPositionException(false, _rpos, 1, size());
|
||||
}
|
||||
|
||||
uint8 bit;
|
||||
(*this) >> bit;
|
||||
guid |= (uint64(bit) << (i * 8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string ReadCString(bool requireValidUtf8 = true);
|
||||
uint32 ReadPackedTime();
|
||||
|
||||
ByteBuffer& ReadPackedTime(uint32& time)
|
||||
{
|
||||
time = ReadPackedTime();
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint8* contents()
|
||||
{
|
||||
if (_storage.empty())
|
||||
{
|
||||
throw ByteBufferException();
|
||||
}
|
||||
|
||||
return _storage.data();
|
||||
}
|
||||
|
||||
[[nodiscard]] uint8 const* contents() const
|
||||
{
|
||||
if (_storage.empty())
|
||||
{
|
||||
throw ByteBufferException();
|
||||
}
|
||||
|
||||
return _storage.data();
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t size() const { return _storage.size(); }
|
||||
[[nodiscard]] bool empty() const { return _storage.empty(); }
|
||||
|
||||
void resize(size_t newsize)
|
||||
{
|
||||
_storage.resize(newsize, 0);
|
||||
_rpos = 0;
|
||||
_wpos = size();
|
||||
}
|
||||
|
||||
void reserve(size_t ressize)
|
||||
{
|
||||
if (ressize > size())
|
||||
{
|
||||
_storage.reserve(ressize);
|
||||
}
|
||||
}
|
||||
|
||||
void shrink_to_fit()
|
||||
{
|
||||
_storage.shrink_to_fit();
|
||||
}
|
||||
|
||||
void append(const char *src, size_t cnt)
|
||||
{
|
||||
return append((const uint8 *)src, cnt);
|
||||
}
|
||||
|
||||
template<class T> void append(const T* src, size_t cnt)
|
||||
{
|
||||
return append((const uint8*)src, cnt * sizeof(T));
|
||||
}
|
||||
|
||||
void append(uint8 const* src, size_t cnt);
|
||||
|
||||
void append(ByteBuffer const& buffer)
|
||||
{
|
||||
if (buffer.wpos())
|
||||
{
|
||||
append(buffer.contents(), buffer.wpos());
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t Size>
|
||||
void append(std::array<uint8, Size> const& arr)
|
||||
{
|
||||
append(arr.data(), Size);
|
||||
}
|
||||
|
||||
// can be used in SMSG_MONSTER_MOVE opcode
|
||||
void appendPackXYZ(float x, float y, float z)
|
||||
{
|
||||
uint32 packed = 0;
|
||||
packed |= ((int)(x / 0.25f) & 0x7FF);
|
||||
packed |= ((int)(y / 0.25f) & 0x7FF) << 11;
|
||||
packed |= ((int)(z / 0.25f) & 0x3FF) << 22;
|
||||
*this << packed;
|
||||
}
|
||||
|
||||
void appendPackGUID(uint64 guid)
|
||||
{
|
||||
uint8 packGUID[8 + 1];
|
||||
packGUID[0] = 0;
|
||||
size_t size = 1;
|
||||
|
||||
for (uint8 i = 0; guid != 0;++i)
|
||||
{
|
||||
if (guid & 0xFF)
|
||||
{
|
||||
packGUID[0] |= uint8(1 << i);
|
||||
packGUID[size] = uint8(guid & 0xFF);
|
||||
++size;
|
||||
}
|
||||
|
||||
guid >>= 8;
|
||||
}
|
||||
|
||||
append(packGUID, size);
|
||||
}
|
||||
|
||||
void AppendPackedTime(time_t time);
|
||||
void put(size_t pos, const uint8 *src, size_t cnt);
|
||||
void print_storage() const;
|
||||
void textlike() const;
|
||||
void hexlike() const;
|
||||
|
||||
protected:
|
||||
size_t _rpos, _wpos;
|
||||
std::vector<uint8> _storage;
|
||||
};
|
||||
|
||||
/// @todo Make a ByteBuffer.cpp and move all this inlining to it.
|
||||
template<>
|
||||
inline std::string ByteBuffer::read<std::string>()
|
||||
{
|
||||
std::string tmp;
|
||||
*this >> tmp;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void ByteBuffer::read_skip<char*>()
|
||||
{
|
||||
std::string temp;
|
||||
*this >> temp;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void ByteBuffer::read_skip<char const*>()
|
||||
{
|
||||
read_skip<char*>();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void ByteBuffer::read_skip<std::string>()
|
||||
{
|
||||
read_skip<char*>();
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user