Add packet generation + handling automagically
This commit is contained in:
parent
e11f7ee3c5
commit
e8244a3359
|
@ -2,15 +2,13 @@
|
||||||
#include "config/ServerProperties.h"
|
#include "config/ServerProperties.h"
|
||||||
|
|
||||||
#include "PacketReader.h"
|
#include "PacketReader.h"
|
||||||
#include <Windows.h>
|
|
||||||
|
|
||||||
namespace Feather
|
namespace Feather
|
||||||
{
|
{
|
||||||
DedicatedServer::DedicatedServer(ServerProperties* properties) :
|
DedicatedServer::DedicatedServer(ServerProperties* properties) :
|
||||||
m_properties(properties),
|
m_properties(properties),
|
||||||
m_listener(properties->serverPort.GetValue(), this),
|
m_listener(properties->serverPort.GetValue(), this),
|
||||||
m_status(),
|
m_status()
|
||||||
m_protocol(*this)
|
|
||||||
{
|
{
|
||||||
m_status.descriptionText = properties->motd.GetValue();
|
m_status.descriptionText = properties->motd.GetValue();
|
||||||
m_status.maxPlayers = properties->maxPlayers.GetValue();
|
m_status.maxPlayers = properties->maxPlayers.GetValue();
|
||||||
|
@ -27,7 +25,7 @@ namespace Feather
|
||||||
while (offset != data.size())
|
while (offset != data.size())
|
||||||
{
|
{
|
||||||
auto reader = PacketReader(&data[offset]);
|
auto reader = PacketReader(&data[offset]);
|
||||||
m_protocol.HandlePacket(client, reader);
|
Protocol::ProcessPacket(*this, client, reader, client.GetContext().GetState());
|
||||||
offset += reader.Size();
|
offset += reader.Size();
|
||||||
}
|
}
|
||||||
data.clear();
|
data.clear();
|
||||||
|
@ -56,4 +54,62 @@ namespace Feather
|
||||||
auto [clients, lock] = m_clients.borrow();
|
auto [clients, lock] = m_clients.borrow();
|
||||||
clients.remove_if([&](MinecraftClient& other) { return other.GetTCPClient().get() == client; });
|
clients.remove_if([&](MinecraftClient& other) { return other.GetTCPClient().get() == client; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using namespace Protocol;
|
||||||
|
|
||||||
|
void DedicatedServer::HandleLegacyPing(MinecraftClient& client)
|
||||||
|
{
|
||||||
|
Log_Info("Got legacy server list ping.");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void DedicatedServer::HandlePacket(MinecraftClient& client, const Handholding::ServerboundHandshake& handshake)
|
||||||
|
{
|
||||||
|
Log_Info("Client Intention Packet: version=%d, serverIp=%s, port=%u, intention=%d\n",
|
||||||
|
handshake.protocolVersion,
|
||||||
|
handshake.serverIP.c_str(),
|
||||||
|
handshake.port,
|
||||||
|
handshake.intention
|
||||||
|
);
|
||||||
|
|
||||||
|
client.GetContext().SetState(handshake.intention);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void DedicatedServer::HandlePacket(MinecraftClient& client, const Status::ServerboundRequest& request)
|
||||||
|
{
|
||||||
|
Log_Info("Client sent STATUS_PING_REQUEST");
|
||||||
|
|
||||||
|
Status::ClientboundResponse message =
|
||||||
|
{
|
||||||
|
.jsonResponse = m_status.GetServerStatusJSON()
|
||||||
|
};
|
||||||
|
|
||||||
|
client.SendMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void DedicatedServer::HandlePacket(MinecraftClient& client, const Status::ServerboundPing& ping)
|
||||||
|
{
|
||||||
|
Log_Info("Client sent STATUS_PING: %llu", ping.timestamp);
|
||||||
|
|
||||||
|
Status::ClientboundPong message =
|
||||||
|
{
|
||||||
|
.timestamp = ping.timestamp
|
||||||
|
};
|
||||||
|
|
||||||
|
client.SendMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void DedicatedServer::HandlePacket(MinecraftClient& client, const Login::ServerboundStart& start)
|
||||||
|
{
|
||||||
|
Login::ClientboundSuccess message =
|
||||||
|
{
|
||||||
|
.uuid = { 4658857991808325907ull, 7518717155607718277ull },
|
||||||
|
.username = start.username
|
||||||
|
};
|
||||||
|
|
||||||
|
client.SendMessage(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include "MinecraftClient.h"
|
#include "MinecraftClient.h"
|
||||||
#include "ServerStatus.h"
|
#include "ServerStatus.h"
|
||||||
#include "Protocol.h"
|
#include "protocol/Protocol.h"
|
||||||
|
|
||||||
#include "network/IListenerInterface.h"
|
#include "network/IListenerInterface.h"
|
||||||
|
|
||||||
|
@ -19,13 +19,15 @@ namespace Feather
|
||||||
void OnClientConnect(Network::TCPClientHandle&& client) override;
|
void OnClientConnect(Network::TCPClientHandle&& client) override;
|
||||||
void OnClientDisconnect(const Network::TCPClient* client) override;
|
void OnClientDisconnect(const Network::TCPClient* client) override;
|
||||||
|
|
||||||
ServerStatus& GetStatus() { return m_status; }
|
void HandleLegacyPing(MinecraftClient& client);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void HandlePacket(MinecraftClient& client, const T& message);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ServerProperties* m_properties;
|
ServerProperties* m_properties;
|
||||||
Network::TCPListener m_listener;
|
Network::TCPListener m_listener;
|
||||||
ServerStatus m_status;
|
ServerStatus m_status;
|
||||||
Protocol m_protocol;
|
|
||||||
|
|
||||||
LockableList<MinecraftClient> m_clients;
|
LockableList<MinecraftClient> m_clients;
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include "network/TCPListener.h"
|
#include "network/TCPListener.h"
|
||||||
#include "network/TCPClient.h"
|
#include "network/TCPClient.h"
|
||||||
#include "Protocol.h"
|
#include "protocol/Protocol.h"
|
||||||
#include "NetworkMessage.h"
|
#include "NetworkMessage.h"
|
||||||
|
|
||||||
namespace Feather
|
namespace Feather
|
||||||
|
@ -14,12 +14,12 @@ namespace Feather
|
||||||
~MinecraftClient();
|
~MinecraftClient();
|
||||||
|
|
||||||
inline Network::TCPClientHandle& GetTCPClient() { return m_client; }
|
inline Network::TCPClientHandle& GetTCPClient() { return m_client; }
|
||||||
inline ProtocolContext& GetContext() { return m_context; }
|
inline Protocol::ProtocolContext& GetContext() { return m_context; }
|
||||||
|
|
||||||
void SendMessage(const NetworkMessage& message);
|
void SendMessage(const NetworkMessage& message);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Network::TCPClientHandle m_client;
|
Network::TCPClientHandle m_client;
|
||||||
ProtocolContext m_context;
|
Protocol::ProtocolContext m_context;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#define VARINT_MAX_SIZE 5
|
#define VARINT_MAX_SIZE 5
|
||||||
|
@ -16,9 +17,7 @@ namespace Feather
|
||||||
public:
|
public:
|
||||||
NetworkMessage(int32_t expectedSize)
|
NetworkMessage(int32_t expectedSize)
|
||||||
{
|
{
|
||||||
size_t initialSize = VARINT_MAX_SIZE + expectedSize;
|
m_data.reserve(expectedSize);
|
||||||
|
|
||||||
m_data.reserve(initialSize);
|
|
||||||
m_data.resize(VARINT_MAX_SIZE);
|
m_data.resize(VARINT_MAX_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +36,7 @@ namespace Feather
|
||||||
{
|
{
|
||||||
if constexpr (asEndian == Endian::Big && sizeof(T) > 1)
|
if constexpr (asEndian == Endian::Big && sizeof(T) > 1)
|
||||||
{
|
{
|
||||||
T valueReversed = ReverseBytes<T>(value);
|
T valueReversed = ReverseBytes(value);
|
||||||
WriteData(&valueReversed, sizeof(T));
|
WriteData(&valueReversed, sizeof(T));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -67,6 +66,11 @@ namespace Feather
|
||||||
WriteData(string, stringSize);
|
WriteData(string, stringSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void WriteString(const std::string& string)
|
||||||
|
{
|
||||||
|
WriteString(string.c_str(), string.length());
|
||||||
|
}
|
||||||
|
|
||||||
inline void Finalize()
|
inline void Finalize()
|
||||||
{
|
{
|
||||||
PrependVarIntSize();
|
PrependVarIntSize();
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace Feather
|
||||||
{
|
{
|
||||||
T value = *reinterpret_cast<const T *const>(&m_data[m_offset]);
|
T value = *reinterpret_cast<const T *const>(&m_data[m_offset]);
|
||||||
|
|
||||||
return (endianMode == Endian::Big) ? ReverseBytes<T>(value) : value;
|
return (endianMode == Endian::Big) ? ReverseBytes(value) : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the next value
|
// Read the next value
|
||||||
|
@ -51,11 +51,9 @@ namespace Feather
|
||||||
int32_t length = ReadVarInt(&m_lengthSize);
|
int32_t length = ReadVarInt(&m_lengthSize);
|
||||||
|
|
||||||
// HACK: handle Legacy Server List Ping
|
// HACK: handle Legacy Server List Ping
|
||||||
if (length == 0xFE) {
|
m_legacy = length == 0xFE && PeekByte() == 0x01;
|
||||||
if (PeekByte() == 0x01) {
|
if (m_legacy)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
@ -100,11 +98,15 @@ namespace Feather
|
||||||
|
|
||||||
uint32_t Length() const { return m_length; }
|
uint32_t Length() const { return m_length; }
|
||||||
uint32_t Size() const { return m_length + m_lengthSize; }
|
uint32_t Size() const { return m_length + m_lengthSize; }
|
||||||
|
|
||||||
|
bool IsLegacyPing() const { return m_legacy; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const uint8_t *const m_data;
|
const uint8_t *const m_data;
|
||||||
uint32_t m_offset;
|
uint32_t m_offset;
|
||||||
const uint32_t m_length;
|
const uint32_t m_length;
|
||||||
uint32_t m_lengthSize;
|
uint32_t m_lengthSize;
|
||||||
|
bool m_legacy;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use fast Read and Peek for uint8_t
|
// Use fast Read and Peek for uint8_t
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
namespace Feather
|
|
||||||
{
|
|
||||||
enum class ServerboundHandholdingPacketId : int32_t
|
|
||||||
{
|
|
||||||
Handshake = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ServerboundStatusPacketId : int32_t
|
|
||||||
{
|
|
||||||
Request = 0,
|
|
||||||
Ping = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ClientBoundStatusPacketId : int32_t
|
|
||||||
{
|
|
||||||
Response = 0,
|
|
||||||
Pong = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ServerboundLoginPacketId : int32_t
|
|
||||||
{
|
|
||||||
LoginStart = 0,
|
|
||||||
EncryptionResponse = 1,
|
|
||||||
LoginPluginResponse = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ClientboundLoginPacketId : int32_t
|
|
||||||
{
|
|
||||||
Disconnect = 0,
|
|
||||||
EncryptionRequest = 1,
|
|
||||||
LoginSuccess = 2,
|
|
||||||
SetCompression = 3,
|
|
||||||
LoginPluginRequest = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr uint8_t LegacyServerListPing = 0xFE;
|
|
||||||
}
|
|
125
src/Protocol.cpp
125
src/Protocol.cpp
|
@ -1,125 +0,0 @@
|
||||||
#include "Protocol.h"
|
|
||||||
#include "PacketReader.h"
|
|
||||||
#include "PacketTypes.h"
|
|
||||||
#include "MinecraftClient.h"
|
|
||||||
#include "DedicatedServer.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace Feather
|
|
||||||
{
|
|
||||||
void Protocol::HandlePacket(MinecraftClient& client, PacketReader& packet)
|
|
||||||
{
|
|
||||||
auto& context = client.GetContext();
|
|
||||||
switch (context.GetState())
|
|
||||||
{
|
|
||||||
case ProtocolState::Handholding:
|
|
||||||
{
|
|
||||||
auto id = packet.ReadVarInt<ServerboundHandholdingPacketId>();
|
|
||||||
uint8_t legacyId = packet.Peek<uint8_t>();
|
|
||||||
if (id != ServerboundHandholdingPacketId::Handshake && legacyId != LegacyServerListPing)
|
|
||||||
{
|
|
||||||
printf("[Protocol] Client sent packet with non-zero ID 0x%x while state was HANDSHAKING\n", id);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id != ServerboundHandholdingPacketId::Handshake)
|
|
||||||
{
|
|
||||||
// Legacy server ping.
|
|
||||||
// TODO: Do server list ping here.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
HandshakeMessage handshake(packet);
|
|
||||||
|
|
||||||
printf("[Protocol] Client Intention Packet: version=%d, serverIp=%s, port=%u, intention=%d\n",
|
|
||||||
handshake.protocolVersion,
|
|
||||||
handshake.serverIP.c_str(),
|
|
||||||
handshake.port,
|
|
||||||
handshake.intention
|
|
||||||
);
|
|
||||||
|
|
||||||
context.SetState(handshake.intention);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ProtocolState::Status:
|
|
||||||
{
|
|
||||||
auto id = packet.ReadVarInt<ServerboundStatusPacketId>();
|
|
||||||
switch (id)
|
|
||||||
{
|
|
||||||
case ServerboundStatusPacketId::Request:
|
|
||||||
{
|
|
||||||
printf("[Protocol] Client sent STATUS_PING_REQUEST\n");
|
|
||||||
|
|
||||||
std::string status = m_server.GetStatus().GetServerStatusJSON();
|
|
||||||
|
|
||||||
NetworkMessage msg(VARINT_MAX_SIZE + status.length());
|
|
||||||
// Packet ID
|
|
||||||
msg.WriteVarInt(ClientBoundStatusPacketId::Response);
|
|
||||||
// JSON Contents
|
|
||||||
msg.WriteString(status.c_str(), static_cast<int32_t>(status.length()));
|
|
||||||
msg.Finalize();
|
|
||||||
client.SendMessage(msg);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ServerboundStatusPacketId::Ping:
|
|
||||||
{
|
|
||||||
PingMessage ping(packet);
|
|
||||||
printf("[Protocol] Client sent STATUS_PING: %lld\n", ping.timestamp);
|
|
||||||
|
|
||||||
NetworkMessage msg(VARINT_MAX_SIZE + sizeof(int64_t));
|
|
||||||
|
|
||||||
msg.WriteVarInt(ClientBoundStatusPacketId::Pong);
|
|
||||||
msg.Write<uint64_t>(ping.timestamp);
|
|
||||||
msg.Finalize();
|
|
||||||
|
|
||||||
client.SendMessage(msg);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ProtocolState::Login:
|
|
||||||
{
|
|
||||||
auto id = packet.ReadVarInt<ServerboundLoginPacketId>();
|
|
||||||
|
|
||||||
switch (id)
|
|
||||||
{
|
|
||||||
case ServerboundLoginPacketId::LoginStart:
|
|
||||||
{
|
|
||||||
LoginStartMessage loginStart(packet);
|
|
||||||
|
|
||||||
// 1.15.2...
|
|
||||||
//std::string uuid = "ecb99913-96a8-40a7-8529-a2ca6ad95768";
|
|
||||||
//uuid.resize(36);
|
|
||||||
//NetworkMessage msg(VARINT_MAX_SIZE * 3 + uuid.length() + loginStart.username.length());
|
|
||||||
|
|
||||||
NetworkMessage msg(VARINT_MAX_SIZE * 3 + 2 * sizeof(uint64_t) + loginStart.username.length());
|
|
||||||
|
|
||||||
msg.WriteVarInt(ClientboundLoginPacketId::LoginSuccess);
|
|
||||||
// UUID 1.16.1
|
|
||||||
msg.Write<uint64_t>(4658857991808325907LL);
|
|
||||||
msg.Write<uint64_t>(7518717155607718277LL);
|
|
||||||
|
|
||||||
// UUID 1.15.2
|
|
||||||
//msg.WriteString(uuid.c_str(), uuid.length());
|
|
||||||
|
|
||||||
msg.WriteString(loginStart.username.c_str(), loginStart.username.length());
|
|
||||||
msg.Finalize();
|
|
||||||
|
|
||||||
client.SendMessage(msg);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "util/ByteUtil.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace Feather
|
||||||
|
{
|
||||||
|
struct MinecraftUUID
|
||||||
|
{
|
||||||
|
std::array<uint64_t, 2> data;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline MinecraftUUID ReverseBytes(MinecraftUUID uuid)
|
||||||
|
{
|
||||||
|
return MinecraftUUID
|
||||||
|
{
|
||||||
|
.data = { ReverseBytes(uuid.data[0]), ReverseBytes(uuid.data[1]) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,6 @@ feather_src = [
|
||||||
'Main.cpp',
|
'Main.cpp',
|
||||||
'DedicatedServer.cpp',
|
'DedicatedServer.cpp',
|
||||||
'MinecraftClient.cpp',
|
'MinecraftClient.cpp',
|
||||||
'Protocol.cpp',
|
|
||||||
|
|
||||||
'logging/Logger.cpp',
|
'logging/Logger.cpp',
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,13 @@
|
||||||
|
|
||||||
namespace Feather
|
namespace Feather
|
||||||
{
|
{
|
||||||
class DedicatedServer;
|
|
||||||
class PacketReader;
|
class PacketReader;
|
||||||
class MinecraftClient;
|
class MinecraftClient;
|
||||||
|
class DedicatedServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Feather::Protocol
|
||||||
|
{
|
||||||
class ProtocolContext
|
class ProtocolContext
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -28,13 +31,4 @@ namespace Feather
|
||||||
private:
|
private:
|
||||||
ProtocolState m_state = ProtocolState::Handholding;
|
ProtocolState m_state = ProtocolState::Handholding;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Protocol
|
|
||||||
{
|
|
||||||
DedicatedServer& m_server;
|
|
||||||
public:
|
|
||||||
Protocol(DedicatedServer& server) : m_server(server) {}
|
|
||||||
|
|
||||||
void HandlePacket(MinecraftClient& client, PacketReader& packet);
|
|
||||||
};
|
|
||||||
}
|
}
|
|
@ -2,22 +2,75 @@ import hjson
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
text = ''
|
text = ''
|
||||||
|
varint_max_size = 5
|
||||||
|
indent_count = 0
|
||||||
|
|
||||||
|
def indent():
|
||||||
|
global indent_count
|
||||||
|
indent_count += 1
|
||||||
|
|
||||||
|
def unindent():
|
||||||
|
global indent_count
|
||||||
|
indent_count -= 1
|
||||||
|
|
||||||
def add_text(fmt, *args):
|
def add_text(fmt, *args):
|
||||||
global text
|
global text
|
||||||
|
global indent_count
|
||||||
|
for i in range(indent_count):
|
||||||
|
text += ' '
|
||||||
text += (fmt + '\n').format(*args)
|
text += (fmt + '\n').format(*args)
|
||||||
|
|
||||||
def resolve_type(aliases, type):
|
def newline():
|
||||||
if type in aliases:
|
global text
|
||||||
return aliases[type]
|
text += '\n'
|
||||||
|
|
||||||
|
def extract_array_type(type):
|
||||||
|
if '[' in type and ']' in type:
|
||||||
|
return type[:type.rfind('[')]
|
||||||
return type
|
return type
|
||||||
|
|
||||||
|
def extract_array_count(type):
|
||||||
|
if '[' in type and ']' in type:
|
||||||
|
return int(type[type.find('[')+1:type.rfind(']')])
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def resolve_type(aliases, type):
|
||||||
|
type_no_array = type.rstrip('_')
|
||||||
|
if type_no_array in aliases:
|
||||||
|
return aliases[type_no_array]
|
||||||
|
return type
|
||||||
|
|
||||||
|
def get_type_size(type):
|
||||||
|
global varint_max_size
|
||||||
|
count = extract_array_count(type)
|
||||||
|
type = extract_array_type(type)
|
||||||
|
|
||||||
|
if type == 'varint':
|
||||||
|
return varint_max_size
|
||||||
|
elif type == 'int64' or type == 'uint64' or type == 'double':
|
||||||
|
return 8
|
||||||
|
elif type == 'int32' or type == 'uint32' or type == 'float':
|
||||||
|
return 4
|
||||||
|
elif type == 'int16' or type == 'uint16':
|
||||||
|
return 2
|
||||||
|
elif type == 'int8' or type == 'uint8':
|
||||||
|
return 1
|
||||||
|
elif type == 'string':
|
||||||
|
return count
|
||||||
|
elif type == 'uuid':
|
||||||
|
return 16
|
||||||
|
else:
|
||||||
|
print(type)
|
||||||
|
assert False
|
||||||
|
|
||||||
def print_states(states):
|
def print_states(states):
|
||||||
add_text(' enum class ProtocolState : int32_t')
|
add_text('enum class ProtocolState : int32_t')
|
||||||
add_text(' {{')
|
add_text('{{')
|
||||||
|
indent()
|
||||||
for state, value in states.items():
|
for state, value in states.items():
|
||||||
add_text(' {} = {},', state, value)
|
add_text('{} = {},', state, value)
|
||||||
add_text(' }};')
|
unindent()
|
||||||
|
add_text('}};')
|
||||||
|
|
||||||
def get_rw_func(primitiveType, aliasedType, read):
|
def get_rw_func(primitiveType, aliasedType, read):
|
||||||
prefix = 'Read' if read else 'Write'
|
prefix = 'Read' if read else 'Write'
|
||||||
|
@ -29,30 +82,113 @@ def get_rw_func(primitiveType, aliasedType, read):
|
||||||
elif aliasedType == 'string':
|
elif aliasedType == 'string':
|
||||||
return '{}String'.format(prefix)
|
return '{}String'.format(prefix)
|
||||||
else:
|
else:
|
||||||
return 'Read<{}>'.format(aliasedType)
|
return '{}<{}>'.format(prefix, primitiveType)
|
||||||
|
|
||||||
def print_messages(list, aliases, primitives, serverbound):
|
def print_messages(list, aliases, primitives):
|
||||||
global text
|
global text
|
||||||
for message_name, message in list.items():
|
for state, direction_list in list.items():
|
||||||
add_text(' struct {}Message', message_name)
|
add_text('namespace {}', state.capitalize())
|
||||||
add_text(' {{')
|
add_text('{{')
|
||||||
add_text(' static constexpr int32_t PacketId = {};', message['id'])
|
indent()
|
||||||
add_text(' static constexpr bool ServerBound = {};', 'true' if serverbound else 'false')
|
for direction, messages in direction_list.items():
|
||||||
add_text(' static constexpr ProtocolState PacketState = ProtocolState::{};', message['state'])
|
serverbound = direction == 'serverbound'
|
||||||
add_text('')
|
for message_name, message in messages.items():
|
||||||
if serverbound:
|
global varint_max_size
|
||||||
add_text(' {}Message(PacketReader& reader)', message_name)
|
size = varint_max_size # Packet Length
|
||||||
add_text(' {{')
|
size += varint_max_size # Packet Id
|
||||||
for name, type in message['vars'].items():
|
for name, type in message['vars'].items():
|
||||||
add_text(' {} = reader.{}();', name, get_rw_func(resolve_type(primitives, type), resolve_type(aliases, type), True))
|
size += get_type_size(resolve_type(aliases, type))
|
||||||
add_text(' }}')
|
|
||||||
add_text('')
|
|
||||||
|
|
||||||
for name, type in message['vars'].items():
|
struct_name = '{}{}'.format(direction.capitalize(), message_name)
|
||||||
add_text(' {} {};', resolve_type(primitives, type), name)
|
add_text('struct {}', struct_name)
|
||||||
|
add_text('{{')
|
||||||
|
indent()
|
||||||
|
add_text('static constexpr int32_t PacketId = {};', message['id'])
|
||||||
|
add_text('static constexpr bool Serverbound = {};', 'true' if serverbound else 'false')
|
||||||
|
add_text('static constexpr ProtocolState PacketState = ProtocolState::{};', state.capitalize())
|
||||||
|
add_text('static constexpr size_t MaxSize = {};', size)
|
||||||
|
newline()
|
||||||
|
if serverbound:
|
||||||
|
add_text('{}(PacketReader& reader)', struct_name)
|
||||||
|
add_text('{{')
|
||||||
|
indent()
|
||||||
|
for name, type in message['vars'].items():
|
||||||
|
add_text('{} = reader.{}();', name, get_rw_func(resolve_type(primitives, extract_array_type(type)), resolve_type(aliases, extract_array_type(type)), True))
|
||||||
|
unindent()
|
||||||
|
add_text('}}')
|
||||||
|
newline()
|
||||||
|
else:
|
||||||
|
add_text('operator NetworkMessage() const')
|
||||||
|
add_text('{{')
|
||||||
|
indent()
|
||||||
|
add_text('NetworkMessage msg(MaxSize);')
|
||||||
|
add_text('msg.WriteVarInt(PacketId);')
|
||||||
|
for name, type in message['vars'].items():
|
||||||
|
add_text('msg.{}({});', get_rw_func(resolve_type(primitives, extract_array_type(type)), resolve_type(aliases, extract_array_type(type)), False), name)
|
||||||
|
add_text('msg.Finalize();')
|
||||||
|
add_text('return msg;')
|
||||||
|
unindent()
|
||||||
|
add_text('}}')
|
||||||
|
|
||||||
|
for name, type in message['vars'].items():
|
||||||
|
resolved_type = resolve_type(primitives, extract_array_type(type))
|
||||||
|
if not serverbound and resolved_type == 'std::string':
|
||||||
|
add_text('const {}& {};', resolved_type, name)
|
||||||
|
else:
|
||||||
|
add_text('{} {};', resolved_type, name)
|
||||||
|
|
||||||
|
unindent()
|
||||||
|
add_text('}};')
|
||||||
|
newline()
|
||||||
|
unindent()
|
||||||
|
add_text('}}')
|
||||||
|
newline()
|
||||||
|
|
||||||
|
def print_handler(list):
|
||||||
|
add_text('template <typename HandlerType, typename ClientType>')
|
||||||
|
add_text('void ProcessPacket(HandlerType& handler, ClientType& client, PacketReader& packet, ProtocolState state)')
|
||||||
|
add_text('{{')
|
||||||
|
indent()
|
||||||
|
add_text('if (packet.IsLegacyPing())')
|
||||||
|
add_text('{{')
|
||||||
|
indent()
|
||||||
|
add_text('handler.HandleLegacyPing(client);')
|
||||||
|
add_text('return;')
|
||||||
|
unindent()
|
||||||
|
add_text('}}')
|
||||||
|
newline()
|
||||||
|
add_text('const int32_t packetId = packet.ReadVarInt();')
|
||||||
|
add_text('switch (state)')
|
||||||
|
add_text('{{')
|
||||||
|
indent()
|
||||||
|
for state, direction_list in list.items():
|
||||||
|
add_text('case ProtocolState::{}:', state.capitalize())
|
||||||
|
add_text('{{')
|
||||||
|
indent()
|
||||||
|
add_text('switch (packetId)')
|
||||||
|
add_text('{{')
|
||||||
|
indent()
|
||||||
|
for direction, messages in direction_list.items():
|
||||||
|
serverbound = direction == 'serverbound'
|
||||||
|
if not serverbound:
|
||||||
|
continue
|
||||||
|
for message_name, message in messages.items():
|
||||||
|
name = '{}::Serverbound{}'.format(state.capitalize(), message_name)
|
||||||
|
add_text('case {}::PacketId:', name)
|
||||||
|
indent()
|
||||||
|
add_text('handler.HandlePacket<{}>(client, {}(packet));', name, name)
|
||||||
|
add_text('break;')
|
||||||
|
unindent()
|
||||||
|
unindent()
|
||||||
|
add_text('}}')
|
||||||
|
add_text('break;')
|
||||||
|
unindent()
|
||||||
|
add_text('}}')
|
||||||
|
unindent()
|
||||||
|
add_text('}}')
|
||||||
|
unindent()
|
||||||
|
add_text('}}')
|
||||||
|
|
||||||
add_text(' }};')
|
|
||||||
add_text('')
|
|
||||||
|
|
||||||
def print_protocol():
|
def print_protocol():
|
||||||
with open(sys.argv[1]) as message_file:
|
with open(sys.argv[1]) as message_file:
|
||||||
|
@ -60,29 +196,37 @@ def print_protocol():
|
||||||
|
|
||||||
print_states(message_scheme['states'])
|
print_states(message_scheme['states'])
|
||||||
|
|
||||||
add_text('')
|
newline()
|
||||||
|
|
||||||
print_messages(
|
print_messages(
|
||||||
message_scheme['messages']['serverbound'],
|
message_scheme['messages'],
|
||||||
message_scheme['types']['aliases'],
|
message_scheme['types']['aliases'],
|
||||||
message_scheme['types']['primitives'],
|
message_scheme['types']['primitives'])
|
||||||
True)
|
|
||||||
|
newline()
|
||||||
|
|
||||||
|
print_handler(message_scheme['messages'])
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
global text
|
||||||
|
|
||||||
if len(sys.argv) != 3:
|
if len(sys.argv) != 3:
|
||||||
print('Usage: generate_protocol.py <input: protocol.hjson> <output: ProtocolDefinitions.h>')
|
print('Usage: generate_protocol.py <input: protocol.hjson> <output: ProtocolDefinitions.h>')
|
||||||
return
|
return
|
||||||
|
|
||||||
add_text('#pragma once')
|
add_text('#pragma once')
|
||||||
add_text('')
|
newline()
|
||||||
add_text('#include <cstdint>')
|
add_text('#include <cstdint>')
|
||||||
|
add_text('#include "Types.h"')
|
||||||
add_text('#include "PacketReader.h"')
|
add_text('#include "PacketReader.h"')
|
||||||
add_text('')
|
newline()
|
||||||
add_text('namespace Feather')
|
add_text('namespace Feather::Protocol')
|
||||||
add_text('{{')
|
add_text('{{')
|
||||||
|
indent()
|
||||||
print_protocol()
|
print_protocol()
|
||||||
|
unindent()
|
||||||
add_text('}}')
|
add_text('}}')
|
||||||
add_text('')
|
newline()
|
||||||
|
|
||||||
with open(sys.argv[2], 'w') as out_file:
|
with open(sys.argv[2], 'w') as out_file:
|
||||||
out_file.write(text)
|
out_file.write(text)
|
||||||
|
|
|
@ -10,6 +10,16 @@
|
||||||
{
|
{
|
||||||
varint : int32_t
|
varint : int32_t
|
||||||
string : std::string
|
string : std::string
|
||||||
|
uuid : MinecraftUUID
|
||||||
|
|
||||||
|
uint64 : uint64_t
|
||||||
|
int64 : int64_t
|
||||||
|
uint32 : uint32_t
|
||||||
|
int32 : int32_t
|
||||||
|
uint16 : uint16_t
|
||||||
|
int16 : int16_t
|
||||||
|
uint8 : uint8_t
|
||||||
|
int8 : int8_t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,45 +33,103 @@
|
||||||
|
|
||||||
messages :
|
messages :
|
||||||
{
|
{
|
||||||
serverbound :
|
handholding :
|
||||||
{
|
{
|
||||||
Handshake :
|
serverbound :
|
||||||
{
|
{
|
||||||
id : 0
|
Handshake :
|
||||||
state : Handholding
|
|
||||||
vars :
|
|
||||||
{
|
{
|
||||||
protocolVersion : varint
|
id : 0
|
||||||
serverIP : string
|
vars :
|
||||||
port : uint16_t
|
{
|
||||||
intention : ProtocolState
|
protocolVersion : varint
|
||||||
}
|
serverIP : string[255]
|
||||||
}
|
port : uint16
|
||||||
|
intention : ProtocolState
|
||||||
Ping :
|
}
|
||||||
{
|
|
||||||
id : 1
|
|
||||||
state : Login
|
|
||||||
vars :
|
|
||||||
{
|
|
||||||
timestamp : uint64_t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LoginStart :
|
|
||||||
{
|
|
||||||
id : 0
|
|
||||||
state : Login
|
|
||||||
vars :
|
|
||||||
{
|
|
||||||
username : string
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clientbound :
|
status :
|
||||||
{
|
{
|
||||||
|
serverbound :
|
||||||
|
{
|
||||||
|
Request :
|
||||||
|
{
|
||||||
|
id : 0
|
||||||
|
vars :
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ping :
|
||||||
|
{
|
||||||
|
id : 1
|
||||||
|
vars :
|
||||||
|
{
|
||||||
|
timestamp : uint64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clientbound :
|
||||||
|
{
|
||||||
|
Response :
|
||||||
|
{
|
||||||
|
id : 0
|
||||||
|
vars :
|
||||||
|
{
|
||||||
|
jsonResponse : string[32767]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Pong :
|
||||||
|
{
|
||||||
|
id : 1
|
||||||
|
vars :
|
||||||
|
{
|
||||||
|
timestamp : uint64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
login :
|
||||||
|
{
|
||||||
|
clientbound :
|
||||||
|
{
|
||||||
|
Disconnect :
|
||||||
|
{
|
||||||
|
id : 0
|
||||||
|
vars :
|
||||||
|
{
|
||||||
|
username : string[32767]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Success :
|
||||||
|
{
|
||||||
|
id : 2
|
||||||
|
vars :
|
||||||
|
{
|
||||||
|
uuid : uuid
|
||||||
|
username : string[16]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serverbound :
|
||||||
|
{
|
||||||
|
Start :
|
||||||
|
{
|
||||||
|
id : 0
|
||||||
|
vars :
|
||||||
|
{
|
||||||
|
username : string[16]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,38 +1,30 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#include <cstdlib>
|
||||||
#include <stdlib.h>
|
#include <cstdint>
|
||||||
#endif
|
|
||||||
|
|
||||||
enum class Endian
|
namespace Feather
|
||||||
{
|
{
|
||||||
Little,
|
enum class Endian
|
||||||
Big
|
{
|
||||||
};
|
Little,
|
||||||
|
Big
|
||||||
|
};
|
||||||
|
|
||||||
// Reverses the byte order of a primitive value of any typical size
|
// TODO: Make this constexpr
|
||||||
template <typename T>
|
inline uint8_t ReverseBytes(uint8_t n) { return n; }
|
||||||
inline constexpr T ReverseBytes(T n)
|
#ifdef __GNUC__
|
||||||
{
|
inline uint16_t ReverseBytes(uint16_t n) { return __builtin_bswap16(n); }
|
||||||
// 1 byte, cannot reverse
|
inline uint32_t ReverseBytes(uint32_t n) { return __builtin_bswap32(n); }
|
||||||
if constexpr (sizeof(T) == 1) return n;
|
inline uint32_t ReverseBytes(uint64_t n) { return __builtin_bswap64(n); }
|
||||||
|
#else
|
||||||
|
inline uint16_t ReverseBytes(uint16_t n) { return _byteswap_ushort(n); }
|
||||||
|
inline uint32_t ReverseBytes(uint32_t n) { return _byteswap_ulong (n); }
|
||||||
|
inline uint64_t ReverseBytes(uint64_t n) { return _byteswap_uint64(n); }
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __GNUC__
|
inline int8_t ReverseBytes(int8_t n) { return n; }
|
||||||
// GCC intrinsic byte swaps
|
inline int16_t ReverseBytes(int16_t n) { return int16_t(ReverseBytes(uint16_t(n))); }
|
||||||
// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005fbswap16
|
inline int32_t ReverseBytes(int32_t n) { return int32_t(ReverseBytes(uint32_t(n))); }
|
||||||
|
inline int64_t ReverseBytes(int64_t n) { return int64_t(ReverseBytes(uint64_t(n))); }
|
||||||
if constexpr (sizeof(T) == 2) return __builtin_bswap16(n); // 2 bytes
|
|
||||||
else if constexpr (sizeof(T) == 4) return __builtin_bswap32(n); // 4 bytes
|
|
||||||
else if constexpr (sizeof(T) == 8) return __builtin_bswap64(n); // 8 bytes
|
|
||||||
|
|
||||||
#elif defined(_MSC_VER)
|
|
||||||
// MSVC intrinsic byteswaps
|
|
||||||
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/byteswap-uint64-byteswap-ulong-byteswap-ushort
|
|
||||||
|
|
||||||
if constexpr (sizeof(T) == 2) return _byteswap_ushort(n); // 2 bytes
|
|
||||||
else if constexpr (sizeof(T) == 4) return _byteswap_ulong(n); // 4 bytes
|
|
||||||
else if constexpr (sizeof(T) == 8) return _byteswap_uint64(n); // 8 bytes
|
|
||||||
#endif
|
|
||||||
//else static_assert(false, "Attempted to call ReverseBytes() on type with unusual size.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue