Big network rewrites
Hook up protocol, lots of cleaning 🧹
This commit is contained in:
parent
aaf6abaf07
commit
0e0a8d89ed
|
@ -1,6 +1,9 @@
|
|||
#include "DedicatedServer.h"
|
||||
#include "config/ServerProperties.h"
|
||||
|
||||
#include "Protocol.h"
|
||||
#include "PacketReader.h"
|
||||
|
||||
namespace Feather
|
||||
{
|
||||
DedicatedServer::DedicatedServer(ServerProperties* properties) :
|
||||
|
@ -13,7 +16,17 @@ namespace Feather
|
|||
|
||||
while (1)
|
||||
{
|
||||
|
||||
auto [clients, clientsLock] = m_clients.borrow();
|
||||
for (auto& client : clients)
|
||||
{
|
||||
Protocol protocol;
|
||||
auto [data, clientLock] = client.GetTCPClient().GetData().borrow();
|
||||
if (data.empty())
|
||||
continue;
|
||||
auto reader = PacketReader(data.data());
|
||||
protocol.HandlePacket(client, reader);
|
||||
data.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,6 +43,6 @@ namespace Feather
|
|||
void DedicatedServer::OnClientDisconnect(const Network::TCPClient& client)
|
||||
{
|
||||
auto [clients, lock] = m_clients.borrow();
|
||||
clients.remove(client);
|
||||
clients.remove_if([&](MinecraftClient& other) { return &other.GetTCPClient() == &client; });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "network/TCPListener.h"
|
||||
#include "network/ServerStatus.h"
|
||||
#include "MinecraftClient.h"
|
||||
#include "ServerStatus.h"
|
||||
|
||||
#include "network/IListenerInterface.h"
|
||||
|
||||
namespace Feather
|
||||
|
@ -20,8 +21,8 @@ namespace Feather
|
|||
private:
|
||||
ServerProperties* m_properties;
|
||||
Network::TCPListener m_listener;
|
||||
Network::ServerStatus m_status;
|
||||
ServerStatus m_status;
|
||||
|
||||
LockableList<Network::TCPClient> m_clients;
|
||||
LockableList<MinecraftClient> m_clients;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
#include "MinecraftClient.h"
|
||||
|
||||
namespace Feather
|
||||
{
|
||||
MinecraftClient::MinecraftClient(Network::TCPClient&& client)
|
||||
: m_client (std::move(client))
|
||||
{
|
||||
}
|
||||
|
||||
MinecraftClient::~MinecraftClient()
|
||||
{
|
||||
}
|
||||
|
||||
void MinecraftClient::SendMessage(const NetworkMessage& message)
|
||||
{
|
||||
m_client.Write(message.GetData(), message.GetDataSize());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "network/TCPListener.h"
|
||||
#include "Protocol.h"
|
||||
#include "NetworkMessage.h"
|
||||
|
||||
namespace Feather
|
||||
{
|
||||
class MinecraftClient
|
||||
{
|
||||
public:
|
||||
MinecraftClient(Network::TCPClient&& client);
|
||||
~MinecraftClient();
|
||||
|
||||
inline Network::TCPClient& GetTCPClient() { return m_client; }
|
||||
inline ProtocolContext& GetContext() { return m_context; }
|
||||
|
||||
void SendMessage(const NetworkMessage& message);
|
||||
|
||||
private:
|
||||
Network::TCPClient m_client;
|
||||
ProtocolContext m_context;
|
||||
};
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
#define VARINT_MAX_SIZE 5
|
||||
|
||||
namespace Feather::Network
|
||||
namespace Feather
|
||||
{
|
||||
class NetworkMessageCounter
|
||||
{
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Common.h"
|
||||
#include "Common.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
|
@ -8,9 +8,8 @@
|
|||
|
||||
using std::string;
|
||||
|
||||
namespace Feather::Network
|
||||
namespace Feather
|
||||
{
|
||||
|
||||
// Class to read packet data, such as is produced by NetworkMessage
|
||||
class PacketReader
|
||||
{
|
||||
|
@ -20,18 +19,25 @@ namespace Feather::Network
|
|||
{
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T Peek()
|
||||
{
|
||||
return *reinterpret_cast<const T* const>(&m_data[m_offset]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T Read()
|
||||
{
|
||||
T value = *reinterpret_cast<const T *const>(&m_data[m_offset]);
|
||||
T value = Peek<T>();
|
||||
m_offset += sizeof(T);
|
||||
return value;
|
||||
}
|
||||
|
||||
inline int ReadVarInt()
|
||||
template <typename T = int32_t>
|
||||
inline T ReadVarInt()
|
||||
{
|
||||
int numRead = 0;
|
||||
int result = 0;
|
||||
int32_t numRead = 0;
|
||||
int32_t result = 0;
|
||||
uint8_t read;
|
||||
do
|
||||
{
|
||||
|
@ -47,7 +53,7 @@ namespace Feather::Network
|
|||
}
|
||||
} while ((read & 0b10000000) != 0);
|
||||
|
||||
return result;
|
||||
return static_cast<T>(result);
|
||||
}
|
||||
|
||||
string ReadString()
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
namespace Feather
|
||||
{
|
||||
enum class ServerboundHandholdingPacketId : int32_t
|
||||
{
|
||||
Handshake = 0,
|
||||
};
|
||||
|
||||
enum class ServerboundStatusPacketId : int32_t
|
||||
{
|
||||
Request = 0,
|
||||
Ping = 1
|
||||
};
|
||||
|
||||
constexpr uint8_t LegacyServerListPing = 0xFE;
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
#include "Protocol.h"
|
||||
#include "PacketReader.h"
|
||||
#include "PacketTypes.h"
|
||||
#include "MinecraftClient.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
// TODO Remove.
|
||||
const std::string json_template =
|
||||
R"({
|
||||
"version": {
|
||||
"name": "1.16.1",
|
||||
"protocol": 736
|
||||
},
|
||||
"players": {
|
||||
"max": 10,
|
||||
"online": 10,
|
||||
"sample": [
|
||||
{
|
||||
"name": "thinkofdeath",
|
||||
"id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": {
|
||||
"text": "Hello Nukem!"
|
||||
}
|
||||
})";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int clientProtocolVersion = packet.ReadVarInt();
|
||||
string serverIp = packet.ReadString();
|
||||
uint16_t port = packet.Read<uint16_t>();
|
||||
|
||||
// next desired state
|
||||
ProtocolState intention = (ProtocolState)(packet.ReadVarInt());
|
||||
|
||||
printf("[Protocol] Client Intention Packet: version=%d, serverIp=%s, port=%u, intention=%d\n",
|
||||
clientProtocolVersion,
|
||||
serverIp.c_str(),
|
||||
port,
|
||||
intention
|
||||
);
|
||||
|
||||
context.SetState(intention);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ProtocolState::Status:
|
||||
{
|
||||
auto id = packet.ReadVarInt<ServerboundStatusPacketId>();
|
||||
switch (id)
|
||||
{
|
||||
case ServerboundStatusPacketId::Request:
|
||||
{
|
||||
printf("[Protocol] Client sent STATUS_PING_REQUEST\n");
|
||||
|
||||
DynamicNetworkMessage msg(VARINT_MAX_SIZE + json_template.length());
|
||||
// Packet ID
|
||||
msg.WriteVarInt(0);
|
||||
// JSON Contents
|
||||
msg.WriteString(json_template.c_str(), static_cast<int32_t>(json_template.length()));
|
||||
msg.Finalize();
|
||||
client.SendMessage(msg);
|
||||
|
||||
break;
|
||||
}
|
||||
case ServerboundStatusPacketId::Ping:
|
||||
{
|
||||
int64_t timestamp = packet.Read<int64_t>();
|
||||
printf("[Protocol] Client sent STATUS_PING: %lld\n", timestamp);
|
||||
|
||||
DynamicNetworkMessage msg(VARINT_MAX_SIZE + sizeof(int64_t));
|
||||
|
||||
msg.WriteVarInt(1);
|
||||
msg.WriteLong(timestamp);
|
||||
msg.Finalize();
|
||||
|
||||
client.SendMessage(msg);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include "NetworkMessage.h"
|
||||
|
||||
namespace Feather
|
||||
{
|
||||
class PacketReader;
|
||||
class MinecraftClient;
|
||||
|
||||
enum class ProtocolState
|
||||
{
|
||||
Handholding = -1,
|
||||
Play = 0,
|
||||
Status = 1,
|
||||
Login = 2,
|
||||
};
|
||||
|
||||
class ProtocolContext
|
||||
{
|
||||
public:
|
||||
inline ProtocolState GetState() const
|
||||
{
|
||||
return m_state;
|
||||
}
|
||||
|
||||
inline void SetState(ProtocolState state)
|
||||
{
|
||||
printf("Setting state");
|
||||
m_state = state;
|
||||
}
|
||||
private:
|
||||
ProtocolState m_state = ProtocolState::Handholding;
|
||||
};
|
||||
|
||||
class Protocol
|
||||
{
|
||||
public:
|
||||
void HandlePacket(MinecraftClient& client, PacketReader& packet);
|
||||
};
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
using string = std::string;
|
||||
using stringstream = std::stringstream;
|
||||
|
||||
namespace Feather::Network
|
||||
namespace Feather
|
||||
{
|
||||
class ServerStatus
|
||||
{
|
|
@ -1,12 +1,13 @@
|
|||
feather_src = [
|
||||
'Main.cpp',
|
||||
'DedicatedServer.cpp',
|
||||
'MinecraftClient.cpp',
|
||||
'Protocol.cpp',
|
||||
|
||||
'logging/Logger.cpp',
|
||||
|
||||
'network/NetworkManager.cpp',
|
||||
'network/TCPListener.cpp',
|
||||
'network/Protocol.cpp',
|
||||
|
||||
'util/StringUtil.cpp',
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
// Packets from Client -> Server
|
||||
enum class ServerboundPacketType
|
||||
{
|
||||
STATUS_PING_REQUEST = 0,
|
||||
STATUS_PING = 1
|
||||
};
|
|
@ -1,111 +0,0 @@
|
|||
#if 0
|
||||
#include "Protocol.h"
|
||||
#include "PacketReader.h"
|
||||
#include "PacketTypes.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace Feather::Network
|
||||
{
|
||||
void Protocol::HandlePacket(PacketReader& packet)
|
||||
{
|
||||
int id = packet.ReadVarInt();
|
||||
printf("PacketReader[%u]: ID = %u\n", packet.Length(), id);
|
||||
|
||||
switch (m_state)
|
||||
{
|
||||
case ProtocolState::HANDSHAKING:
|
||||
{
|
||||
if (id != 0) {
|
||||
// this could be, for example, Legacy Server List Ping 0xFE
|
||||
printf("[Protocol] Client sent packet with non-zero ID 0x%x while state was HANDSHAKING\n", id);
|
||||
break;
|
||||
}
|
||||
|
||||
int clientProtocolVersion = packet.ReadVarInt();
|
||||
string serverIp = packet.ReadString();
|
||||
uint16_t port = packet.Read<uint16_t>();
|
||||
|
||||
// next desired state
|
||||
ProtocolState intention = (ProtocolState)(packet.ReadVarInt());
|
||||
|
||||
printf("[Protocol] Client Intention Packet: version=%d, serverIp=%s, port=%u, intention=%d\n",
|
||||
clientProtocolVersion,
|
||||
serverIp.c_str(),
|
||||
port,
|
||||
intention
|
||||
);
|
||||
|
||||
SetState(intention);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ProtocolState::STATUS:
|
||||
{
|
||||
ServerboundPacketType type = (ServerboundPacketType)id;
|
||||
printf("[Protocol] [STATUS mode] Client sent packet ID %d (%d)\n", id, type);
|
||||
switch (type)
|
||||
{
|
||||
case ServerboundPacketType::STATUS_PING_REQUEST:
|
||||
{
|
||||
printf("[Protocol] Client sent STATUS_PING_REQUEST\n");
|
||||
|
||||
const std::string json_template =
|
||||
R"({
|
||||
"version": {
|
||||
"name": "1.16.1",
|
||||
"protocol": 736
|
||||
},
|
||||
"players": {
|
||||
"max": 10,
|
||||
"online": 10,
|
||||
"sample": [
|
||||
{
|
||||
"name": "thinkofdeath",
|
||||
"id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": {
|
||||
"text": "Hello Nukem!"
|
||||
}
|
||||
})";
|
||||
|
||||
/*DynamicNetworkMessage msg(VARINT_MAX_SIZE + json_template.length());
|
||||
// Packet ID
|
||||
msg.WriteVarInt(0);
|
||||
// JSON Contents
|
||||
msg.WriteString(json_template.c_str(), static_cast<int32_t>(json_template.length()));
|
||||
m_client->SendPacket(&msg);*/
|
||||
|
||||
break;
|
||||
}
|
||||
case ServerboundPacketType::STATUS_PING:
|
||||
int64_t timestamp = packet.Read<int64_t>();
|
||||
printf("[Protocol] Client sent STATUS_PING: %lld\n", timestamp);
|
||||
|
||||
/*DynamicNetworkMessage msg(VARINT_MAX_SIZE + sizeof(int64_t));
|
||||
|
||||
msg.WriteVarInt(1);
|
||||
msg.WriteLong(timestamp);
|
||||
|
||||
m_client->SendPacket(&msg);*/
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Protocol::SetState(ProtocolState state)
|
||||
{
|
||||
printf("[Protocol] Switching state from %d to %d\n", m_state, state);
|
||||
// TODO: validate state here
|
||||
m_state = state;
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -1,33 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "NetworkMessage.h"
|
||||
#include "ClientHandle.h"
|
||||
|
||||
namespace Feather::Network
|
||||
{
|
||||
class PacketReader;
|
||||
|
||||
enum class ProtocolState
|
||||
{
|
||||
HANDSHAKING = -1,
|
||||
PLAY = 0,
|
||||
STATUS = 1,
|
||||
LOGIN = 2,
|
||||
};
|
||||
|
||||
// TODO: This should become abstract so we can support multiple versions
|
||||
class Protocol
|
||||
{
|
||||
private:
|
||||
ClientHandle* m_client;
|
||||
ProtocolState m_state = ProtocolState::HANDSHAKING;
|
||||
|
||||
public:
|
||||
Protocol(ClientHandle* client) : m_client(client) {}
|
||||
|
||||
void HandlePacket(PacketReader& packet);
|
||||
|
||||
protected:
|
||||
void SetState(ProtocolState state);
|
||||
};
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
#include "../Lockable.h"
|
||||
|
||||
#include "TCPListener.h"
|
||||
#include "IListenerInterface.h"
|
||||
#include "NetworkManager.h"
|
||||
#include "NetworkMessage.h"
|
||||
#include "PacketReader.h"
|
||||
|
@ -154,7 +155,13 @@ namespace Feather::Network
|
|||
{
|
||||
}
|
||||
|
||||
const LockableVector<uint8_t>& GetData() const
|
||||
void Write(const uint8_t* data, size_t size)
|
||||
{
|
||||
if (bufferevent_write(m_bufferEvent, data, size) != 0)
|
||||
printf("Fuck!");
|
||||
}
|
||||
|
||||
LockableVector<uint8_t>& GetData()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
@ -182,11 +189,16 @@ namespace Feather::Network
|
|||
{
|
||||
}
|
||||
|
||||
const LockableVector<uint8_t>& TCPClient::GetData() const
|
||||
LockableVector<uint8_t>& TCPClient::GetData()
|
||||
{
|
||||
return m_client->GetData();
|
||||
}
|
||||
|
||||
void TCPClient::Write(const uint8_t* data, size_t size)
|
||||
{
|
||||
m_client->Write(data, size);
|
||||
}
|
||||
|
||||
TCPListener::TCPListener(uint16_t port, IListenerInterface* callbacks)
|
||||
: m_callbacks(callbacks)
|
||||
{
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "Lockable.h"
|
||||
#include "IListenerInterface.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
@ -11,6 +10,7 @@ namespace Feather::Network
|
|||
{
|
||||
class TCPSocket;
|
||||
class TCPListenerClient;
|
||||
class IListenerInterface;
|
||||
|
||||
class TCPClient
|
||||
{
|
||||
|
@ -19,15 +19,12 @@ namespace Feather::Network
|
|||
TCPClient(TCPClient&& client);
|
||||
~TCPClient();
|
||||
|
||||
const LockableVector<uint8_t>& GetData() const;
|
||||
LockableVector<uint8_t>& GetData();
|
||||
void Write(const uint8_t* data, size_t size);
|
||||
|
||||
private:
|
||||
std::unique_ptr<TCPListenerClient> m_client;
|
||||
};
|
||||
// TODO: Can we eliminate the move constructor + overloads?
|
||||
// Kinda unclean.
|
||||
inline bool operator==(const TCPClient& a, const TCPClient& b) { return &a == &b; }
|
||||
inline bool operator!=(const TCPClient& a, const TCPClient& b) { return &a != &b; }
|
||||
|
||||
class TCPListener
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue