From 58d37086c6b6756b52d30862391e09473e6b4383 Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Mon, 24 Feb 2025 11:42:09 -0600 Subject: [PATCH] miku gamer --- src/dusk/game.c | 41 ++++++++-- src/dusk/net/CMakeLists.txt | 1 + src/dusk/net/client/CMakeLists.txt | 10 +++ src/dusk/net/client/client.c | 119 +++++++++++++++++++++++++++++ src/dusk/net/client/client.h | 43 +++++++++++ src/dusk/net/packet.h | 3 +- src/dusk/net/server/server.c | 4 + src/dusk/net/server/serverclient.c | 58 +++++++++++--- src/dusk/net/server/serverclient.h | 10 ++- 9 files changed, 267 insertions(+), 22 deletions(-) create mode 100644 src/dusk/net/client/CMakeLists.txt create mode 100644 src/dusk/net/client/client.c create mode 100644 src/dusk/net/client/client.h diff --git a/src/dusk/game.c b/src/dusk/game.c index 0ea17dd..0dd1916 100644 --- a/src/dusk/game.c +++ b/src/dusk/game.c @@ -10,29 +10,54 @@ #include "input.h" #include "console/console.h" #include "net/server/server.h" +#include "net/client/client.h" -#include "entity/entity.h" +int32_t status = 0; void gameInit() { consoleInit(); gameTimeInit(); inputInit(); - serverInit((serverinit_t){ - .type = SERVER_TYPE_ONLINE, - .online = { - .port = SERVER_PORT_DEFAULT - } - }); + consolePrint("Press LEFT to start server, RIGHT to join server."); - entityInit(&ENTITY_TEST); + memset(&SERVER, 0, sizeof(server_t)); + memset(&CLIENT, 0, sizeof(client_t)); + + // serverInit((serverinit_t){ + // .type = SERVER_TYPE_ONLINE, + // .online = { + // .port = SERVER_PORT_DEFAULT + // } + // }); } void gameUpdate(const float delta) { gameTimeUpdate(delta); inputUpdate(); + + switch(status) { + case 0: + if(inputWasPressed(INPUT_LEFT)) { + + serverInit((serverinit_t){ + .type = SERVER_TYPE_ONLINE, + .online = { + .port = SERVER_PORT_DEFAULT + } + }); + } else if(inputWasPressed(INPUT_RIGHT)) { + + clientInit((clientinit_t){ + .host = "127.0.0.1", + .port = SERVER_PORT_DEFAULT + }); + } + break; + } } void gameDispose() { + clientDispose(); serverDispose(); } \ No newline at end of file diff --git a/src/dusk/net/CMakeLists.txt b/src/dusk/net/CMakeLists.txt index 22ebcf1..3b38f58 100644 --- a/src/dusk/net/CMakeLists.txt +++ b/src/dusk/net/CMakeLists.txt @@ -9,4 +9,5 @@ target_sources(${DUSK_TARGET_NAME} ) # Subdirs +add_subdirectory(client) add_subdirectory(server) \ No newline at end of file diff --git a/src/dusk/net/client/CMakeLists.txt b/src/dusk/net/client/CMakeLists.txt new file mode 100644 index 0000000..a7b255a --- /dev/null +++ b/src/dusk/net/client/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2023 Dominic Masters +# +# This software is released under the MIT License. +# https:#opensource.org/licenses/MIT + +# Sources +target_sources(${DUSK_TARGET_NAME} + PRIVATE + client.c +) \ No newline at end of file diff --git a/src/dusk/net/client/client.c b/src/dusk/net/client/client.c new file mode 100644 index 0000000..91c98c0 --- /dev/null +++ b/src/dusk/net/client/client.c @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "client.h" +#include "console/console.h" +#include "assert/assert.h" + +client_t CLIENT; + +void clientInit(const clientinit_t init) { + memset(&CLIENT, 0, sizeof(client_t)); + CLIENT.state = CLIENT_STATE_CONNECTING; + + CLIENT.socket = socket(AF_INET, SOCK_STREAM, 0); + CLIENT.serverAddress.sin_family = AF_INET; + CLIENT.serverAddress.sin_port = htons(init.port); + + if(inet_pton(AF_INET, init.host, &CLIENT.serverAddress.sin_addr) <= 0) { + assertUnreachable("Invalid address or address not supported."); + } + + if(connect(CLIENT.socket, (struct sockaddr *)&CLIENT.serverAddress, sizeof(CLIENT.serverAddress)) < 0) { + assertUnreachable("Connection failed."); + } + + pthread_create(&CLIENT.thread, NULL, clientThread, NULL); + + // Wait for client to connect + while(CLIENT.state == CLIENT_STATE_CONNECTING) usleep(1000); + + consolePrint("Connected to server."); +} + +void * clientThread(void* arg) { + assertNull(arg, "Client thread does not accept arguments."); + + + // Server sends 16 bytes. + { + char_t buffer[16]; + consolePrint("Reading first packet."); + if(_clientReceive((uint8_t*)buffer, sizeof(buffer)) != sizeof(buffer)) { + assertUnreachable("Failed to read initial server data."); + } + + // First 4 bytes must be "Dusk" + if(memcmp(buffer, "Dusk", 4) != 0) { + assertUnreachable("Invalid server data."); + } + + // Next, server will send its version number, we compare to DUSK_VERSION + char_t cmp[] = DUSK_VERSION; + if(memcmp(buffer+4, cmp, 4) != 0) { + assertUnreachable("Server version mismatch."); + } + } + + // Send "OK" and nothing else (don't send NULL term) + if(_clientSend((uint8_t*)"OK", 2) != 2) { + assertUnreachable("Failed to send OK to server."); + } + + // Server will send the first properly formed packet now. + + CLIENT.state = CLIENT_STATE_CONNECTED; + while(CLIENT.state == CLIENT_STATE_CONNECTED) { + usleep(1000); + } + + CLIENT.state = CLIENT_STATE_DISCONNECTED; + close(CLIENT.socket); + return NULL; +} + +ssize_t _clientSend(const uint8_t *data, const size_t size) { + assertNotNull(data, "Data cannot be NULL."); + assertTrue(size > 0, "Size cannot be zero."); + + return send(CLIENT.socket, data, size, 0); +} + +ssize_t _clientReceive(uint8_t *data, const size_t size) { + assertNotNull(data, "Data cannot be NULL."); + assertTrue(size > 0, "Size cannot be zero."); + + fd_set readfds; + struct timeval timeout; + int32_t activity; + + FD_ZERO(&readfds); + FD_SET(CLIENT.socket, &readfds); + timeout.tv_sec = 5; // 5 seconds timeout + timeout.tv_usec = 0; + + activity = select(CLIENT.socket + 1, &readfds, NULL, NULL, &timeout); + if(activity <= 0) { + assertUnreachable("Timeout or error while waiting for server data."); + } + + return recv(CLIENT.socket, data, size, 0); +} + +void clientDispose() { + if(CLIENT.state == CLIENT_STATE_CONNECTED) { + CLIENT.state = CLIENT_STATE_DISCONNECTING; + + // Wait for client to disconnect + while(CLIENT.state == CLIENT_STATE_DISCONNECTING) usleep(1000); + pthread_join(CLIENT.thread, NULL); + + consolePrint("Disconnected from server."); + } + + close(CLIENT.socket); +} \ No newline at end of file diff --git a/src/dusk/net/client/client.h b/src/dusk/net/client/client.h new file mode 100644 index 0000000..42ecaee --- /dev/null +++ b/src/dusk/net/client/client.h @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" +#include +#include +#include +#include + +#define CLIENT_HOST_STR_LEN 256 + +typedef enum { + CLIENT_STATE_DISCONNECTED, + CLIENT_STATE_CONNECTING, + CLIENT_STATE_CONNECTED, + CLIENT_STATE_DISCONNECTING +} clientstate_t; + +typedef struct { + clientstate_t state; + int32_t socket; + pthread_t thread; + struct sockaddr_in serverAddress; +} client_t; + +typedef struct { + char_t host[CLIENT_HOST_STR_LEN+1]; + uint32_t port; +} clientinit_t; + +extern client_t CLIENT; + +void clientInit(const clientinit_t init); +void clientDispose(); +void * clientThread(void* arg); + +ssize_t _clientSend(const uint8_t *data, const size_t size); +ssize_t _clientReceive(uint8_t *data, const size_t size); \ No newline at end of file diff --git a/src/dusk/net/packet.h b/src/dusk/net/packet.h index 381be94..5ec76b4 100644 --- a/src/dusk/net/packet.h +++ b/src/dusk/net/packet.h @@ -9,8 +9,7 @@ #include "dusk.h" typedef enum { - PACKET_TYPE_HELLO, - PACKET_TYPE_PING + PACKET_TYPE_CONNECTED } packettype_t; typedef struct { diff --git a/src/dusk/net/server/server.c b/src/dusk/net/server/server.c index e9d946f..f2de108 100644 --- a/src/dusk/net/server/server.c +++ b/src/dusk/net/server/server.c @@ -54,6 +54,8 @@ void serverInit(const serverinit_t init) { // Wait for server to start while(SERVER.state == SERVER_STATE_STARTING) usleep(1000); + + consolePrint("Server started"); } void * serverThread(void *arg) { @@ -141,5 +143,7 @@ void serverDispose() { assertUnreachable("Invalid server type."); break; } + + consolePrint("Server stopped"); } } \ No newline at end of file diff --git a/src/dusk/net/server/serverclient.c b/src/dusk/net/server/serverclient.c index 2755a0a..71f9305 100644 --- a/src/dusk/net/server/serverclient.c +++ b/src/dusk/net/server/serverclient.c @@ -31,29 +31,40 @@ void * serverClientThread(void *arg) { serverclient_t *client = (serverclient_t*)arg; assertNotNull(client, "Client cannot be NULL."); - // Set the client state to connected - client->state = SERVER_CLIENT_STATE_CONNECTED; - // Send hello to client, as well as the version number. { uint8_t hello[16]; memset(hello, 0, sizeof(hello)); memcpy(hello, "Dusk", 4); strcpy((char*)&hello[4], DUSK_VERSION); - - _serverClientSend(client, hello, sizeof(hello)); + ssize_t bytesSent = serverClientSend(client, hello, sizeof(hello)); + assertTrue( + bytesSent == sizeof(hello), + "Failed to send hello to client." + ); } // Client should respond "OK" { uint8_t response[2]; - _serverClientReceive(client, response, sizeof(response)); + ssize_t bytesReceived = serverClientReceive(client, response, sizeof(response)); assertTrue( + bytesReceived == sizeof(response) && response[0] == 'O' && response[1] == 'K', "Client did not respond with OK." ); } + + // At this point we can start communicating "properly", so let's send the + // first real packet. + packet_t packet = { .type = PACKET_TYPE_CONNECTED }; + if(!serverClientSendPacket(client, &packet)) { + assertUnreachable("Failed to send connected packet to client."); + } + + // Set the client state to connected + client->state = SERVER_CLIENT_STATE_CONNECTED; while(client->state == SERVER_CLIENT_STATE_CONNECTED) { // Do nothing for now. @@ -65,7 +76,7 @@ void * serverClientThread(void *arg) { return NULL; } -void _serverClientSend( +ssize_t serverClientSend( serverclient_t *client, const uint8_t *data, const size_t size @@ -73,9 +84,11 @@ void _serverClientSend( assertNotNull(client, "Client cannot be NULL."); assertNotNull(data, "Data cannot be NULL."); assertTrue(size > 0, "Size cannot be zero."); + + return send(client->socket, data, size, 0); } -void _serverClientReceive( +ssize_t serverClientReceive( serverclient_t *client, uint8_t *data, const size_t size @@ -83,8 +96,33 @@ void _serverClientReceive( assertNotNull(client, "Client cannot be NULL."); assertNotNull(data, "Data cannot be NULL."); assertTrue(size > 0, "Size cannot be zero."); - - recv(client->socket, data, size, 0); + + fd_set readfds; + struct timeval timeout; + int32_t activity; + + FD_ZERO(&readfds); + FD_SET(client->socket, &readfds); + timeout.tv_sec = 5; // 5 seconds timeout + timeout.tv_usec = 0; + + activity = select(client->socket + 1, &readfds, NULL, NULL, &timeout); + if(activity <= 0) { + assertUnreachable("Timeout or error while waiting for client data."); + } + + return recv(client->socket, data, size, 0); +} + +bool_t serverClientSendPacket( + serverclient_t *client, + const packet_t *packet +) { + assertNotNull(client, "Client cannot be NULL."); + assertNotNull(packet, "Packet cannot be NULL."); + + ssize_t sent = serverClientSend(client, (const uint8_t*)packet, sizeof(packet_t)); + return sent == sizeof(packet_t); } void serverClientDispose(serverclient_t *client) { diff --git a/src/dusk/net/server/serverclient.h b/src/dusk/net/server/serverclient.h index e5b5c5f..18bacab 100644 --- a/src/dusk/net/server/serverclient.h +++ b/src/dusk/net/server/serverclient.h @@ -7,6 +7,7 @@ #pragma once #include "dusk.h" +#include "net/packet.h" #include #include @@ -30,16 +31,21 @@ typedef struct { void serverClientInit(serverclient_t *client, const serverclientinit_t init); void * serverClientThread(void *arg); -void _serverClientSend( +ssize_t serverClientSend( serverclient_t *client, const uint8_t *data, const size_t size ); -void _serverClientReceive( +ssize_t serverClientReceive( serverclient_t *client, uint8_t *buffer, const size_t size ); +bool_t serverClientSendPacket( + serverclient_t *client, + const packet_t *packet +); + void serverClientDispose(serverclient_t *client); \ No newline at end of file