Add packet generation + handling automagically
This commit is contained in:
parent
e11f7ee3c5
commit
e8244a3359
|
@ -2,15 +2,13 @@
|
|||
#include "config/ServerProperties.h"
|
||||
|
||||
#include "PacketReader.h"
|
||||
#include <Windows.h>
|
||||
|
||||
namespace Feather
|
||||
{
|
||||
DedicatedServer::DedicatedServer(ServerProperties* properties) :
|
||||
m_properties(properties),
|
||||
m_listener(properties->serverPort.GetValue(), this),
|
||||
m_status(),
|
||||
m_protocol(*this)
|
||||
m_status()
|
||||
{
|
||||
m_status.descriptionText = properties->motd.GetValue();
|
||||
m_status.maxPlayers = properties->maxPlayers.GetValue();
|
||||
|
@ -27,7 +25,7 @@ namespace Feather
|
|||
while (offset != data.size())
|
||||
{
|
||||
auto reader = PacketReader(&data[offset]);
|
||||
m_protocol.HandlePacket(client, reader);
|
||||
Protocol::ProcessPacket(*this, client, reader, client.GetContext().GetState());
|
||||
offset += reader.Size();
|
||||
}
|
||||
data.clear();
|
||||
|
@ -56,4 +54,62 @@ namespace Feather
|
|||
auto [clients, lock] = m_clients.borrow();
|
||||
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 "ServerStatus.h"
|
||||
#include "Protocol.h"
|
||||
#include "protocol/Protocol.h"
|
||||
|
||||
#include "network/IListenerInterface.h"
|
||||
|
||||
|
@ -19,13 +19,15 @@ namespace Feather
|
|||
void OnClientConnect(Network::TCPClientHandle&& 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:
|
||||
ServerProperties* m_properties;
|
||||
Network::TCPListener m_listener;
|
||||
ServerStatus m_status;
|
||||
Protocol m_protocol;
|
||||
|
||||
LockableList<MinecraftClient> m_clients;
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include "network/TCPListener.h"
|
||||
#include "network/TCPClient.h"
|
||||
#include "Protocol.h"
|
||||
#include "protocol/Protocol.h"
|
||||
#include "NetworkMessage.h"
|
||||
|
||||
namespace Feather
|
||||
|
@ -14,12 +14,12 @@ namespace Feather
|
|||
~MinecraftClient();
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
Network::TCPClientHandle m_client;
|
||||
ProtocolContext m_context;
|
||||
Network::TCPClientHandle m_client;
|
||||
Protocol::ProtocolContext m_context;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
|
||||
#define VARINT_MAX_SIZE 5
|
||||
|
@ -16,9 +17,7 @@ namespace Feather
|
|||
public:
|
||||
NetworkMessage(int32_t expectedSize)
|
||||
{
|
||||
size_t initialSize = VARINT_MAX_SIZE + expectedSize;
|
||||
|
||||
m_data.reserve(initialSize);
|
||||
m_data.reserve(expectedSize);
|
||||
m_data.resize(VARINT_MAX_SIZE);
|
||||
}
|
||||
|
||||
|
@ -37,7 +36,7 @@ namespace Feather
|
|||
{
|
||||
if constexpr (asEndian == Endian::Big && sizeof(T) > 1)
|
||||
{
|
||||
T valueReversed = ReverseBytes<T>(value);
|
||||
T valueReversed = ReverseBytes(value);
|
||||
WriteData(&valueReversed, sizeof(T));
|
||||
}
|
||||
else
|
||||
|
@ -67,6 +66,11 @@ namespace Feather
|
|||
WriteData(string, stringSize);
|
||||
}
|
||||
|
||||
inline void WriteString(const std::string& string)
|
||||
{
|
||||
WriteString(string.c_str(), string.length());
|
||||
}
|
||||
|
||||
inline void Finalize()
|
||||
{
|
||||
PrependVarIntSize();
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace Feather
|
|||
{
|
||||
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
|
||||
|
@ -51,11 +51,9 @@ namespace Feather
|
|||
int32_t length = ReadVarInt(&m_lengthSize);
|
||||
|
||||
// HACK: handle Legacy Server List Ping
|
||||
if (length == 0xFE) {
|
||||
if (PeekByte() == 0x01) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
m_legacy = length == 0xFE && PeekByte() == 0x01;
|
||||
if (m_legacy)
|
||||
return 1;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
@ -100,11 +98,15 @@ namespace Feather
|
|||
|
||||
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
|
||||
|
|
|
@ -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',
|
||||
'DedicatedServer.cpp',
|
||||
'MinecraftClient.cpp',
|
||||
'Protocol.cpp',
|
||||
|
||||
'logging/Logger.cpp',
|
||||
|
||||
|
|
|
@ -8,10 +8,13 @@
|
|||
|
||||
namespace Feather
|
||||
{
|
||||
class DedicatedServer;
|
||||
class PacketReader;
|
||||
class MinecraftClient;
|
||||
class DedicatedServer;
|
||||
}
|
||||
|
||||
namespace Feather::Protocol
|
||||
{
|
||||
class ProtocolContext
|
||||
{
|
||||
public:
|
||||
|
@ -28,13 +31,4 @@ namespace Feather
|
|||
private:
|
||||
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
|
||||
|
||||
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):
|
||||
global text
|
||||
global indent_count
|
||||
for i in range(indent_count):
|
||||
text += ' '
|
||||
text += (fmt + '\n').format(*args)
|
||||
|
||||
def resolve_type(aliases, type):
|
||||
if type in aliases:
|
||||
return aliases[type]
|
||||
def newline():
|
||||
global text
|
||||
text += '\n'
|
||||
|
||||
def extract_array_type(type):
|
||||
if '[' in type and ']' in type:
|
||||
return type[:type.rfind('[')]
|
||||
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):
|
||||
add_text(' enum class ProtocolState : int32_t')
|
||||
add_text(' {{')
|
||||
add_text('enum class ProtocolState : int32_t')
|
||||
add_text('{{')
|
||||
indent()
|
||||
for state, value in states.items():
|
||||
add_text(' {} = {},', state, value)
|
||||
add_text(' }};')
|
||||
add_text('{} = {},', state, value)
|
||||
unindent()
|
||||
add_text('}};')
|
||||
|
||||
def get_rw_func(primitiveType, aliasedType, read):
|
||||
prefix = 'Read' if read else 'Write'
|
||||
|
@ -29,30 +82,113 @@ def get_rw_func(primitiveType, aliasedType, read):
|
|||
elif aliasedType == 'string':
|
||||
return '{}String'.format(prefix)
|
||||
else:
|
||||
return 'Read<{}>'.format(aliasedType)
|
||||
return '{}<{}>'.format(prefix, primitiveType)
|
||||
|
||||
def print_messages(list, aliases, primitives, serverbound):
|
||||
def print_messages(list, aliases, primitives):
|
||||
global text
|
||||
for message_name, message in list.items():
|
||||
add_text(' struct {}Message', message_name)
|
||||
add_text(' {{')
|
||||
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::{};', message['state'])
|
||||
add_text('')
|
||||
if serverbound:
|
||||
add_text(' {}Message(PacketReader& reader)', message_name)
|
||||
add_text(' {{')
|
||||
for name, type in message['vars'].items():
|
||||
add_text(' {} = reader.{}();', name, get_rw_func(resolve_type(primitives, type), resolve_type(aliases, type), True))
|
||||
add_text(' }}')
|
||||
add_text('')
|
||||
for state, direction_list in list.items():
|
||||
add_text('namespace {}', state.capitalize())
|
||||
add_text('{{')
|
||||
indent()
|
||||
for direction, messages in direction_list.items():
|
||||
serverbound = direction == 'serverbound'
|
||||
for message_name, message in messages.items():
|
||||
global varint_max_size
|
||||
size = varint_max_size # Packet Length
|
||||
size += varint_max_size # Packet Id
|
||||
for name, type in message['vars'].items():
|
||||
size += get_type_size(resolve_type(aliases, type))
|
||||
|
||||
struct_name = '{}{}'.format(direction.capitalize(), message_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('}}')
|
||||
|
||||
for name, type in message['vars'].items():
|
||||
add_text(' {} {};', resolve_type(primitives, type), name)
|
||||
|
||||
add_text(' }};')
|
||||
add_text('')
|
||||
|
||||
def print_protocol():
|
||||
with open(sys.argv[1]) as message_file:
|
||||
|
@ -60,29 +196,37 @@ def print_protocol():
|
|||
|
||||
print_states(message_scheme['states'])
|
||||
|
||||
add_text('')
|
||||
newline()
|
||||
|
||||
print_messages(
|
||||
message_scheme['messages']['serverbound'],
|
||||
message_scheme['messages'],
|
||||
message_scheme['types']['aliases'],
|
||||
message_scheme['types']['primitives'],
|
||||
True)
|
||||
message_scheme['types']['primitives'])
|
||||
|
||||
newline()
|
||||
|
||||
print_handler(message_scheme['messages'])
|
||||
|
||||
def main():
|
||||
global text
|
||||
|
||||
if len(sys.argv) != 3:
|
||||
print('Usage: generate_protocol.py <input: protocol.hjson> <output: ProtocolDefinitions.h>')
|
||||
return
|
||||
|
||||
add_text('#pragma once')
|
||||
add_text('')
|
||||
newline()
|
||||
add_text('#include <cstdint>')
|
||||
add_text('#include "Types.h"')
|
||||
add_text('#include "PacketReader.h"')
|
||||
add_text('')
|
||||
add_text('namespace Feather')
|
||||
newline()
|
||||
add_text('namespace Feather::Protocol')
|
||||
add_text('{{')
|
||||
indent()
|
||||
print_protocol()
|
||||
unindent()
|
||||
add_text('}}')
|
||||
add_text('')
|
||||
newline()
|
||||
|
||||
with open(sys.argv[2], 'w') as out_file:
|
||||
out_file.write(text)
|
||||
|
|
|
@ -10,6 +10,16 @@
|
|||
{
|
||||
varint : int32_t
|
||||
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 :
|
||||
{
|
||||
serverbound :
|
||||
handholding :
|
||||
{
|
||||
Handshake :
|
||||
serverbound :
|
||||
{
|
||||
id : 0
|
||||
state : Handholding
|
||||
vars :
|
||||
Handshake :
|
||||
{
|
||||
protocolVersion : varint
|
||||
serverIP : string
|
||||
port : uint16_t
|
||||
intention : ProtocolState
|
||||
}
|
||||
}
|
||||
|
||||
Ping :
|
||||
{
|
||||
id : 1
|
||||
state : Login
|
||||
vars :
|
||||
{
|
||||
timestamp : uint64_t
|
||||
}
|
||||
}
|
||||
|
||||
LoginStart :
|
||||
{
|
||||
id : 0
|
||||
state : Login
|
||||
vars :
|
||||
{
|
||||
username : string
|
||||
id : 0
|
||||
vars :
|
||||
{
|
||||
protocolVersion : varint
|
||||
serverIP : string[255]
|
||||
port : uint16
|
||||
intention : ProtocolState
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
|
||||
enum class Endian
|
||||
namespace Feather
|
||||
{
|
||||
Little,
|
||||
Big
|
||||
};
|
||||
enum class Endian
|
||||
{
|
||||
Little,
|
||||
Big
|
||||
};
|
||||
|
||||
// Reverses the byte order of a primitive value of any typical size
|
||||
template <typename T>
|
||||
inline constexpr T ReverseBytes(T n)
|
||||
{
|
||||
// 1 byte, cannot reverse
|
||||
if constexpr (sizeof(T) == 1) return n;
|
||||
|
||||
#ifdef __GNUC__
|
||||
// GCC intrinsic byte swaps
|
||||
// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005fbswap16
|
||||
|
||||
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.");
|
||||
}
|
||||
// TODO: Make this constexpr
|
||||
inline uint8_t ReverseBytes(uint8_t n) { return n; }
|
||||
#ifdef __GNUC__
|
||||
inline uint16_t ReverseBytes(uint16_t n) { return __builtin_bswap16(n); }
|
||||
inline uint32_t ReverseBytes(uint32_t n) { return __builtin_bswap32(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
|
||||
|
||||
inline int8_t ReverseBytes(int8_t n) { return n; }
|
||||
inline int16_t ReverseBytes(int16_t n) { return int16_t(ReverseBytes(uint16_t(n))); }
|
||||
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))); }
|
||||
}
|
Loading…
Reference in New Issue