abt to big refactor
This commit is contained in:
@ -9,6 +9,6 @@ include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
raylib
|
||||
GIT_REPOSITORY https://github.com/raysan5/raylib
|
||||
GIT_TAG 5.5
|
||||
GIT_TAG 5aa3f0ccc374c1fbfc880402b37871b9c3bb7d8e
|
||||
)
|
||||
FetchContent_MakeAvailable(raylib)
|
@ -21,6 +21,7 @@ errorret_t errorCode(const errorret_t code, const char_t *message, ...) {
|
||||
"Multiple errors encountered."
|
||||
);
|
||||
errorPrint();
|
||||
return code;
|
||||
}
|
||||
|
||||
va_list args;
|
||||
|
22
src/main.c
22
src/main.c
@ -7,13 +7,29 @@
|
||||
|
||||
#include "console/console.h"
|
||||
#include "server/server.h"
|
||||
#include "util/string.h"
|
||||
|
||||
bool_t exitRequested = false;
|
||||
|
||||
void cmdExit(const consolecmdexec_t *exec) {
|
||||
CloseWindow();
|
||||
exitRequested = true;
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -39,6 +55,8 @@ int main(void) {
|
||||
consoleDraw();
|
||||
|
||||
EndDrawing();
|
||||
|
||||
if(exitRequested) break;
|
||||
}
|
||||
|
||||
CloseWindow();
|
||||
|
@ -8,4 +8,7 @@ target_sources(${DUSK_TARGET_NAME}
|
||||
PRIVATE
|
||||
server.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() {
|
||||
memoryZero(&SERVER, sizeof(server_t));
|
||||
pthread_mutex_init(&SERVER.startMutex, NULL);
|
||||
pthread_cond_init(&SERVER.startCond, NULL);
|
||||
SERVER.state = SERVER_STATE_STOPPED;
|
||||
}
|
||||
|
||||
errorret_t serverStart(uint16_t port) {
|
||||
assertFalse(SERVER.isRunning, "Server is already running?");
|
||||
consolePrint("Starting server on port %d...\n", port);
|
||||
if(SERVER.state != SERVER_STATE_STOPPED) {
|
||||
return error("Server is already running");
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&SERVER.startMutex);
|
||||
SERVER.threadReady = false;
|
||||
consolePrint("Starting server on port %d...\n", port);
|
||||
memoryZero(&SERVER, sizeof(server_t));
|
||||
|
||||
// Initialize the server socket
|
||||
SERVER.serverSocket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if(SERVER.serverSocket < 0) {
|
||||
pthread_mutex_unlock(&SERVER.startMutex);
|
||||
return error("Failed to create socket");
|
||||
}
|
||||
SERVER.serverAddress.sin_family = AF_INET;
|
||||
@ -41,35 +40,32 @@ errorret_t serverStart(uint16_t port) {
|
||||
(struct sockaddr *)&SERVER.serverAddress,
|
||||
sizeof(SERVER.serverAddress)
|
||||
);
|
||||
if(res < 0) {
|
||||
if(res != 0) {
|
||||
close(SERVER.serverSocket);
|
||||
pthread_mutex_unlock(&SERVER.startMutex);
|
||||
return error("Failed to bind socket");
|
||||
}
|
||||
|
||||
// Set the socket to listen for incoming connections
|
||||
res = listen(SERVER.serverSocket, SERVER_MAX_CLIENTS);
|
||||
if(res < 0) {
|
||||
if(res != 0) {
|
||||
close(SERVER.serverSocket);
|
||||
pthread_mutex_unlock(&SERVER.startMutex);
|
||||
return error("Failed to listen on socket");
|
||||
}
|
||||
|
||||
// Start the server thread
|
||||
SERVER.isRunning = true;
|
||||
if(pthread_create(&SERVER.thread, NULL, serverThread, NULL) != 0) {
|
||||
// Start the server thread.
|
||||
SERVER.state = SERVER_STATE_STARTING;
|
||||
res = pthread_create(&SERVER.thread, NULL, serverThread, NULL);
|
||||
if(res != 0) {
|
||||
perror("Failed to create server thread");
|
||||
serverStop();
|
||||
pthread_mutex_unlock(&SERVER.startMutex);
|
||||
return error("Failed to create server thread");
|
||||
}
|
||||
|
||||
// Wait for the server thread to be ready
|
||||
while(!SERVER.threadReady) {
|
||||
pthread_cond_wait(&SERVER.startCond, &SERVER.startMutex);
|
||||
// Wait
|
||||
while(SERVER.state == SERVER_STATE_STARTING) {
|
||||
usleep(1000);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&SERVER.startMutex);
|
||||
|
||||
consolePrint("Server started.");
|
||||
return ERROR_OK;
|
||||
}
|
||||
@ -78,18 +74,17 @@ void * serverThread(void *arg) {
|
||||
struct timeval timeout;
|
||||
fd_set readfds;
|
||||
|
||||
pthread_mutex_lock(&SERVER.startMutex);
|
||||
SERVER.threadReady = true;
|
||||
pthread_cond_signal(&SERVER.startCond);
|
||||
pthread_mutex_unlock(&SERVER.startMutex);
|
||||
SERVER.state = SERVER_STATE_RUNNING;
|
||||
|
||||
while(SERVER.isRunning) {
|
||||
usleep(SERVER_TIMEOUT * 1000);
|
||||
while(SERVER.state == SERVER_STATE_RUNNING) {
|
||||
usleep(1000);
|
||||
|
||||
FD_ZERO(&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
|
||||
int32_t activity = select(
|
||||
@ -119,7 +114,6 @@ void * serverThread(void *arg) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Initialize the client
|
||||
serverclient_t *client = NULL;
|
||||
uint8_t i = 0;
|
||||
@ -147,14 +141,17 @@ void * serverThread(void *arg) {
|
||||
errorFlush();
|
||||
close(clientSocket);
|
||||
}
|
||||
|
||||
usleep(1000);
|
||||
}
|
||||
|
||||
printf("Server thread exiting.\n");
|
||||
assertFalse(
|
||||
SERVER.isRunning, "Server thread exiting while server is running?"
|
||||
assertTrue(
|
||||
SERVER.state != SERVER_STATE_RUNNING,
|
||||
"Server thread exiting while server is running?"
|
||||
);
|
||||
|
||||
SERVER.thread = 0;
|
||||
SERVER.state = SERVER_STATE_STOPPED;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -168,9 +165,36 @@ uint8_t serverGetClientCount() {
|
||||
}
|
||||
|
||||
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) {
|
||||
close(SERVER.serverSocket);
|
||||
SERVER.serverSocket = -1;
|
||||
@ -181,9 +205,6 @@ void serverStop() {
|
||||
SERVER.thread = 0;
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&SERVER.startMutex);
|
||||
pthread_cond_destroy(&SERVER.startCond);
|
||||
|
||||
consolePrint("Server stopped.");
|
||||
}
|
||||
|
||||
|
@ -12,16 +12,19 @@
|
||||
|
||||
#define SERVER_MAX_CLIENTS 32
|
||||
#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 {
|
||||
bool_t isRunning;
|
||||
serverstate_t state;
|
||||
pthread_t thread;
|
||||
int serverSocket;
|
||||
struct sockaddr_in serverAddress;
|
||||
pthread_mutex_t startMutex;
|
||||
pthread_cond_t startCond;
|
||||
bool_t threadReady;
|
||||
serverclient_t clients[SERVER_MAX_CLIENTS];
|
||||
} server_t;
|
||||
|
||||
|
@ -6,6 +6,8 @@
|
||||
*/
|
||||
|
||||
#include "serverclient.h"
|
||||
#include "server.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include <sys/socket.h>
|
||||
#include "console/console.h"
|
||||
@ -18,8 +20,9 @@ errorret_t serverClientAccept(
|
||||
client->socket = socket;
|
||||
client->state = SERVER_CLIENT_STATE_ACCEPTING;
|
||||
|
||||
// Set timeout to 20 seconds
|
||||
client->timeout.tv_usec = SERVER_CLIENT_TIMEOUT * 1000;
|
||||
// Set timeout to 8 seconds
|
||||
client->timeout.tv_sec = 8;
|
||||
client->timeout.tv_usec = 0;
|
||||
|
||||
// Create a thread for the client
|
||||
if(pthread_create(&client->thread, NULL, serverClientThread, client) != 0) {
|
||||
@ -33,6 +36,10 @@ errorret_t serverClientSend(
|
||||
const uint8_t *data,
|
||||
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) {
|
||||
return error("Client is not accepting");
|
||||
}
|
||||
@ -48,62 +55,156 @@ errorret_t serverClientSendString(
|
||||
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(
|
||||
serverclient_t * client,
|
||||
uint8_t *buffer,
|
||||
const size_t len
|
||||
) {
|
||||
if(client->state != SERVER_CLIENT_STATE_ACCEPTING) return -1;
|
||||
ssize_t received = recv(client->socket, buffer, len, 0);
|
||||
if(received < 0) return received;
|
||||
return received;
|
||||
assertNotNull(client, "Client is NULL");
|
||||
assertNotNull(buffer, "Buffer is NULL");
|
||||
assertTrue(len > 0, "Buffer length is 0");
|
||||
|
||||
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) {
|
||||
client->state = SERVER_CLIENT_STATE_DISCONNECTED;
|
||||
close(client->socket);
|
||||
assertNotNull(client, "Client is NULL");
|
||||
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);
|
||||
client->thread = 0;
|
||||
|
||||
// Close the socket
|
||||
if(client->socket != -1) {
|
||||
close(client->socket);
|
||||
client->socket = -1;
|
||||
}
|
||||
|
||||
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;
|
||||
consolePrint("Client disconnected.");
|
||||
|
||||
client->state = SERVER_CLIENT_STATE_DISCONNECTED;
|
||||
client->thread = 0;
|
||||
|
||||
consolePrint("Client %d disconnected: %s", client->socket, reason);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void *serverClientThread(void *arg) {
|
||||
assertNotNull(arg, "Client is NULL");
|
||||
|
||||
serverclient_t *client = (serverclient_t *)arg;
|
||||
char_t buffer[1024];
|
||||
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
|
||||
setsockopt(
|
||||
if(setsockopt(
|
||||
client->socket,
|
||||
SOL_SOCKET,
|
||||
SO_RCVTIMEO,
|
||||
&client->timeout,
|
||||
sizeof(client->timeout)
|
||||
);
|
||||
|
||||
// Send welcome message
|
||||
serverClientSendString(client, "DUSK|"DUSK_VERSION);
|
||||
|
||||
// Receive version from client
|
||||
read = serverClientReceive(client, buffer, sizeof(buffer));
|
||||
|
||||
// First 5 bytes should be "DUSK|"
|
||||
if(read < 5 || strncmp(buffer, "DUSK|", 5) != 0) {
|
||||
serverClientSendString(client, "ERR|Invalid version (0)");
|
||||
serverClientClose(client);
|
||||
) < 0) {
|
||||
serverClientCloseOnThread(client, "Failed to set socket timeout");
|
||||
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
|
||||
// First message from the client should always be "DUSK|VERSION" to match
|
||||
// the server version.
|
||||
{
|
||||
const char_t *expecting = "DUSK|"DUSK_VERSION;
|
||||
read = serverClientReceive(client, buffer, sizeof(buffer));
|
||||
if(read <= 0) {
|
||||
packetDisconnectCreate(&packet, PACKET_DISCONNECT_REASON_INVALID_VERSION);
|
||||
err = serverClientSendPacket(client, &packet);
|
||||
serverClientCloseOnThread(client, "Failed to receive version");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Received version: %s\n", buffer);
|
||||
|
||||
serverClientClose(client);
|
||||
// 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);
|
||||
|
||||
|
||||
if(SERVER.state != SERVER_STATE_RUNNING) {
|
||||
serverClientCloseOnThread(client, "Server is shutting down");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
serverClientSendString(client, "Server is shutting down");
|
||||
client->state = SERVER_CLIENT_STATE_DISCONNECTED;
|
||||
return NULL;
|
||||
}
|
@ -7,23 +7,28 @@
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
|
||||
#define SERVER_CLIENT_TIMEOUT 20000 // 20 seconds
|
||||
#include "server/packet/packet.h"
|
||||
|
||||
typedef enum {
|
||||
SERVER_CLIENT_STATE_DISCONNECTED,
|
||||
SERVER_CLIENT_STATE_ACCEPTING,
|
||||
SERVER_CLIENT_STATE_CONNECTED,
|
||||
SERVER_CLIENT_STATE_DISCONNECTING,
|
||||
} serverclientstate_t;
|
||||
|
||||
typedef struct {
|
||||
int32_t socket;
|
||||
serverclientstate_t state;
|
||||
pthread_t thread; // Add thread field
|
||||
struct timeval timeout; // Add timeout field
|
||||
pthread_t thread;
|
||||
struct timeval timeout;
|
||||
} serverclient_t;
|
||||
|
||||
/**
|
||||
* 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(
|
||||
serverclient_t *client,
|
||||
@ -56,6 +61,18 @@ errorret_t serverClientSendString(
|
||||
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.
|
||||
*
|
||||
@ -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(
|
||||
serverclient_t *client
|
||||
void serverClientClose(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.
|
||||
*
|
||||
* @param arg Pointer to the server client structure.
|
||||
* @return NULL.
|
||||
*/
|
||||
void *serverClientThread(void *arg);
|
@ -82,4 +82,46 @@ int32_t stringFormatVA(
|
||||
assertTrue(ret >= 0, "Failed to format string.");
|
||||
assertTrue(ret < destSize, "Formatted string is too long.");
|
||||
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;
|
||||
}
|
@ -79,4 +79,31 @@ int32_t stringFormatVA(
|
||||
const size_t destSize,
|
||||
char_t *format,
|
||||
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