FeatherMC/src/PacketReader.h

134 lines
3.5 KiB
C++

#pragma once
#include "Common.h"
#include "util/ByteUtil.h"
#include <cstdio>
#include <cstdint>
#include <string>
using std::string;
namespace Feather
{
// Class to read packet data, such as is produced by NetworkMessage
class PacketReader
{
public:
PacketReader(const uint8_t *const dataPtr)
: m_data(dataPtr), m_offset(0), m_length(ReadLength())
{
}
// Peek the next value to be read
// If big endian (the default), will reverse byte order.
template <typename T, Endian endianMode = Endian::Big>
inline T Peek()
{
T value = *reinterpret_cast<const T *const>(&m_data[m_offset]);
return (endianMode == Endian::Big) ? ReverseBytes(value) : value;
}
// Read the next value
// If big endian (the default), will reverse byte order.
template <typename T, Endian endianMode = Endian::Big>
inline T Read()
{
T value = Peek<T, endianMode>();
m_offset += sizeof(T);
return value;
}
// Read bool as 1 byte
template <>
inline bool Read<bool>()
{
// TODO: Could warn on non-bool value
return (ReadByte() != 0x00);
}
// Fast way to peek the next byte
inline uint8_t PeekByte() { return m_data[m_offset]; }
// Fast way to read a byte
inline uint8_t ReadByte() { return m_data[m_offset++]; }
inline int32_t ReadLength()
{
int32_t length = ReadVarInt(&m_lengthSize);
// HACK: handle Legacy Server List Ping
m_legacy = length == 0xFE && PeekByte() == 0x01;
if (m_legacy)
return 1;
return length;
}
template <typename T = int32_t>
inline T ReadVarInt(uint32_t* outCount = nullptr)
{
uint32_t numRead = 0;
int32_t result = 0;
uint8_t read;
do
{
read = ReadByte();
int value = (read & 0b01111111);
result |= (value << (7 * numRead));
numRead++;
if (numRead > 5)
{
// complain
//throw new RuntimeException("VarInt is too big");
}
} while ((read & 0b10000000) != 0);
if (outCount != nullptr)
*outCount = numRead;
return static_cast<T>(result);
}
// can be assigned to string&, string_view
string ReadString()
{
int size = ReadVarInt();
string str;
for (int i = 0; i < size; i++) {
str += ReadByte();
}
return str;
}
inline BlockPos ReadPosition()
{
return BlockPos(Read<int64_t>());
}
uint32_t Length() const { return m_length; }
uint32_t Size() const { return m_length + m_lengthSize; }
bool IsLegacyPing() const { return m_legacy; }
private:
const uint8_t *const m_data;
uint32_t m_offset;
const uint32_t m_length;
uint32_t m_lengthSize;
bool m_legacy;
};
// Use fast Read and Peek for uint8_t
template <>
inline uint8_t PacketReader::Peek<uint8_t>() { return PeekByte(); }
template<>
inline uint8_t PacketReader::Read<uint8_t>() { return ReadByte(); }
}