From 2cc96422a228ca5fd4414ccf3b4a7692bbf00bf0 Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Mon, 8 Sep 2025 13:46:30 -0500 Subject: [PATCH] packet generation experiment --- client/Makefile | 5 +- client/net/NetworkManager.cpp | 60 +++++------ client/net/NetworkManager.hpp | 18 ++-- generate_packets.php | 198 ++++++++++++++++++++++++++++++++++ packets.json | 96 +++++++++++++++++ 5 files changed, 337 insertions(+), 40 deletions(-) create mode 100755 generate_packets.php create mode 100644 packets.json diff --git a/client/Makefile b/client/Makefile index f34df73..bc85d10 100644 --- a/client/Makefile +++ b/client/Makefile @@ -20,4 +20,7 @@ clean: run: $(TARGET) ./$(TARGET) -.PHONY: all clean run \ No newline at end of file +packets: + cd .. && lua generate_packets.lua + +.PHONY: all clean run packets \ No newline at end of file diff --git a/client/net/NetworkManager.cpp b/client/net/NetworkManager.cpp index 02e66b7..286bc56 100644 --- a/client/net/NetworkManager.cpp +++ b/client/net/NetworkManager.cpp @@ -31,9 +31,9 @@ void NetworkManager::startReceive() { void NetworkManager::processMessage(const uint8_t* data, std::size_t size) { if (size == 0) return; - + auto msgType = static_cast(data[0]); - + switch (msgType) { case MessageType::Spawn: handleSpawn(data, size); @@ -61,19 +61,19 @@ void NetworkManager::processMessage(const uint8_t* data, std::size_t size) { void NetworkManager::handleSpawn(const uint8_t* data, std::size_t size) { // Message format: [type(1)][id(4)][x(4)][y(4)][z(4)][colorLen(1)][color(colorLen)] if (size < 18) return; - + uint32_t id; float x, y, z; std::memcpy(&id, &data[1], sizeof(id)); std::memcpy(&x, &data[5], sizeof(x)); std::memcpy(&y, &data[9], sizeof(y)); std::memcpy(&z, &data[13], sizeof(z)); - + uint8_t colorLen = data[17]; if (size >= 18 + colorLen) { playerColor = std::string(reinterpret_cast(&data[18]), colorLen); } - + playerID = id; { std::lock_guard lock(positionMutex); @@ -85,14 +85,14 @@ void NetworkManager::handleSpawn(const uint8_t* data, std::size_t size) { void NetworkManager::handleUpdate(const uint8_t* data, std::size_t size) { if (size < 17) return; - + uint32_t id; float x, y, z; std::memcpy(&id, &data[1], sizeof(id)); std::memcpy(&x, &data[5], sizeof(x)); std::memcpy(&y, &data[9], sizeof(y)); std::memcpy(&z, &data[13], sizeof(z)); - + if (id == playerID) { std::lock_guard lock(positionMutex); serverPosition = {x, y, z}; @@ -108,20 +108,20 @@ void NetworkManager::handleUpdate(const uint8_t* data, std::size_t size) { void NetworkManager::handlePlayerJoined(const uint8_t* data, std::size_t size) { // Message format: [type(1)][id(4)][x(4)][y(4)][z(4)][colorLen(1)][color(colorLen)] if (size < 18) return; - + uint32_t id; float x, y, z; std::memcpy(&id, &data[1], sizeof(id)); std::memcpy(&x, &data[5], sizeof(x)); std::memcpy(&y, &data[9], sizeof(y)); std::memcpy(&z, &data[13], sizeof(z)); - + uint8_t colorLen = data[17]; std::string color = "red"; if (size >= 18 + colorLen) { color = std::string(reinterpret_cast(&data[18]), colorLen); } - + if (id != playerID) { std::lock_guard lock(remotePlayersMutex); remotePlayers[id] = {id, {x, y, z}, color, static_cast(GetTime())}; @@ -131,10 +131,10 @@ void NetworkManager::handlePlayerJoined(const uint8_t* data, std::size_t size) { void NetworkManager::handlePlayerLeft(const uint8_t* data, std::size_t size) { if (size < 5) return; - + uint32_t id; std::memcpy(&id, &data[1], sizeof(id)); - + std::lock_guard lock(remotePlayersMutex); remotePlayers.erase(id); std::cout << "Player " << id << " left\n"; @@ -142,13 +142,13 @@ void NetworkManager::handlePlayerLeft(const uint8_t* data, std::size_t size) { void NetworkManager::handlePlayerList(const uint8_t* data, std::size_t size) { if (size < 2) return; - + uint8_t count = data[1]; size_t offset = 2; - + std::lock_guard lock(remotePlayersMutex); remotePlayers.clear(); - + for (uint8_t i = 0; i < count && offset + 17 < size; i++) { uint32_t id; float x, y, z; @@ -156,21 +156,21 @@ void NetworkManager::handlePlayerList(const uint8_t* data, std::size_t size) { std::memcpy(&x, &data[offset + 4], sizeof(x)); std::memcpy(&y, &data[offset + 8], sizeof(y)); std::memcpy(&z, &data[offset + 12], sizeof(z)); - + uint8_t colorLen = data[offset + 16]; std::string color = "red"; - + if (offset + 17 + colorLen <= size) { color = std::string(reinterpret_cast(&data[offset + 17]), colorLen); } - + offset += 17 + colorLen; - + if (id != playerID) { remotePlayers[id] = {id, {x, y, z}, color, static_cast(GetTime())}; } } - + std::cout << "Received list of " << (int)count << " players\n"; } @@ -181,30 +181,30 @@ void NetworkManager::sendLogin() { void NetworkManager::sendMove(float dx, float dy, float dz) { if (!connected) return; - + std::array msg{}; msg[0] = static_cast(MessageType::Move); - + uint32_t id = playerID; std::memcpy(&msg[1], &id, sizeof(id)); std::memcpy(&msg[5], &dx, sizeof(dx)); std::memcpy(&msg[9], &dy, sizeof(dy)); std::memcpy(&msg[13], &dz, sizeof(dz)); - + socket.send_to(buffer(msg), serverEndpoint); } void NetworkManager::sendColorChange(const std::string& newColor) { if (!connected) return; - + std::vector msg(6 + newColor.size()); msg[0] = static_cast(MessageType::ChangeColor); - + uint32_t id = playerID; std::memcpy(&msg[1], &id, sizeof(id)); msg[5] = static_cast(newColor.size()); std::memcpy(&msg[6], newColor.data(), newColor.size()); - + socket.send_to(buffer(msg), serverEndpoint); } @@ -216,14 +216,14 @@ Vector3 NetworkManager::getPosition() { void NetworkManager::handleColorChanged(const uint8_t* data, std::size_t size) { // Message format: [type(1)][id(4)][colorLen(1)][color(colorLen)] if (size < 6) return; - + uint32_t id; std::memcpy(&id, &data[1], sizeof(id)); - + uint8_t colorLen = data[5]; if (size >= 6 + colorLen) { std::string newColor(reinterpret_cast(&data[6]), colorLen); - + if (id == playerID) { playerColor = newColor; std::cout << "Your color changed to " << newColor << "\n"; @@ -240,4 +240,4 @@ void NetworkManager::handleColorChanged(const uint8_t* data, std::size_t size) { std::unordered_map NetworkManager::getRemotePlayers() { std::lock_guard lock(remotePlayersMutex); return remotePlayers; -} \ No newline at end of file +} diff --git a/client/net/NetworkManager.hpp b/client/net/NetworkManager.hpp index b1fe6e2..a4ed64d 100644 --- a/client/net/NetworkManager.hpp +++ b/client/net/NetworkManager.hpp @@ -36,37 +36,37 @@ class NetworkManager { public: NetworkManager(); ~NetworkManager(); - + void sendLogin(); void sendMove(float dx, float dy, float dz); void sendColorChange(const std::string& newColor); - + Vector3 getPosition(); bool isConnected() const { return connected; } uint32_t getPlayerID() const { return playerID; } std::string getPlayerColor() const { return playerColor; } - + std::unordered_map getRemotePlayers(); - + // Available colors for cycling static const std::vector AVAILABLE_COLORS; - + private: io_context ioContext; udp::socket socket{ioContext}; udp::endpoint serverEndpoint; std::thread ioThread; std::array recvBuffer; - + std::atomic playerID{0}; std::string playerColor{"red"}; std::mutex positionMutex; Vector3 serverPosition{0, 0, 0}; std::atomic connected{false}; - + std::mutex remotePlayersMutex; std::unordered_map remotePlayers; - + void startReceive(); void processMessage(const uint8_t* data, std::size_t size); void handleSpawn(const uint8_t* data, std::size_t size); @@ -75,4 +75,4 @@ private: void handlePlayerLeft(const uint8_t* data, std::size_t size); void handlePlayerList(const uint8_t* data, std::size_t size); void handleColorChanged(const uint8_t* data, std::size_t size); -}; \ No newline at end of file +}; diff --git a/generate_packets.php b/generate_packets.php new file mode 100755 index 0000000..c7294d1 --- /dev/null +++ b/generate_packets.php @@ -0,0 +1,198 @@ +#!/usr/bin/env php + +#include + +CPP; + + // Generate enum + $header .= "enum class MessageType : uint8_t {\n"; + + // Sort keys for consistent output + $names = array_keys($packets['opcodes']); + sort($names); + + foreach ($names as $i => $name) { + $packet = $packets['opcodes'][$name]; + $header .= sprintf(" %s = %s", $name, $packet['id']); + if ($i < count($names) - 1) { + $header .= ","; + } + $header .= "\n"; + } + $header .= "};\n\n"; + + // Generate packet structures + $header .= "// Packet structure definitions\n"; + $header .= "namespace Packets {\n\n"; + + foreach ($names as $name) { + $packet = $packets['opcodes'][$name]; + if (!empty($packet['fields'])) { + $header .= sprintf("struct %s {\n", $name); + $header .= sprintf(" static constexpr MessageType TYPE = MessageType::%s;\n", $name); + + foreach ($packet['fields'] as $field) { + $cpp_type = ''; + + switch ($field['type']) { + case 'string': + $cpp_type = 'std::string'; + break; + case 'uint8': + $cpp_type = 'uint8_t'; + break; + case 'uint32': + $cpp_type = 'uint32_t'; + break; + case 'float32': + $cpp_type = 'float'; + break; + case 'array': + $header .= sprintf(" // Array field: %s - requires custom handling\n", $field['name']); + continue 2; + } + + $header .= sprintf(" %s %s;\n", $cpp_type, $field['name']); + } + + $header .= "};\n\n"; + } + } + + $header .= "} // namespace Packets\n"; + + return $header; +} + +/** + * Generate Go file with packet definitions + */ +function generate_go_file($packets) { + $go_file = <<getMessage() . "\n"; + exit(1); +} diff --git a/packets.json b/packets.json new file mode 100644 index 0000000..37636cf --- /dev/null +++ b/packets.json @@ -0,0 +1,96 @@ +{ + "version": "1.0.0", + "opcodes": { + "Login": { + "id": "0x01", + "fields": [] + }, + "Position": { + "id": "0x02", + "fields": [ + {"name": "x", "type": "float32"}, + {"name": "y", "type": "float32"}, + {"name": "z", "type": "float32"} + ] + }, + "Spawn": { + "id": "0x03", + "fields": [ + {"name": "playerId", "type": "uint32"}, + {"name": "x", "type": "float32"}, + {"name": "y", "type": "float32"}, + {"name": "z", "type": "float32"}, + {"name": "color", "type": "string"} + ] + }, + "Move": { + "id": "0x04", + "fields": [ + {"name": "playerId", "type": "uint32"}, + {"name": "dx", "type": "float32"}, + {"name": "dy", "type": "float32"}, + {"name": "dz", "type": "float32"} + ] + }, + "Update": { + "id": "0x05", + "fields": [ + {"name": "playerId", "type": "uint32"}, + {"name": "x", "type": "float32"}, + {"name": "y", "type": "float32"}, + {"name": "z", "type": "float32"} + ] + }, + "PlayerJoined": { + "id": "0x06", + "fields": [ + {"name": "playerId", "type": "uint32"}, + {"name": "x", "type": "float32"}, + {"name": "y", "type": "float32"}, + {"name": "z", "type": "float32"}, + {"name": "color", "type": "string"} + ] + }, + "PlayerLeft": { + "id": "0x07", + "fields": [ + {"name": "playerId", "type": "uint32"} + ] + }, + "PlayerList": { + "id": "0x08", + "fields": [ + {"name": "count", "type": "uint8"}, + {"name": "players", "type": "array", "itemType": { + "fields": [ + {"name": "playerId", "type": "uint32"}, + {"name": "x", "type": "float32"}, + {"name": "y", "type": "float32"}, + {"name": "z", "type": "float32"}, + {"name": "color", "type": "string"} + ] + }} + ] + }, + "ChangeColor": { + "id": "0x09", + "fields": [ + {"name": "playerId", "type": "uint32"}, + {"name": "color", "type": "string"} + ] + }, + "ColorChanged": { + "id": "0x0A", + "fields": [ + {"name": "playerId", "type": "uint32"}, + {"name": "color", "type": "string"} + ] + } + }, + "types": { + "uint8": {"size": 1, "encoding": "little-endian"}, + "uint32": {"size": 4, "encoding": "little-endian"}, + "float32": {"size": 4, "encoding": "little-endian"}, + "string": {"encoding": "length-prefixed", "lengthType": "uint8"} + } +} \ No newline at end of file