From cebe1bb05c764a1ebfd114f7c7e779d4f90b816c Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Sat, 22 Feb 2025 20:22:58 -0600 Subject: [PATCH] prog --- {src/dusk => backup}/client/CMakeLists.txt | 0 {src/dusk => backup}/client/client.c | 0 {src/dusk => backup}/client/client.h | 0 {src/dusk => backup}/client/clientremote.c | 24 ++- {src/dusk => backup}/client/clientremote.h | 0 {src/dusk => backup}/server/CMakeLists.txt | 0 {src/dusk => backup}/server/server.c | 0 {src/dusk => backup}/server/server.h | 0 {src/dusk => backup}/server/serverlocal.c | 8 +- {src/dusk => backup}/server/serverlocal.h | 0 .../server/serversingleplayer.c | 0 .../server/serversingleplayer.h | 0 src/dusk/CMakeLists.txt | 3 +- src/dusk/dusk.h | 4 +- src/dusk/game.c | 12 +- src/dusk/net/CMakeLists.txt | 12 ++ src/dusk/net/server/CMakeLists.txt | 11 ++ src/dusk/net/server/server.c | 145 ++++++++++++++++++ src/dusk/net/server/server.h | 55 +++++++ src/dusk/net/server/serverclient.c | 101 ++++++++++++ src/dusk/net/server/serverclient.h | 45 ++++++ 21 files changed, 402 insertions(+), 18 deletions(-) rename {src/dusk => backup}/client/CMakeLists.txt (100%) rename {src/dusk => backup}/client/client.c (100%) rename {src/dusk => backup}/client/client.h (100%) rename {src/dusk => backup}/client/clientremote.c (56%) rename {src/dusk => backup}/client/clientremote.h (100%) rename {src/dusk => backup}/server/CMakeLists.txt (100%) rename {src/dusk => backup}/server/server.c (100%) rename {src/dusk => backup}/server/server.h (100%) rename {src/dusk => backup}/server/serverlocal.c (100%) rename {src/dusk => backup}/server/serverlocal.h (100%) rename {src/dusk => backup}/server/serversingleplayer.c (100%) rename {src/dusk => backup}/server/serversingleplayer.h (100%) create mode 100644 src/dusk/net/CMakeLists.txt create mode 100644 src/dusk/net/server/CMakeLists.txt create mode 100644 src/dusk/net/server/server.c create mode 100644 src/dusk/net/server/server.h create mode 100644 src/dusk/net/server/serverclient.c create mode 100644 src/dusk/net/server/serverclient.h diff --git a/src/dusk/client/CMakeLists.txt b/backup/client/CMakeLists.txt similarity index 100% rename from src/dusk/client/CMakeLists.txt rename to backup/client/CMakeLists.txt diff --git a/src/dusk/client/client.c b/backup/client/client.c similarity index 100% rename from src/dusk/client/client.c rename to backup/client/client.c diff --git a/src/dusk/client/client.h b/backup/client/client.h similarity index 100% rename from src/dusk/client/client.h rename to backup/client/client.h diff --git a/src/dusk/client/clientremote.c b/backup/client/clientremote.c similarity index 56% rename from src/dusk/client/clientremote.c rename to backup/client/clientremote.c index e736ad9..9f35fa8 100644 --- a/src/dusk/client/clientremote.c +++ b/backup/client/clientremote.c @@ -8,6 +8,8 @@ #include "client.h" #include "assert/assert.h" #include "console/console.h" +#include +#include void* clientRemoteThreadFunc(void* arg) { client_t *client = (client_t*)arg; @@ -15,10 +17,24 @@ void* clientRemoteThreadFunc(void* arg) { client->state = CLIENT_STATE_CONNECTED; - // Send some data - write(client->remote.clientSockDesc, "Hello, World!", 13); - - printf("Thread func\n"); + // Set socket to non-blocking + int flags = fcntl(client->remote.clientSockDesc, F_GETFL, 0); + fcntl(client->remote.clientSockDesc, F_SETFL, flags | O_NONBLOCK); + + char buffer[1024]; + while (client->state == CLIENT_STATE_CONNECTED) { + ssize_t bytesRead = read(client->remote.clientSockDesc, buffer, sizeof(buffer)); + if (bytesRead > 0) { + // Process the received data + printf("Received data: %.*s\n", (int)bytesRead, buffer); + } else if (bytesRead == -1 && errno != EAGAIN && errno != EWOULDBLOCK) { + // An error occurred + perror("Read error"); + break; + } + // Sleep for a short duration to prevent busy-waiting + usleep(10000); // 10ms + } return NULL; } diff --git a/src/dusk/client/clientremote.h b/backup/client/clientremote.h similarity index 100% rename from src/dusk/client/clientremote.h rename to backup/client/clientremote.h diff --git a/src/dusk/server/CMakeLists.txt b/backup/server/CMakeLists.txt similarity index 100% rename from src/dusk/server/CMakeLists.txt rename to backup/server/CMakeLists.txt diff --git a/src/dusk/server/server.c b/backup/server/server.c similarity index 100% rename from src/dusk/server/server.c rename to backup/server/server.c diff --git a/src/dusk/server/server.h b/backup/server/server.h similarity index 100% rename from src/dusk/server/server.h rename to backup/server/server.h diff --git a/src/dusk/server/serverlocal.c b/backup/server/serverlocal.c similarity index 100% rename from src/dusk/server/serverlocal.c rename to backup/server/serverlocal.c index dab079b..0c15f20 100644 --- a/src/dusk/server/serverlocal.c +++ b/backup/server/serverlocal.c @@ -17,10 +17,6 @@ void serverLocalStart(const serverlocalstart_t start) { assertUnreachable("Failed to create socket"); } - // Set the socket to non-blocking mode - int flags = fcntl(SERVER.local.sockDesc, F_GETFL, 0); - fcntl(SERVER.local.sockDesc, F_SETFL, flags | O_NONBLOCK); - //Bind the socket SERVER.local.sockAddr.sin_family = AF_INET; SERVER.local.sockAddr.sin_addr.s_addr = INADDR_ANY; @@ -78,6 +74,10 @@ void * serverLocalThread(void *arg) { timeout.tv_sec = 0; timeout.tv_usec = SERVER_LOCAL_THREAD_ACCEPT_SLEEP_TIME; + // Set the socket to non-blocking mode + int flags = fcntl(SERVER.local.sockDesc, F_GETFL, 0); + fcntl(SERVER.local.sockDesc, F_SETFL, flags | O_NONBLOCK); + while(SERVER.local.threadState == SERVER_LOCAL_THREAD_STATE_RUNNING) { FD_ZERO(&readfds); FD_SET(SERVER.local.sockDesc, &readfds); diff --git a/src/dusk/server/serverlocal.h b/backup/server/serverlocal.h similarity index 100% rename from src/dusk/server/serverlocal.h rename to backup/server/serverlocal.h diff --git a/src/dusk/server/serversingleplayer.c b/backup/server/serversingleplayer.c similarity index 100% rename from src/dusk/server/serversingleplayer.c rename to backup/server/serversingleplayer.c diff --git a/src/dusk/server/serversingleplayer.h b/backup/server/serversingleplayer.h similarity index 100% rename from src/dusk/server/serversingleplayer.h rename to backup/server/serversingleplayer.h diff --git a/src/dusk/CMakeLists.txt b/src/dusk/CMakeLists.txt index 3529a5e..178610a 100644 --- a/src/dusk/CMakeLists.txt +++ b/src/dusk/CMakeLists.txt @@ -26,7 +26,6 @@ target_sources(${DUSK_TARGET_NAME} # Subdirs add_subdirectory(assert) add_subdirectory(console) -add_subdirectory(client) -add_subdirectory(server) +add_subdirectory(net) add_subdirectory(entity) add_subdirectory(display) \ No newline at end of file diff --git a/src/dusk/dusk.h b/src/dusk/dusk.h index 47c7111..51d88a1 100644 --- a/src/dusk/dusk.h +++ b/src/dusk/dusk.h @@ -22,4 +22,6 @@ typedef float float_t; typedef double double_t; typedef bool bool_t; -typedef char char_t; \ No newline at end of file +typedef char char_t; + +#define DUSK_VERSION "1.0.0" \ No newline at end of file diff --git a/src/dusk/game.c b/src/dusk/game.c index 2978cc4..0ea17dd 100644 --- a/src/dusk/game.c +++ b/src/dusk/game.c @@ -9,7 +9,7 @@ #include "gametime.h" #include "input.h" #include "console/console.h" -#include "server/server.h" +#include "net/server/server.h" #include "entity/entity.h" @@ -18,11 +18,10 @@ void gameInit() { gameTimeInit(); inputInit(); - serverInit(); - serverStart((serverstart_t){ - .type = SERVER_TYPE_LOCAL, - .local = { - .port = SERVER_LOCAL_PORT_DEFAULT + serverInit((serverinit_t){ + .type = SERVER_TYPE_ONLINE, + .online = { + .port = SERVER_PORT_DEFAULT } }); @@ -32,7 +31,6 @@ void gameInit() { void gameUpdate(const float delta) { gameTimeUpdate(delta); inputUpdate(); - serverUpdate(); } void gameDispose() { diff --git a/src/dusk/net/CMakeLists.txt b/src/dusk/net/CMakeLists.txt new file mode 100644 index 0000000..22ebcf1 --- /dev/null +++ b/src/dusk/net/CMakeLists.txt @@ -0,0 +1,12 @@ +# 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 +) + +# Subdirs +add_subdirectory(server) \ No newline at end of file diff --git a/src/dusk/net/server/CMakeLists.txt b/src/dusk/net/server/CMakeLists.txt new file mode 100644 index 0000000..aaeacf1 --- /dev/null +++ b/src/dusk/net/server/CMakeLists.txt @@ -0,0 +1,11 @@ +# 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 + server.c + serverclient.c +) \ No newline at end of file diff --git a/src/dusk/net/server/server.c b/src/dusk/net/server/server.c new file mode 100644 index 0000000..e9d946f --- /dev/null +++ b/src/dusk/net/server/server.c @@ -0,0 +1,145 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "server.h" +#include "assert/assert.h" +#include "console/console.h" + +server_t SERVER; + +void serverInit(const serverinit_t init) { + memset(&SERVER, 0, sizeof(server_t)); + SERVER.state = SERVER_STATE_STARTING; + SERVER.type = init.type; + + // Create the server + switch(init.type) { + case SERVER_TYPE_ONLINE: + SERVER.online.socket = socket(AF_INET, SOCK_STREAM, 0); + SERVER.online.address.sin_family = AF_INET; + SERVER.online.address.sin_addr.s_addr = INADDR_ANY; + SERVER.online.address.sin_port = htons(init.online.port); + + // Bind + if(bind( + SERVER.online.socket, + (struct sockaddr *)&SERVER.online.address, + sizeof(SERVER.online.address) + ) < 0) { + assertUnreachable("Failed to bind server."); + } + + // Begin listening + if(listen(SERVER.online.socket, SERVER_CLIENT_COUNT_MAX) < 0) { + assertUnreachable("Failed to listen on server."); + } + + // Set the socket to non-blocking mode + int flags = fcntl(SERVER.online.socket, F_GETFL, 0); + fcntl(SERVER.online.socket, F_SETFL, flags | O_NONBLOCK); + + break; + + default: + assertUnreachable("Invalid server type."); + break; + } + + // Start thread + pthread_create(&SERVER.thread, NULL, serverThread, NULL); + + // Wait for server to start + while(SERVER.state == SERVER_STATE_STARTING) usleep(1000); +} + +void * serverThread(void *arg) { + assertNull(arg, "Server thread does not accept arguments."); + + // Server now running + SERVER.state = SERVER_STATE_RUNNING; + + fd_set readfds; + int32_t activity; + int32_t error; + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 1000; + + // Start server + while(SERVER.state == SERVER_STATE_RUNNING) { + FD_ZERO(&readfds); + FD_SET(SERVER.online.socket, &readfds); + + activity = select( + SERVER.online.socket + 1, + &readfds, + NULL, + NULL, + &timeout + ); + + if(activity < 0) assertUnreachable("Select error"); + + if(!FD_ISSET(SERVER.online.socket, &readfds)) { + continue; + } + + // Accept incoming connections + int32_t clientSockDesc = accept(SERVER.online.socket, NULL, NULL); + if(clientSockDesc < 0) { + printf("Failed to accept connection\n"); + continue; + } + + // Now, is the server full? + if(SERVER.clientCount >= SERVER_CLIENT_COUNT_MAX) { + consolePrint("Server is full, rejecting connection."); + // TODO: Write a reason to client. + close(clientSockDesc); + continue; + } + + // Create server client for handling + serverclient_t *client = &SERVER.clients[SERVER.clientCount]; + serverClientInit(client, (serverclientinit_t){ + .socket = clientSockDesc + }); + SERVER.clientCount++; + } + + SERVER.state = SERVER_STATE_STOPPED; + return NULL; +} + +void serverDispose() { + // Disconnect clients + for(uint8_t i = 0; i < SERVER.clientCount; i++) { + serverClientDispose(&SERVER.clients[i]); + } + SERVER.clientCount = 0; + + // Stop server thread. + if(SERVER.state == SERVER_STATE_RUNNING) { + // Request thread to stop + SERVER.state = SERVER_STATE_STOPPING; + + // Wait for server to stop + while(SERVER.state == SERVER_STATE_STOPPING) usleep(1000); + pthread_join(SERVER.thread, NULL); + + // Close the socket + switch(SERVER.type) { + case SERVER_TYPE_ONLINE: + close(SERVER.online.socket); + break; + + default: + assertUnreachable("Invalid server type."); + break; + } + } +} \ No newline at end of file diff --git a/src/dusk/net/server/server.h b/src/dusk/net/server/server.h new file mode 100644 index 0000000..967fe86 --- /dev/null +++ b/src/dusk/net/server/server.h @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "serverclient.h" + +#define SERVER_CLIENT_COUNT_MAX 8 +#define SERVER_PORT_DEFAULT 24555 + +typedef enum { + SERVER_STATE_STOPPED, + SERVER_STATE_STARTING, + SERVER_STATE_RUNNING, + SERVER_STATE_STOPPING +} serverstate_t; + +typedef enum { + SERVER_TYPE_OFFLINE, + SERVER_TYPE_ONLINE +} servertype_t; + +typedef struct { + serverstate_t state; + servertype_t type; + serverclient_t clients[SERVER_CLIENT_COUNT_MAX]; + uint8_t clientCount; + pthread_t thread; + + union { + struct { + int32_t socket; + struct sockaddr_in address; + } online; + }; +} server_t; + +typedef struct { + servertype_t type; + + union { + struct { + int32_t port; + } online; + }; +} serverinit_t; + +extern server_t SERVER; + +void serverInit(const serverinit_t init); +void * serverThread(void* arg); +void serverDispose(); \ No newline at end of file diff --git a/src/dusk/net/server/serverclient.c b/src/dusk/net/server/serverclient.c new file mode 100644 index 0000000..2755a0a --- /dev/null +++ b/src/dusk/net/server/serverclient.c @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "serverclient.h" +#include "assert/assert.h" +#include "console/console.h" + +void serverClientInit(serverclient_t *client, const serverclientinit_t init) { + assertNotNull(client, "Client cannot be NULL."); + client->socket = init.socket; + consolePrint("Accepted client %i", client->socket); + + client->state = SERVER_CLIENT_STATE_CONNECTING; + + // Set socket to non-blocking + int flags = fcntl(client->socket, F_GETFL, 0); + fcntl(client->socket, F_SETFL, flags | O_NONBLOCK); + + // Start thread + pthread_create(&client->thread, NULL, serverClientThread, client); + + // Wait for client to connect + while(client->state == SERVER_CLIENT_STATE_CONNECTING) usleep(1000); +} + +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)); + } + + // Client should respond "OK" + { + uint8_t response[2]; + _serverClientReceive(client, response, sizeof(response)); + assertTrue( + response[0] == 'O' && + response[1] == 'K', + "Client did not respond with OK." + ); + } + + while(client->state == SERVER_CLIENT_STATE_CONNECTED) { + // Do nothing for now. + usleep(1000); + } + + // Disconnect + client->state = SERVER_CLIENT_STATE_DISCONNECTED; + return NULL; +} + +void _serverClientSend( + serverclient_t *client, + const uint8_t *data, + const size_t size +) { + assertNotNull(client, "Client cannot be NULL."); + assertNotNull(data, "Data cannot be NULL."); + assertTrue(size > 0, "Size cannot be zero."); +} + +void _serverClientReceive( + serverclient_t *client, + uint8_t *data, + const size_t size +) { + 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); +} + +void serverClientDispose(serverclient_t *client) { + assertNotNull(client, "Client cannot be NULL."); + + if(client->state == SERVER_CLIENT_STATE_CONNECTED) { + // Let the thread know we are disconnecting + client->state = SERVER_CLIENT_STATE_DISCONNECTING; + + // Wait for the thread to finish + while(client->state == SERVER_CLIENT_STATE_DISCONNECTING) usleep(1000); + pthread_join(client->thread, NULL); + } +} \ No newline at end of file diff --git a/src/dusk/net/server/serverclient.h b/src/dusk/net/server/serverclient.h new file mode 100644 index 0000000..e5b5c5f --- /dev/null +++ b/src/dusk/net/server/serverclient.h @@ -0,0 +1,45 @@ +/** + * 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 + +typedef enum { + SERVER_CLIENT_STATE_DISCONNECTED, + SERVER_CLIENT_STATE_CONNECTING, + SERVER_CLIENT_STATE_CONNECTED, + SERVER_CLIENT_STATE_DISCONNECTING +} serverclientstate_t; + +typedef struct { + serverclientstate_t state; + pthread_t thread; + int32_t socket; +} serverclient_t; + +typedef struct { + int32_t socket; +} serverclientinit_t; + +void serverClientInit(serverclient_t *client, const serverclientinit_t init); +void * serverClientThread(void *arg); + +void _serverClientSend( + serverclient_t *client, + const uint8_t *data, + const size_t size +); + +void _serverClientReceive( + serverclient_t *client, + uint8_t *buffer, + const size_t size +); + +void serverClientDispose(serverclient_t *client); \ No newline at end of file