#include #include #include #include #include #include #include #include #include /* Networking Design Notes * * All packets are prefixed with `CaveGame`'s ascii representation. This functions as a header. * After that, an 8-bit number indicates the message type of the packet. * */ using namespace CaveGame; using namespace CaveGame::Core; using namespace std::chrono_literals; using namespace J3ML::SizedIntegralTypes; template void test(T value, T expected) { if (value != expected) Logs::Error(std::format("rtt<{}> fail: expected {}, value {}", Reflection::type_name(), expected, value)); else Logs::Server(std::format("rtt<{}> success: expected {}, value {}", Reflection::type_name(), expected, value)); } template void test_diff(T value, T expected, T epsilon) { T diff = std::abs(std::abs(value) - std::abs(expected)); if (diff > epsilon) Logs::Error(std::format("rtt<{}> fail: expected {}, value {}, diff {}", Reflection::type_name(), expected, value, diff)); else Logs::Server(std::format("rtt<{}> success: expected {}, value {}, diff {}", Reflection::type_name(), expected, value, diff)); } bool server_running = true; bool client_running = true; void RunServer() { std::cout << "Starting server thread." << std::endl; UDPServer<32776> udpServer; udpServer.onRawMessageReceived = [&](const char* message, int length, std::string ipv4, uint16_t port) { Logs::Server(std::format("recv from {}:{} => {} ({} bytes)", ipv4, port, std::string(message), length)); //std::cout << "server: " << ipv4 << ":" << port << " => " << message << "(" << length << ")" << std::endl; if (length == 32776) { std::cout << "peter" << std::endl; } if (message[0] == 21) { Logs::Server(std::format("Received encoded-data buffer, decoding...")); Buffer b; b.size = length; b.data = new uint8_t[length]; b.index = 1; memcpy(b.data, message, length); test(read_string(b), "encoded-data"); test(read_u8(b), 7); test(read_u8(b), 12); test(read_u16(b), 32000); test(read_u32(b), 666420); test(read_u64(b), 420690000); test(read_s8(b), -64); test(read_f32(b), 3.14159f); test_diff(read_f16(b), 3.14159f, 1e-2f); test(read_string(b), "Tetsuo!.!.!..!!..!!"); } if (strcmp(message, "ping") == 0) { udpServer.SendTo("pong", ipv4, port); //return; } if (strcmp(message, "poll") == 0) { udpServer.SendTo("CaveGame Server v1.0.0 Protocol v1.", ipv4, port); //return; } //udpServer.SendTo(message, length, ipv4, port); }; // Bind the server to a port. udpServer.Bind(42069, [](int errorCode, std::string errorMessage) { std::cout << errorCode << ":" << errorMessage << std::endl; }); std::cout << "Server running on port " << udpServer.RemotePort() << std::endl; // You should do an input loop, so the program won't terminate immediately. //std::string input; //getline(std::cin, input); //while (input != "exit") { // getline(std::cin, input); //} while (server_running) { std::this_thread::sleep_for(10ms); } std::cout << "Server Closing" << std::endl; udpServer.Close(); } /* Packet - up to 1024 bytes of data. PacketType - Unreliable, Reliable, ReliableOrdered Packet data is appended to NetworkDataBuffer, . Messages are read out of NetworkDataBuffer. Each message has an ACK id which upon receiving, you reply to your peer with a list of ACKs, unacknowledged messages are re-sent in proceeding packets, up to N retries. */ void RunClient() { CaveGame::Core::Generator* generator = new Generator(0); CaveGame::Core::Chunk* chk = new Chunk({0, 5}); generator->FirstPass(chk); std::cout << "Starting client thread." << std::endl; const std::string IP = "localhost"; const uint16_t PORT = 42069; UDPSocket<32776> udpSocket(true); udpSocket.Connect(IP, PORT); udpSocket.onRawMessageReceived = [&](const char* message, int length, std::string ipv4, uint16_t port) { Logs::Client(std::format("recv from")); //std::cout << "received: " << ipv4 << ":" << port << " => " << message << "(" << length << ")" << std::endl; }; udpSocket.Send("ping"); std::this_thread::sleep_for(10ms); udpSocket.Send("poll"); std::this_thread::sleep_for(10ms); for (int i = 0; i < 1000; i++) { udpSocket.Send(std::format("Sequence of messages in rapid succession {}/1000.", i+1)); if (i % 20 == 0) std::this_thread::sleep_for(20ms); } Buffer test; test.index = 0; test.size = 512; test.data = new uint8_t[384]; write_u8(test, 21); write_string(test, "encoded-data"); write_u8(test, 7); write_u8(test, 12); write_u16(test, 32000); write_u32(test, 666420); write_u64(test, 420690000); write_s8(test, -64); write_f32(test, 3.14159f); write_f16(test, 3.14159f); write_string(test, "Tetsuo!.!.!..!!..!!"); udpSocket.Send((const char*)test.data, test.size); std::this_thread::sleep_for(10ms); test.index = 0; test.size = 32776; test.data = new uint8_t[32776]; chk->write(test); uint8_t messages[33][1024]; for (int i = 0; i < 33; i++) { memcpy(&messages[i], (test.data+test.index)+(i*1024), 1024); udpSocket.Send((const char*)messages[i], 1024); } //udpSocket.Send((const char*)test.data, test.size); while (client_running) { std::this_thread::sleep_for(10ms); } std::cout << "Client closing." << std::endl; } namespace Reliability { struct Header { uint16_t sequence; // A number that increases with each packet sent. uint16_t ack; // The most recent packet sequence number received. uint32_t ack_bits; // A bitfield encoding the set of acked packets. }; const int BufferSize = 1024; uint32_t sequence_buffer[BufferSize]; struct PacketData { bool acked; double send_time; }; PacketData packet_data[BufferSize]; PacketData * GetPacketData(uint16_t sequence) { const int index = sequence % BufferSize; if (sequence_buffer[index] == sequence) return &packet_data[index]; else return nullptr; } PacketData & InsertPacketData(uint16_t sequence) { const int index = sequence % BufferSize; sequence_buffer[index] = sequence; return packet_data[index]; } struct Message {}; struct TestMessage : public Message { }; } using namespace CaveGame::Core; struct Header { std::string watermark = "RCG25"; void Write(Buffer& buffer) { write_string(buffer, watermark); } void Read(Buffer& buffer) { watermark = read_string(buffer); } }; class Packet { public: Header header; virtual void Write(Buffer& buffer) { header.Write(buffer); } virtual void Read(Buffer& buffer) { header.Read(buffer); } }; class ChatPacket : public Packet { public: std::string sender; std::string message; void Read(CaveGame::Core::Buffer &buffer) override { Packet::Read(buffer); write_string(buffer, sender); write_string(buffer, message); } void Write(CaveGame::Core::Buffer &buffer) override { Packet::Write(buffer); sender = read_string(buffer); message = read_string(buffer); } }; // TODO: Boneyard structure - Rare surface "dungeon" int main(int argc, char** argv) { Logs::Server.IncludeLocation(false); Logs::Client.IncludeLocation(false); std::cout << "Running Program" << std::endl; std::thread server(RunServer); std::thread client(RunClient); server.detach(); std::this_thread::sleep_for(100ms); client.detach(); std::string input; getline(std::cin, input); while (input != "exit") { getline(std::cin, input); } server_running = false; client_running = false; return 0; }