abt to big refactor
This commit is contained in:
@ -9,6 +9,6 @@ include(FetchContent)
|
|||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
raylib
|
raylib
|
||||||
GIT_REPOSITORY https://github.com/raysan5/raylib
|
GIT_REPOSITORY https://github.com/raysan5/raylib
|
||||||
GIT_TAG 5.5
|
GIT_TAG 5aa3f0ccc374c1fbfc880402b37871b9c3bb7d8e
|
||||||
)
|
)
|
||||||
FetchContent_MakeAvailable(raylib)
|
FetchContent_MakeAvailable(raylib)
|
@ -21,6 +21,7 @@ errorret_t errorCode(const errorret_t code, const char_t *message, ...) {
|
|||||||
"Multiple errors encountered."
|
"Multiple errors encountered."
|
||||||
);
|
);
|
||||||
errorPrint();
|
errorPrint();
|
||||||
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
va_list args;
|
va_list args;
|
||||||
|
22
src/main.c
22
src/main.c
@ -7,13 +7,29 @@
|
|||||||
|
|
||||||
#include "console/console.h"
|
#include "console/console.h"
|
||||||
#include "server/server.h"
|
#include "server/server.h"
|
||||||
|
#include "util/string.h"
|
||||||
|
|
||||||
|
bool_t exitRequested = false;
|
||||||
|
|
||||||
void cmdExit(const consolecmdexec_t *exec) {
|
void cmdExit(const consolecmdexec_t *exec) {
|
||||||
CloseWindow();
|
exitRequested = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmdServe(const consolecmdexec_t *exec) {
|
void cmdServe(const consolecmdexec_t *exec) {
|
||||||
serverStart(3030);
|
uint16_t port = 3030;
|
||||||
|
if(exec->argc != 0) {
|
||||||
|
if(!stringToU16(exec->argv[0], &port)) {
|
||||||
|
consolePrint("Invalid port number: %s", exec->argv[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t ret = serverStart(port);
|
||||||
|
if(ret != ERROR_OK) {
|
||||||
|
consolePrint("Failed to start server: %s", errorString());
|
||||||
|
errorFlush();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmdClose(const consolecmdexec_t *exec) {
|
void cmdClose(const consolecmdexec_t *exec) {
|
||||||
@ -39,6 +55,8 @@ int main(void) {
|
|||||||
consoleDraw();
|
consoleDraw();
|
||||||
|
|
||||||
EndDrawing();
|
EndDrawing();
|
||||||
|
|
||||||
|
if(exitRequested) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
CloseWindow();
|
CloseWindow();
|
||||||
|
@ -9,3 +9,6 @@ target_sources(${DUSK_TARGET_NAME}
|
|||||||
server.c
|
server.c
|
||||||
serverclient.c
|
serverclient.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Subdirs
|
||||||
|
add_subdirectory(packet)
|
11
src/server/packet/CMakeLists.txt
Normal file
11
src/server/packet/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Copyright (c) 2025 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
# Sources
|
||||||
|
target_sources(${DUSK_TARGET_NAME}
|
||||||
|
PRIVATE
|
||||||
|
packet.c
|
||||||
|
packetwelcome.c
|
||||||
|
)
|
29
src/server/packet/packet.c
Normal file
29
src/server/packet/packet.c
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "packet.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
|
||||||
|
void packetInit(
|
||||||
|
packet_t *packet,
|
||||||
|
const packettype_t type,
|
||||||
|
const uint32_t length
|
||||||
|
) {
|
||||||
|
assertNotNull(packet, "Packet is NULL");
|
||||||
|
|
||||||
|
memoryZero(packet, sizeof(packet_t));
|
||||||
|
|
||||||
|
assertTrue(length > 0, "Packet length is 0");
|
||||||
|
assertTrue(
|
||||||
|
length <= sizeof(packetdata_t),
|
||||||
|
"Packet length is too large"
|
||||||
|
);
|
||||||
|
|
||||||
|
packet->type = type;
|
||||||
|
packet->length = length;
|
||||||
|
}
|
42
src/server/packet/packet.h
Normal file
42
src/server/packet/packet.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
#include "packetwelcome.h"
|
||||||
|
#include "packetdisconnect.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PACKET_TYPE_INVALID = 0,
|
||||||
|
PACKET_TYPE_WELCOME = 1,
|
||||||
|
PACKET_TYPE_DISCONNECT = 2,
|
||||||
|
} packettype_t;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
packetwelcome_t welcome;
|
||||||
|
packetdisconnect_t disconnect;
|
||||||
|
} packetdata_t;
|
||||||
|
|
||||||
|
typedef struct packet_s {
|
||||||
|
packettype_t type;
|
||||||
|
uint32_t length;
|
||||||
|
packetdata_t data;
|
||||||
|
} packet_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a packet with the given type. This is only to be used by sub
|
||||||
|
* initializers, such as packetWelcomeCreate for example.
|
||||||
|
*
|
||||||
|
* @param packet Pointer to the packet structure to initialize.
|
||||||
|
* @param type The type of the packet.
|
||||||
|
* @param length The length of the packet data.
|
||||||
|
*/
|
||||||
|
void packetInit(
|
||||||
|
packet_t *packet,
|
||||||
|
const packettype_t type,
|
||||||
|
const uint32_t length
|
||||||
|
);
|
17
src/server/packet/packetdisconnect.c
Normal file
17
src/server/packet/packetdisconnect.c
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "packet.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
|
||||||
|
void packetDisconnectCreate(
|
||||||
|
packet_t *packet,
|
||||||
|
const packetdisconnectreason_t reason
|
||||||
|
) {
|
||||||
|
packetInit(packet, PACKET_TYPE_DISCONNECT, sizeof(packetdisconnect_t));
|
||||||
|
packet->data.disconnect.reason = reason;
|
||||||
|
}
|
32
src/server/packet/packetdisconnect.h
Normal file
32
src/server/packet/packetdisconnect.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
|
||||||
|
typedef struct packet_s packet_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PACKET_DISCONNECT_REASON_UNKNOWN = 0,
|
||||||
|
PACKET_DISCONNECT_REASON_INVALID_VERSION = 1,
|
||||||
|
PACKET_DISCONNECT_REASON_MALFORMED_PACKET = 2,
|
||||||
|
} packetdisconnectreason_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
packetdisconnectreason_t reason;
|
||||||
|
} packetdisconnect_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a disconnect packet.
|
||||||
|
*
|
||||||
|
* @param packet Pointer to the packet structure to initialize.
|
||||||
|
* @param reason The reason for the disconnect.
|
||||||
|
*/
|
||||||
|
void packetDisconnectCreate(
|
||||||
|
packet_t *packet,
|
||||||
|
const packetdisconnectreason_t reason
|
||||||
|
);
|
16
src/server/packet/packetwelcome.c
Normal file
16
src/server/packet/packetwelcome.c
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "packet.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
|
||||||
|
void packetWelcomeCreate(packet_t *packet) {
|
||||||
|
packetInit(packet, PACKET_TYPE_WELCOME, PACKET_WELCOME_SIZE);
|
||||||
|
memoryCopy(
|
||||||
|
packet->data.welcome.dusk, PACKET_WELCOME_STRING, PACKET_WELCOME_SIZE
|
||||||
|
);
|
||||||
|
}
|
25
src/server/packet/packetwelcome.h
Normal file
25
src/server/packet/packetwelcome.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
|
||||||
|
typedef struct packet_s packet_t;
|
||||||
|
|
||||||
|
#define PACKET_WELCOME_STRING "DUSK"
|
||||||
|
#define PACKET_WELCOME_SIZE 4
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char_t dusk[PACKET_WELCOME_SIZE];
|
||||||
|
} packetwelcome_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a welcome packet.
|
||||||
|
*
|
||||||
|
* @param packet Pointer to the packet structure to initialize.
|
||||||
|
*/
|
||||||
|
void packetWelcomeCreate(packet_t *packet);
|
@ -14,21 +14,20 @@ server_t SERVER;
|
|||||||
|
|
||||||
void serverInit() {
|
void serverInit() {
|
||||||
memoryZero(&SERVER, sizeof(server_t));
|
memoryZero(&SERVER, sizeof(server_t));
|
||||||
pthread_mutex_init(&SERVER.startMutex, NULL);
|
SERVER.state = SERVER_STATE_STOPPED;
|
||||||
pthread_cond_init(&SERVER.startCond, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t serverStart(uint16_t port) {
|
errorret_t serverStart(uint16_t port) {
|
||||||
assertFalse(SERVER.isRunning, "Server is already running?");
|
if(SERVER.state != SERVER_STATE_STOPPED) {
|
||||||
consolePrint("Starting server on port %d...\n", port);
|
return error("Server is already running");
|
||||||
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(&SERVER.startMutex);
|
consolePrint("Starting server on port %d...\n", port);
|
||||||
SERVER.threadReady = false;
|
memoryZero(&SERVER, sizeof(server_t));
|
||||||
|
|
||||||
// Initialize the server socket
|
// Initialize the server socket
|
||||||
SERVER.serverSocket = socket(AF_INET, SOCK_STREAM, 0);
|
SERVER.serverSocket = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
if(SERVER.serverSocket < 0) {
|
if(SERVER.serverSocket < 0) {
|
||||||
pthread_mutex_unlock(&SERVER.startMutex);
|
|
||||||
return error("Failed to create socket");
|
return error("Failed to create socket");
|
||||||
}
|
}
|
||||||
SERVER.serverAddress.sin_family = AF_INET;
|
SERVER.serverAddress.sin_family = AF_INET;
|
||||||
@ -41,35 +40,32 @@ errorret_t serverStart(uint16_t port) {
|
|||||||
(struct sockaddr *)&SERVER.serverAddress,
|
(struct sockaddr *)&SERVER.serverAddress,
|
||||||
sizeof(SERVER.serverAddress)
|
sizeof(SERVER.serverAddress)
|
||||||
);
|
);
|
||||||
if(res < 0) {
|
if(res != 0) {
|
||||||
close(SERVER.serverSocket);
|
close(SERVER.serverSocket);
|
||||||
pthread_mutex_unlock(&SERVER.startMutex);
|
|
||||||
return error("Failed to bind socket");
|
return error("Failed to bind socket");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the socket to listen for incoming connections
|
// Set the socket to listen for incoming connections
|
||||||
res = listen(SERVER.serverSocket, SERVER_MAX_CLIENTS);
|
res = listen(SERVER.serverSocket, SERVER_MAX_CLIENTS);
|
||||||
if(res < 0) {
|
if(res != 0) {
|
||||||
close(SERVER.serverSocket);
|
close(SERVER.serverSocket);
|
||||||
pthread_mutex_unlock(&SERVER.startMutex);
|
|
||||||
return error("Failed to listen on socket");
|
return error("Failed to listen on socket");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the server thread
|
// Start the server thread.
|
||||||
SERVER.isRunning = true;
|
SERVER.state = SERVER_STATE_STARTING;
|
||||||
if(pthread_create(&SERVER.thread, NULL, serverThread, NULL) != 0) {
|
res = pthread_create(&SERVER.thread, NULL, serverThread, NULL);
|
||||||
|
if(res != 0) {
|
||||||
perror("Failed to create server thread");
|
perror("Failed to create server thread");
|
||||||
serverStop();
|
serverStop();
|
||||||
pthread_mutex_unlock(&SERVER.startMutex);
|
|
||||||
return error("Failed to create server thread");
|
return error("Failed to create server thread");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for the server thread to be ready
|
// Wait
|
||||||
while(!SERVER.threadReady) {
|
while(SERVER.state == SERVER_STATE_STARTING) {
|
||||||
pthread_cond_wait(&SERVER.startCond, &SERVER.startMutex);
|
usleep(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&SERVER.startMutex);
|
|
||||||
consolePrint("Server started.");
|
consolePrint("Server started.");
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
@ -78,18 +74,17 @@ void * serverThread(void *arg) {
|
|||||||
struct timeval timeout;
|
struct timeval timeout;
|
||||||
fd_set readfds;
|
fd_set readfds;
|
||||||
|
|
||||||
pthread_mutex_lock(&SERVER.startMutex);
|
SERVER.state = SERVER_STATE_RUNNING;
|
||||||
SERVER.threadReady = true;
|
|
||||||
pthread_cond_signal(&SERVER.startCond);
|
|
||||||
pthread_mutex_unlock(&SERVER.startMutex);
|
|
||||||
|
|
||||||
while(SERVER.isRunning) {
|
while(SERVER.state == SERVER_STATE_RUNNING) {
|
||||||
usleep(SERVER_TIMEOUT * 1000);
|
usleep(1000);
|
||||||
|
|
||||||
FD_ZERO(&readfds);
|
FD_ZERO(&readfds);
|
||||||
FD_SET(SERVER.serverSocket, &readfds);
|
FD_SET(SERVER.serverSocket, &readfds);
|
||||||
|
|
||||||
timeout.tv_usec = SERVER_TIMEOUT * 1000;
|
// Fix timeout struct
|
||||||
|
timeout.tv_sec = 1;
|
||||||
|
timeout.tv_usec = 0;
|
||||||
|
|
||||||
// Wait for
|
// Wait for
|
||||||
int32_t activity = select(
|
int32_t activity = select(
|
||||||
@ -119,7 +114,6 @@ void * serverThread(void *arg) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Initialize the client
|
// Initialize the client
|
||||||
serverclient_t *client = NULL;
|
serverclient_t *client = NULL;
|
||||||
uint8_t i = 0;
|
uint8_t i = 0;
|
||||||
@ -147,14 +141,17 @@ void * serverThread(void *arg) {
|
|||||||
errorFlush();
|
errorFlush();
|
||||||
close(clientSocket);
|
close(clientSocket);
|
||||||
}
|
}
|
||||||
|
|
||||||
usleep(1000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Server thread exiting.\n");
|
printf("Server thread exiting.\n");
|
||||||
assertFalse(
|
assertTrue(
|
||||||
SERVER.isRunning, "Server thread exiting while server is running?"
|
SERVER.state != SERVER_STATE_RUNNING,
|
||||||
|
"Server thread exiting while server is running?"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
SERVER.thread = 0;
|
||||||
|
SERVER.state = SERVER_STATE_STOPPED;
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,9 +165,36 @@ uint8_t serverGetClientCount() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void serverStop() {
|
void serverStop() {
|
||||||
consolePrint("Stopping server...");
|
if(SERVER.state == SERVER_STATE_STOPPED) return;
|
||||||
|
|
||||||
SERVER.isRunning = false;
|
assertTrue(
|
||||||
|
SERVER.state != SERVER_STATE_STARTING,
|
||||||
|
"Stopping a server that is starting?\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
consolePrint("Stopping server...");
|
||||||
|
SERVER.state = SERVER_STATE_STOPPING;
|
||||||
|
|
||||||
|
// Wait for the server thread to finish
|
||||||
|
int32_t maxWaits = 0;
|
||||||
|
while(SERVER.state == SERVER_STATE_STOPPING) {
|
||||||
|
usleep(1000);
|
||||||
|
maxWaits++;
|
||||||
|
if(maxWaits < 1000) continue;
|
||||||
|
|
||||||
|
consolePrint("Server thread did not stop in time, forcing exit.");
|
||||||
|
SERVER.state = SERVER_STATE_STOPPED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill the clients
|
||||||
|
uint8_t i = 0;
|
||||||
|
do {
|
||||||
|
serverclient_t *client = &SERVER.clients[i++];
|
||||||
|
serverClientClose(client);
|
||||||
|
} while(i < SERVER_MAX_CLIENTS);
|
||||||
|
|
||||||
|
// Stop the server
|
||||||
if(SERVER.serverSocket >= 0) {
|
if(SERVER.serverSocket >= 0) {
|
||||||
close(SERVER.serverSocket);
|
close(SERVER.serverSocket);
|
||||||
SERVER.serverSocket = -1;
|
SERVER.serverSocket = -1;
|
||||||
@ -181,9 +205,6 @@ void serverStop() {
|
|||||||
SERVER.thread = 0;
|
SERVER.thread = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_destroy(&SERVER.startMutex);
|
|
||||||
pthread_cond_destroy(&SERVER.startCond);
|
|
||||||
|
|
||||||
consolePrint("Server stopped.");
|
consolePrint("Server stopped.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,16 +12,19 @@
|
|||||||
|
|
||||||
#define SERVER_MAX_CLIENTS 32
|
#define SERVER_MAX_CLIENTS 32
|
||||||
#define SERVER_MAX_CHANNELS 2
|
#define SERVER_MAX_CHANNELS 2
|
||||||
#define SERVER_TIMEOUT 200
|
|
||||||
|
typedef enum {
|
||||||
|
SERVER_STATE_STOPPED,
|
||||||
|
SERVER_STATE_STARTING,
|
||||||
|
SERVER_STATE_RUNNING,
|
||||||
|
SERVER_STATE_STOPPING
|
||||||
|
} serverstate_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool_t isRunning;
|
serverstate_t state;
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
int serverSocket;
|
int serverSocket;
|
||||||
struct sockaddr_in serverAddress;
|
struct sockaddr_in serverAddress;
|
||||||
pthread_mutex_t startMutex;
|
|
||||||
pthread_cond_t startCond;
|
|
||||||
bool_t threadReady;
|
|
||||||
serverclient_t clients[SERVER_MAX_CLIENTS];
|
serverclient_t clients[SERVER_MAX_CLIENTS];
|
||||||
} server_t;
|
} server_t;
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "serverclient.h"
|
#include "serverclient.h"
|
||||||
|
#include "server.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include "console/console.h"
|
#include "console/console.h"
|
||||||
@ -18,8 +20,9 @@ errorret_t serverClientAccept(
|
|||||||
client->socket = socket;
|
client->socket = socket;
|
||||||
client->state = SERVER_CLIENT_STATE_ACCEPTING;
|
client->state = SERVER_CLIENT_STATE_ACCEPTING;
|
||||||
|
|
||||||
// Set timeout to 20 seconds
|
// Set timeout to 8 seconds
|
||||||
client->timeout.tv_usec = SERVER_CLIENT_TIMEOUT * 1000;
|
client->timeout.tv_sec = 8;
|
||||||
|
client->timeout.tv_usec = 0;
|
||||||
|
|
||||||
// Create a thread for the client
|
// Create a thread for the client
|
||||||
if(pthread_create(&client->thread, NULL, serverClientThread, client) != 0) {
|
if(pthread_create(&client->thread, NULL, serverClientThread, client) != 0) {
|
||||||
@ -33,6 +36,10 @@ errorret_t serverClientSend(
|
|||||||
const uint8_t *data,
|
const uint8_t *data,
|
||||||
const size_t len
|
const size_t len
|
||||||
) {
|
) {
|
||||||
|
assertNotNull(client, "Client is NULL");
|
||||||
|
assertNotNull(data, "Data is NULL");
|
||||||
|
assertTrue(len > 0, "Data length is 0");
|
||||||
|
|
||||||
if(client->state != SERVER_CLIENT_STATE_ACCEPTING) {
|
if(client->state != SERVER_CLIENT_STATE_ACCEPTING) {
|
||||||
return error("Client is not accepting");
|
return error("Client is not accepting");
|
||||||
}
|
}
|
||||||
@ -48,62 +55,156 @@ errorret_t serverClientSendString(
|
|||||||
return serverClientSend(client, (const uint8_t *)data, strlen(data));
|
return serverClientSend(client, (const uint8_t *)data, strlen(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
errorret_t serverClientSendPacket(
|
||||||
|
serverclient_t * client,
|
||||||
|
const packet_t *packet
|
||||||
|
) {
|
||||||
|
assertNotNull(client, "Client is NULL");
|
||||||
|
assertNotNull(packet, "Packet is NULL");
|
||||||
|
assertTrue(packet->type != PACKET_TYPE_INVALID, "Packet type is INVALID");
|
||||||
|
assertTrue(packet->length > 0, "Packet length is 0");
|
||||||
|
assertTrue(
|
||||||
|
packet->length <= sizeof(packetdata_t),
|
||||||
|
"Packet length is too large (1)"
|
||||||
|
);
|
||||||
|
|
||||||
|
size_t fullSize = sizeof(packet_t) - sizeof(packet->data) + packet->length;
|
||||||
|
assertTrue(fullSize <= sizeof(packet_t), "Packet size is too large (2)");
|
||||||
|
|
||||||
|
return serverClientSend(client, (const uint8_t *)packet, fullSize);
|
||||||
|
}
|
||||||
|
|
||||||
ssize_t serverClientReceive(
|
ssize_t serverClientReceive(
|
||||||
serverclient_t * client,
|
serverclient_t * client,
|
||||||
uint8_t *buffer,
|
uint8_t *buffer,
|
||||||
const size_t len
|
const size_t len
|
||||||
) {
|
) {
|
||||||
if(client->state != SERVER_CLIENT_STATE_ACCEPTING) return -1;
|
assertNotNull(client, "Client is NULL");
|
||||||
ssize_t received = recv(client->socket, buffer, len, 0);
|
assertNotNull(buffer, "Buffer is NULL");
|
||||||
if(received < 0) return received;
|
assertTrue(len > 0, "Buffer length is 0");
|
||||||
return received;
|
|
||||||
|
if(client->state == SERVER_CLIENT_STATE_DISCONNECTED) return -1;
|
||||||
|
if(client->state == SERVER_CLIENT_STATE_DISCONNECTING) return -1;
|
||||||
|
|
||||||
|
return recv(client->socket, buffer, len, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void serverClientClose(serverclient_t *client) {
|
void serverClientClose(serverclient_t *client) {
|
||||||
client->state = SERVER_CLIENT_STATE_DISCONNECTED;
|
assertNotNull(client, "Client is NULL");
|
||||||
close(client->socket);
|
if(client->state == SERVER_CLIENT_STATE_DISCONNECTED) return;
|
||||||
|
|
||||||
|
// Mark client as disconnecting
|
||||||
|
client->state = SERVER_CLIENT_STATE_DISCONNECTING;
|
||||||
|
|
||||||
|
int32_t maxWaits = 0;
|
||||||
|
while(client->state == SERVER_CLIENT_STATE_DISCONNECTING) {
|
||||||
|
// Wait for the thread to finish
|
||||||
|
usleep(1000);
|
||||||
|
maxWaits++;
|
||||||
|
if(maxWaits > 10) break;
|
||||||
|
}
|
||||||
|
|
||||||
pthread_join(client->thread, NULL);
|
pthread_join(client->thread, NULL);
|
||||||
client->thread = 0;
|
client->thread = 0;
|
||||||
|
|
||||||
|
// Close the socket
|
||||||
|
if(client->socket != -1) {
|
||||||
|
close(client->socket);
|
||||||
client->socket = -1;
|
client->socket = -1;
|
||||||
consolePrint("Client disconnected.");
|
}
|
||||||
|
|
||||||
|
client->state = SERVER_CLIENT_STATE_DISCONNECTED;
|
||||||
|
consolePrint("Client %d disconnected.", client->socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
void serverClientCloseOnThread(serverclient_t *client, const char_t *reason) {
|
||||||
|
assertNotNull(client, "Client is NULL");
|
||||||
|
assertNotNull(reason, "Reason is NULL");
|
||||||
|
|
||||||
|
client->state = SERVER_CLIENT_STATE_DISCONNECTING;
|
||||||
|
|
||||||
|
close(client->socket);
|
||||||
|
client->socket = -1;
|
||||||
|
|
||||||
|
client->state = SERVER_CLIENT_STATE_DISCONNECTED;
|
||||||
|
client->thread = 0;
|
||||||
|
|
||||||
|
consolePrint("Client %d disconnected: %s", client->socket, reason);
|
||||||
|
pthread_exit(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *serverClientThread(void *arg) {
|
void *serverClientThread(void *arg) {
|
||||||
|
assertNotNull(arg, "Client is NULL");
|
||||||
|
|
||||||
serverclient_t *client = (serverclient_t *)arg;
|
serverclient_t *client = (serverclient_t *)arg;
|
||||||
char_t buffer[1024];
|
char_t buffer[1024];
|
||||||
ssize_t read;
|
ssize_t read;
|
||||||
|
errorret_t err;
|
||||||
|
packet_t packet;
|
||||||
|
|
||||||
printf("Client thread started for socket %d.\n", client->socket);
|
consolePrint("Accepting client on socket %d.", client->socket);
|
||||||
|
|
||||||
// Set socket timeout
|
// Set socket timeout
|
||||||
setsockopt(
|
if(setsockopt(
|
||||||
client->socket,
|
client->socket,
|
||||||
SOL_SOCKET,
|
SOL_SOCKET,
|
||||||
SO_RCVTIMEO,
|
SO_RCVTIMEO,
|
||||||
&client->timeout,
|
&client->timeout,
|
||||||
sizeof(client->timeout)
|
sizeof(client->timeout)
|
||||||
);
|
) < 0) {
|
||||||
|
serverClientCloseOnThread(client, "Failed to set socket timeout");
|
||||||
// Send welcome message
|
return NULL;
|
||||||
serverClientSendString(client, "DUSK|"DUSK_VERSION);
|
}
|
||||||
|
|
||||||
// Receive version from client
|
// First message from the client should always be "DUSK|VERSION" to match
|
||||||
read = serverClientReceive(client, buffer, sizeof(buffer));
|
// the server version.
|
||||||
|
{
|
||||||
// First 5 bytes should be "DUSK|"
|
const char_t *expecting = "DUSK|"DUSK_VERSION;
|
||||||
if(read < 5 || strncmp(buffer, "DUSK|", 5) != 0) {
|
read = serverClientReceive(client, buffer, sizeof(buffer));
|
||||||
serverClientSendString(client, "ERR|Invalid version (0)");
|
if(read <= 0) {
|
||||||
serverClientClose(client);
|
packetDisconnectCreate(&packet, PACKET_DISCONNECT_REASON_INVALID_VERSION);
|
||||||
|
err = serverClientSendPacket(client, &packet);
|
||||||
|
serverClientCloseOnThread(client, "Failed to receive version");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next (up to X bytes where X is the length of the version string)
|
|
||||||
// should be the version string
|
|
||||||
buffer[read] = '\0'; // Null-terminate the string
|
buffer[read] = '\0'; // Null-terminate the string
|
||||||
|
if(strncmp(buffer, expecting, strlen(expecting)) != 0) {
|
||||||
|
packetDisconnectCreate(&packet, PACKET_DISCONNECT_REASON_INVALID_VERSION);
|
||||||
|
err = serverClientSendPacket(client, &packet);
|
||||||
|
serverClientCloseOnThread(client, "Invalid version");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send DUSK back!
|
||||||
|
packetWelcomeCreate(&packet);
|
||||||
|
err = serverClientSendPacket(client, &packet);
|
||||||
|
if(err != ERROR_OK) {
|
||||||
|
serverClientCloseOnThread(client, "Failed to send welcome message");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
client->state = SERVER_CLIENT_STATE_CONNECTED;
|
||||||
|
while(client->state == SERVER_CLIENT_STATE_CONNECTED) {
|
||||||
|
read = serverClientReceive(client, buffer, sizeof(buffer));
|
||||||
|
|
||||||
|
if(read <= 0) {
|
||||||
|
serverClientCloseOnThread(client, "Failed to receive data");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[read] = '\0'; // Null-terminate the string
|
||||||
|
consolePrint("Received: %s", buffer);
|
||||||
|
|
||||||
|
|
||||||
printf("Received version: %s\n", buffer);
|
if(SERVER.state != SERVER_STATE_RUNNING) {
|
||||||
|
serverClientCloseOnThread(client, "Server is shutting down");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
serverClientClose(client);
|
serverClientSendString(client, "Server is shutting down");
|
||||||
|
client->state = SERVER_CLIENT_STATE_DISCONNECTED;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
@ -7,23 +7,28 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "error/error.h"
|
#include "error/error.h"
|
||||||
|
#include "server/packet/packet.h"
|
||||||
#define SERVER_CLIENT_TIMEOUT 20000 // 20 seconds
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SERVER_CLIENT_STATE_DISCONNECTED,
|
SERVER_CLIENT_STATE_DISCONNECTED,
|
||||||
SERVER_CLIENT_STATE_ACCEPTING,
|
SERVER_CLIENT_STATE_ACCEPTING,
|
||||||
|
SERVER_CLIENT_STATE_CONNECTED,
|
||||||
|
SERVER_CLIENT_STATE_DISCONNECTING,
|
||||||
} serverclientstate_t;
|
} serverclientstate_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int32_t socket;
|
int32_t socket;
|
||||||
serverclientstate_t state;
|
serverclientstate_t state;
|
||||||
pthread_t thread; // Add thread field
|
pthread_t thread;
|
||||||
struct timeval timeout; // Add timeout field
|
struct timeval timeout;
|
||||||
} serverclient_t;
|
} serverclient_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accepts an incoming connection from a server client.
|
* Accepts an incoming connection from a server client.
|
||||||
|
*
|
||||||
|
* @param client Pointer to the server client structure.
|
||||||
|
* @param socket The socket file descriptor for the client.
|
||||||
|
* @return Error code indicating success or failure.
|
||||||
*/
|
*/
|
||||||
errorret_t serverClientAccept(
|
errorret_t serverClientAccept(
|
||||||
serverclient_t *client,
|
serverclient_t *client,
|
||||||
@ -56,6 +61,18 @@ errorret_t serverClientSendString(
|
|||||||
const char_t *data
|
const char_t *data
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a packet to a server client.
|
||||||
|
*
|
||||||
|
* @param client Pointer to the server client structure.
|
||||||
|
* @param packet Pointer to the packet to send.
|
||||||
|
* @return Error code indicating success or failure.
|
||||||
|
*/
|
||||||
|
errorret_t serverClientSendPacket(
|
||||||
|
serverclient_t * client,
|
||||||
|
const packet_t *packet
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receives data from a server client.
|
* Receives data from a server client.
|
||||||
*
|
*
|
||||||
@ -71,15 +88,28 @@ ssize_t serverClientReceive(
|
|||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the connection to a server client.
|
* Closes the connection to a server client. Waits for the thread to finish.
|
||||||
|
*
|
||||||
|
* @param client Pointer to the server client structure.
|
||||||
*/
|
*/
|
||||||
void serverClientClose(
|
void serverClientClose(serverclient_t *client);
|
||||||
serverclient_t *client
|
|
||||||
|
/**
|
||||||
|
* Closes the connection to a server client without waiting for the thread to
|
||||||
|
* finish.
|
||||||
|
*
|
||||||
|
* @param client Pointer to the server client structure.
|
||||||
|
* @param reason Reason for closing the connection.
|
||||||
|
*/
|
||||||
|
void serverClientCloseOnThread(
|
||||||
|
serverclient_t *client,
|
||||||
|
const char_t *reason
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thread function for handling a server client.
|
* Thread function for handling a server client.
|
||||||
*
|
*
|
||||||
* @param arg Pointer to the server client structure.
|
* @param arg Pointer to the server client structure.
|
||||||
|
* @return NULL.
|
||||||
*/
|
*/
|
||||||
void *serverClientThread(void *arg);
|
void *serverClientThread(void *arg);
|
@ -83,3 +83,45 @@ int32_t stringFormatVA(
|
|||||||
assertTrue(ret < destSize, "Formatted string is too long.");
|
assertTrue(ret < destSize, "Formatted string is too long.");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool_t stringToI32(const char_t *str, int32_t *out) {
|
||||||
|
assertNotNull(str, "str must not be NULL");
|
||||||
|
assertNotNull(out, "out must not be NULL");
|
||||||
|
|
||||||
|
char_t *endptr;
|
||||||
|
errno = 0;
|
||||||
|
long int result = strtol(str, &endptr, 10);
|
||||||
|
if (errno != 0 || *endptr != '\0') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*out = (int32_t)result;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t stringToI64(const char_t *str, int64_t *out) {
|
||||||
|
assertNotNull(str, "str must not be NULL");
|
||||||
|
assertNotNull(out, "out must not be NULL");
|
||||||
|
|
||||||
|
char_t *endptr;
|
||||||
|
errno = 0;
|
||||||
|
long long int result = strtoll(str, &endptr, 10);
|
||||||
|
if (errno != 0 || *endptr != '\0') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*out = (int64_t)result;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t stringToU16(const char_t *str, uint16_t *out) {
|
||||||
|
assertNotNull(str, "str must not be NULL");
|
||||||
|
assertNotNull(out, "out must not be NULL");
|
||||||
|
|
||||||
|
char_t *endptr;
|
||||||
|
errno = 0;
|
||||||
|
unsigned long int result = strtoul(str, &endptr, 10);
|
||||||
|
if (errno != 0 || *endptr != '\0' || result > UINT16_MAX) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*out = (uint16_t)result;
|
||||||
|
return true;
|
||||||
|
}
|
@ -80,3 +80,30 @@ int32_t stringFormatVA(
|
|||||||
char_t *format,
|
char_t *format,
|
||||||
va_list args
|
va_list args
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a string to an integer.
|
||||||
|
*
|
||||||
|
* @param str The string to convert.
|
||||||
|
* @param out The output integer.
|
||||||
|
* @return TRUE if the conversion was successful, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
bool_t stringToI32(const char_t *str, int32_t *out);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a string to a signed 16-bit integer.
|
||||||
|
*
|
||||||
|
* @param str The string to convert.
|
||||||
|
* @param out The output signed integer.
|
||||||
|
* @return TRUE if the conversion was successful, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
bool_t stringToI16(const char_t *str, int16_t *out);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a string to an unsigned 16-bit integer.
|
||||||
|
*
|
||||||
|
* @param str The string to convert.
|
||||||
|
* @param out The output unsigned integer.
|
||||||
|
* @return TRUE if the conversion was successful, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
bool_t stringToU16(const char_t *str, uint16_t *out);
|
Reference in New Issue
Block a user