Add Protocol, ClientHandle, PacketTypes
This commit is contained in:
parent
fb6d99409e
commit
8dabf013ef
|
@ -4,6 +4,7 @@ feather_src = [
|
|||
|
||||
'network/NetworkManager.cpp',
|
||||
'network/TCPListener.cpp',
|
||||
'network/Protocol.cpp',
|
||||
|
||||
'util/StringUtil.cpp',
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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(); }
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
// Packets from Client -> Server
|
||||
enum class ServerboundPacketType
|
||||
{
|
||||
STATUS_PING_REQUEST = 0,
|
||||
STATUS_PING = 1
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue