Add Protocol, ClientHandle, PacketTypes

This commit is contained in:
DankParrot 2020-07-27 21:45:03 -07:00
parent fb6d99409e
commit 8dabf013ef
9 changed files with 283 additions and 14 deletions

View File

@ -4,6 +4,7 @@ feather_src = [
'network/NetworkManager.cpp',
'network/TCPListener.cpp',
'network/Protocol.cpp',
'util/StringUtil.cpp',

View File

@ -0,0 +1,24 @@
#pragma once
#include "NetworkMessage.h"
#include <queue>
using std::queue;
namespace Feather::Network
{
class ClientHandle
{
private:
friend class TCPListener;
queue<NetworkMessage*> m_queue;
public:
void SendPacket(NetworkMessage* msg)
{
m_queue.push(msg);
}
};
}

View File

@ -33,6 +33,11 @@ namespace Feather::Network
m_size += stringSize;
}
inline void WriteLong(int64_t value)
{
m_size += sizeof(int64_t);
}
inline int32_t GetSize() const { return m_size; }
private:
@ -77,6 +82,14 @@ namespace Feather::Network
std::memcpy(&m_data[oldSize], string, size_t(stringSize));
}
inline void WriteLong(int64_t value)
{
size_t oldSize = m_data.size();
m_data.resize(oldSize + sizeof(int64_t));
// TODO: do this with registers if faster (see libcore.io.Memory.pokeLong)
std::memcpy(&m_data[oldSize], &value, sizeof(int64_t));
}
inline virtual const uint8_t* GetData() const { return m_data.data(); }
inline virtual size_t GetDataSize() const { return m_data.size(); }

View File

@ -1,7 +1,12 @@
#pragma once
#include "../Common.h"
#include <cstdio>
#include <cstdint>
#include <string>
using std::string;
namespace Feather::Network
{
@ -42,7 +47,41 @@ namespace Feather::Network
return result;
}
inline uint32_t Length() const { return m_length; }
inline uint16_t ReadUnsignedShort()
{
return (ReadByte() << 8) | ReadByte();
}
inline int64_t ReadLong()
{
int64_t value = 0;
// FIXME
value |= (ReadByte() << 56);
value |= (ReadByte() << 48);
value |= (ReadByte() << 40);
value |= (ReadByte() << 32);
value |= (ReadByte() << 24);
value |= (ReadByte() << 16);
value |= (ReadByte() << 8);
value |= (ReadByte() << 0);
return value;
}
string ReadString()
{
int size = ReadVarInt();
string str;
for (int i = 0; i < size; i++) {
str += ReadByte();
}
//printf("Read string of length %d: %s\n", size, str);
return str;
}
uint32_t Length() const { return m_length; }
private:
const uint8_t *const m_data;
const uint32_t m_length;

View File

@ -0,0 +1,8 @@
#pragma once
// Packets from Client -> Server
enum class ServerboundPacketType
{
STATUS_PING_REQUEST = 0,
STATUS_PING = 1
};

109
src/network/Protocol.cpp Normal file
View File

@ -0,0 +1,109 @@
#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();
unsigned short port = packet.ReadUnsignedShort();
// 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.ReadLong();
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;
}
}

33
src/network/Protocol.h Normal file
View File

@ -0,0 +1,33 @@
#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);
};
}

View File

@ -1,6 +1,12 @@
#include "TCPListener.h"

#include "../Common.h"
#include "Protocol.h"
#include "TCPListener.h"
#include "NetworkManager.h"
#include "NetworkMessage.h"
#include "PacketReader.h"
#include "ClientHandle.h"
#include <event2/event.h>
#include <event2/listener.h>
@ -100,13 +106,26 @@ namespace Feather::Network
bool m_ipv6;
};
class TCPListenerClient
class TCPListenerClient final : public ClientHandle
{
private:
friend class TCPListener;
// Protocol for this specific client
Protocol m_protocol;
TCPListener *m_parent;
bufferevent *m_bufferEvent;
std::vector<uint8_t> m_incomingData;
std::mutex m_mutex;
public:
TCPListenerClient(TCPListener* parent, evutil_socket_t socket)
: m_parent(parent)
, m_protocol(this)
, m_bufferEvent(bufferevent_socket_new(NetworkManager::Instance().GetEventBase(), socket, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE | BEV_OPT_DEFER_CALLBACKS | BEV_OPT_UNLOCK_CALLBACKS))
{
printf("Created TCPListenerClient\n");
bufferevent_setcb(m_bufferEvent,
[](bufferevent* be, void* self) { static_cast<TCPListenerClient*>(self)->ReadCallback(); },
[](bufferevent* be, void* self) { static_cast<TCPListenerClient*>(self)->WriteCallback(); },
@ -122,16 +141,26 @@ namespace Feather::Network
void ReadCallback()
{
printf("Read callback!\n");
auto lock = std::unique_lock(m_mutex);
char data[1024];
uint8_t data[1KB];
size_t length;
size_t originalSize = m_incomingData.size();
// TODO: could switch to bufferevent_read_buffer and use evbuffer to avoid copies
while ((length = bufferevent_read(m_bufferEvent, data, sizeof(data))) > 0)
{
size_t oldSize = m_incomingData.size();
m_incomingData.resize(oldSize + length);
std::memcpy(&m_incomingData[oldSize], data, length);
}
uint8_t* dataPtr = &(m_incomingData.data()[originalSize]);
PacketReader reader(dataPtr);
m_protocol.HandlePacket(reader);
}
template <typename T>
@ -177,19 +206,14 @@ R"({
void WriteCallback()
{
printf("Write callback!\n");
SendShitTest();
//SendShitTest();
}
void EventCallback(short event)
{
printf("Event!\n");
printf("Event! %d\n", event);
}
private:
TCPListener* m_parent;
bufferevent* m_bufferEvent;
std::vector<char> m_incomingData;
std::mutex m_mutex;
};
TCPListener::TCPListener(uint16_t port)
@ -228,4 +252,18 @@ R"({
{
}
void TCPListener::DispatchQueuedPackets()
{
for (const auto& client : m_clients)
{
while (!client->m_queue.empty())
{
NetworkMessage *msg = client->m_queue.front();
printf("Writing packet of size %u\n", msg->GetDataSize());
bufferevent_write(client->m_bufferEvent, msg->GetData(), msg->GetDataSize());
client->m_queue.pop();
}
}
}
}

View File

@ -5,7 +5,9 @@
#include <vector>
namespace Feather::Network
{
{
class Protocol;
class TCPSocket;
class TCPListenerClient;
@ -15,6 +17,8 @@ namespace Feather::Network
TCPListener(uint16_t port);
~TCPListener();
void DispatchQueuedPackets();
private:
std::unique_ptr<TCPSocket> m_socket;