From 8dabf013ef6cb22c762f480917a343e0e017b954 Mon Sep 17 00:00:00 2001 From: DankParrot Date: Mon, 27 Jul 2020 21:45:03 -0700 Subject: [PATCH] Add Protocol, ClientHandle, PacketTypes --- src/meson.build | 1 + src/network/ClientHandle.h | 24 ++++++++ src/network/NetworkMessage.h | 13 +++++ src/network/PacketReader.h | 41 ++++++++++++- src/network/PacketTypes.h | 8 +++ src/network/Protocol.cpp | 109 +++++++++++++++++++++++++++++++++++ src/network/Protocol.h | 33 +++++++++++ src/network/TCPListener.cpp | 62 ++++++++++++++++---- src/network/TCPListener.h | 6 +- 9 files changed, 283 insertions(+), 14 deletions(-) create mode 100644 src/network/ClientHandle.h create mode 100644 src/network/PacketTypes.h create mode 100644 src/network/Protocol.cpp create mode 100644 src/network/Protocol.h diff --git a/src/meson.build b/src/meson.build index 8bd1c1b..9fc1449 100644 --- a/src/meson.build +++ b/src/meson.build @@ -4,6 +4,7 @@ feather_src = [ 'network/NetworkManager.cpp', 'network/TCPListener.cpp', + 'network/Protocol.cpp', 'util/StringUtil.cpp', diff --git a/src/network/ClientHandle.h b/src/network/ClientHandle.h new file mode 100644 index 0000000..71c5bfb --- /dev/null +++ b/src/network/ClientHandle.h @@ -0,0 +1,24 @@ +#pragma once + +#include "NetworkMessage.h" + +#include + +using std::queue; + +namespace Feather::Network +{ + class ClientHandle + { + private: + friend class TCPListener; + + queue m_queue; + + public: + void SendPacket(NetworkMessage* msg) + { + m_queue.push(msg); + } + }; +} \ No newline at end of file diff --git a/src/network/NetworkMessage.h b/src/network/NetworkMessage.h index 0138102..41314d5 100644 --- a/src/network/NetworkMessage.h +++ b/src/network/NetworkMessage.h @@ -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(); } diff --git a/src/network/PacketReader.h b/src/network/PacketReader.h index 22118f7..339c527 100644 --- a/src/network/PacketReader.h +++ b/src/network/PacketReader.h @@ -1,7 +1,12 @@ #pragma once +#include "../Common.h" + #include #include +#include + +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; diff --git a/src/network/PacketTypes.h b/src/network/PacketTypes.h new file mode 100644 index 0000000..cb234cf --- /dev/null +++ b/src/network/PacketTypes.h @@ -0,0 +1,8 @@ +#pragma once + +// Packets from Client -> Server +enum class ServerboundPacketType +{ + STATUS_PING_REQUEST = 0, + STATUS_PING = 1 +}; \ No newline at end of file diff --git a/src/network/Protocol.cpp b/src/network/Protocol.cpp new file mode 100644 index 0000000..4b9ef6b --- /dev/null +++ b/src/network/Protocol.cpp @@ -0,0 +1,109 @@ +#include "Protocol.h" +#include "PacketReader.h" +#include "PacketTypes.h" + +#include + +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(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; + } +} \ No newline at end of file diff --git a/src/network/Protocol.h b/src/network/Protocol.h new file mode 100644 index 0000000..cd83a7f --- /dev/null +++ b/src/network/Protocol.h @@ -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); + }; +} \ No newline at end of file diff --git a/src/network/TCPListener.cpp b/src/network/TCPListener.cpp index 5893cb4..5388bc5 100644 --- a/src/network/TCPListener.cpp +++ b/src/network/TCPListener.cpp @@ -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 #include @@ -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 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(self)->ReadCallback(); }, [](bufferevent* be, void* self) { static_cast(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 @@ -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 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(); + } + } + } } \ No newline at end of file diff --git a/src/network/TCPListener.h b/src/network/TCPListener.h index 66b7bd1..f92e4e8 100644 --- a/src/network/TCPListener.h +++ b/src/network/TCPListener.h @@ -5,7 +5,9 @@ #include 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 m_socket;