Files
ReCaveGame/ServerApp/main.cpp
2025-02-10 14:43:12 -05:00

339 lines
8.4 KiB
C++

#include <vector>
#include <string>
#include <iostream>
#include <Sockets/UdpServer.hpp>
#include <Core/Serialization.hpp>
#include <Core/Loggers.hpp>
#include <J3ML/J3ML.hpp>
#include <Core/Reflection.hpp>
#include <Core/Generator.hpp>
/* 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 <typename T>
void test(T value, T expected)
{
if (value != expected)
Logs::Error(std::format("rtt<{}> fail: expected {}, value {}", Reflection::type_name<T>(), expected, value));
else
Logs::Server(std::format("rtt<{}> success: expected {}, value {}", Reflection::type_name<T>(), expected, value));
}
template <typename T>
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<T>(), expected, value, diff));
else
Logs::Server(std::format("rtt<{}> success: expected {}, value {}, diff {}", Reflection::type_name<T>(), 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<std::string>(read_string(b), "encoded-data");
test<u8>(read_u8(b), 7);
test<u8>(read_u8(b), 12);
test<u16>(read_u16(b), 32000);
test<u32>(read_u32(b), 666420);
test<u64>(read_u64(b), 420690000);
test<s8>(read_s8(b), -64);
test<float>(read_f32(b), 3.14159f);
test_diff<float>(read_f16(b), 3.14159f, 1e-2f);
test<std::string>(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;
}