About to make the processing queue lock properly
This commit is contained in:
		| @@ -27,5 +27,6 @@ add_subdirectory(assert) | ||||
| add_subdirectory(client) | ||||
| add_subdirectory(console) | ||||
| add_subdirectory(error) | ||||
| add_subdirectory(packet) | ||||
| add_subdirectory(server) | ||||
| add_subdirectory(util) | ||||
| @@ -75,6 +75,12 @@ void clientDisconnect() { | ||||
|     default: | ||||
|       assertUnreachable("Invalid client type"); | ||||
|   } | ||||
|    | ||||
|   consolePrint("Client disconnected"); | ||||
| } | ||||
|  | ||||
| void clientProcess() { | ||||
|   if(CLIENT.state == CLIENT_STATE_DISCONNECTED) return; | ||||
| } | ||||
|  | ||||
| void clientDispose() { | ||||
|   | ||||
| @@ -7,6 +7,7 @@ | ||||
|  | ||||
| #pragma once | ||||
| #include "client/networked/networkedclient.h" | ||||
| #include "packet/packetqueue.h" | ||||
|  | ||||
| typedef enum { | ||||
|   CLIENT_TYPE_NETWORKED, | ||||
| @@ -30,6 +31,7 @@ typedef struct clientconnect_s { | ||||
| typedef struct client_s { | ||||
|   clientstate_t state; | ||||
|   clienttype_t type; | ||||
|   packetqueue_t packetQueue; | ||||
|  | ||||
|   union { | ||||
|     networkedclient_t networked; | ||||
|   | ||||
| @@ -96,7 +96,7 @@ errorret_t networkedClientConnect( | ||||
|   ret = pthread_create( | ||||
|     &client->networked.thread, | ||||
|     NULL, | ||||
|     clientThread, | ||||
|     networkedClientThread, | ||||
|     client | ||||
|   ); | ||||
|  | ||||
| @@ -123,9 +123,9 @@ void networkedClientDisconnect(client_t *client) { | ||||
|  | ||||
|   int32_t maxAttempts = 0; | ||||
|   while(client->state == CLIENT_STATE_DISCONNECTING) { | ||||
|     usleep(1000); | ||||
|     usleep(100 * 1000);// Sleep for 100ms | ||||
|     maxAttempts++; | ||||
|     if(maxAttempts > 15) { | ||||
|     if(maxAttempts > 5) { | ||||
|       consolePrint("Client disconnect timed out, force closing"); | ||||
|       break; | ||||
|     } | ||||
| @@ -142,8 +142,6 @@ void networkedClientDisconnect(client_t *client) { | ||||
|     close(client->networked.socket); | ||||
|     client->networked.socket = 0; | ||||
|   } | ||||
|  | ||||
|   consolePrint("Client disconnected"); | ||||
| } | ||||
|  | ||||
| errorret_t networkedClientWrite( | ||||
| @@ -237,10 +235,11 @@ errorret_t networkedClientReadPacket( | ||||
|   return ERROR_OK; | ||||
| } | ||||
|  | ||||
| void * clientThread(void *arg) { | ||||
| void * networkedClientThread(void *arg) { | ||||
|   assertNotNull(arg, "Client thread argument is NULL"); | ||||
|   assertNotMainThread("Client thread must not be on main thread"); | ||||
|  | ||||
|    | ||||
|   packet_t packet; | ||||
|   client_t *client = (client_t *)arg; | ||||
|   assertTrue( | ||||
|     client->type == CLIENT_TYPE_NETWORKED, | ||||
| @@ -254,8 +253,14 @@ void * clientThread(void *arg) { | ||||
|   client->state = CLIENT_STATE_CONNECTED; | ||||
|  | ||||
|   while(client->state == CLIENT_STATE_CONNECTED) { | ||||
|     usleep(1000 * 1000); | ||||
|     usleep(1000*1000);// Sleep for 1 second | ||||
|      | ||||
|     packetPingCreate(&packet); | ||||
|     packetQueuePushOutbound( | ||||
|       &client->packetQueue, | ||||
|       &packet | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   printf("Client thread exiting\n"); | ||||
|   client->state = CLIENT_STATE_DISCONNECTED; | ||||
| } | ||||
| @@ -8,7 +8,7 @@ | ||||
| #pragma once | ||||
| #include "error/error.h" | ||||
| #include <arpa/inet.h> | ||||
| #include "server/packet/packet.h" | ||||
| #include "packet/packet.h" | ||||
|  | ||||
| typedef struct client_s client_t; | ||||
| typedef struct clientconnect_s clientconnect_t; | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
| #include "console/console.h" | ||||
| #include "client/client.h" | ||||
| #include "server/server.h" | ||||
| #include "util/random.h" | ||||
| #include "util/string.h" | ||||
| #include "assert/assert.h" | ||||
|  | ||||
| @@ -20,6 +21,7 @@ void cmdExit(const consolecmdexec_t *exec) { | ||||
| int main(void) { | ||||
|   assertInit(); | ||||
|   consoleInit(); | ||||
|   randomInit(); | ||||
|   clientInit(); | ||||
|   serverInit(); | ||||
|  | ||||
|   | ||||
| @@ -9,4 +9,6 @@ target_sources(${DUSK_TARGET_NAME} | ||||
|     packet.c | ||||
|     packetwelcome.c | ||||
|     packetdisconnect.c | ||||
|     packetqueue.c | ||||
|     packetping.c | ||||
| ) | ||||
| @@ -9,16 +9,19 @@ | ||||
| #include "dusk.h" | ||||
| #include "packetwelcome.h" | ||||
| #include "packetdisconnect.h" | ||||
| #include "packetping.h" | ||||
| 
 | ||||
| typedef enum { | ||||
|   PACKET_TYPE_INVALID = 0, | ||||
|   PACKET_TYPE_WELCOME = 1, | ||||
|   PACKET_TYPE_DISCONNECT = 2, | ||||
|   PACKET_TYPE_PING = 3, | ||||
| } packettype_t; | ||||
| 
 | ||||
| typedef union { | ||||
|   packetwelcome_t welcome; | ||||
|   packetdisconnect_t disconnect; | ||||
|   packetping_t ping; | ||||
| } packetdata_t; | ||||
| 
 | ||||
| typedef struct packet_s { | ||||
							
								
								
									
										26
									
								
								src/packet/packetping.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/packet/packetping.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| /** | ||||
|  * 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" | ||||
| #include "assert/assert.h" | ||||
|  | ||||
| void packetPingCreate(packet_t *packet) { | ||||
|   packetInit(packet, PACKET_TYPE_PING, sizeof(packetping_t)); | ||||
|   packet->data.ping.number = GetRandomValue(0, INT32_MAX); | ||||
| } | ||||
|  | ||||
| errorret_t packetPingClient(packet_t *packet) { | ||||
|   assertNotNull(packet, "Packet is NULL"); | ||||
|   assertTrue(packet->type == PACKET_TYPE_PING, "Packet type is not PING"); | ||||
|  | ||||
|   if(packet->length != sizeof(packetping_t)) { | ||||
|     return error("Ping packet length is not %d", sizeof(packetping_t)); | ||||
|   } | ||||
|  | ||||
|   return ERROR_OK; | ||||
| } | ||||
							
								
								
									
										28
									
								
								src/packet/packetping.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/packet/packetping.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| /** | ||||
|  * Copyright (c) 2025 Dominic Masters | ||||
|  *  | ||||
|  * This software is released under the MIT License. | ||||
|  * https://opensource.org/licenses/MIT | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
| #include "error/error.h" | ||||
|  | ||||
| typedef struct { | ||||
|   int32_t number; | ||||
| } packetping_t; | ||||
|  | ||||
| /** | ||||
|  * Creates a ping packet. | ||||
|  *  | ||||
|  * @param packet Pointer to the packet structure to initialize. | ||||
|  */ | ||||
| void packetPingCreate(packet_t *packet); | ||||
|  | ||||
| /** | ||||
|  * Handles a ping packet received FROM a server INTO a client. | ||||
|  *  | ||||
|  * @param packet Pointer to the packet structure to handle. | ||||
|  * @return ERROR_OK on success, or an error code on failure. | ||||
|  */ | ||||
| errorret_t packetPingClient(packet_t *packet); | ||||
							
								
								
									
										35
									
								
								src/packet/packetqueue.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/packet/packetqueue.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| /** | ||||
|  * Copyright (c) 2025 Dominic Masters | ||||
|  *  | ||||
|  * This software is released under the MIT License. | ||||
|  * https://opensource.org/licenses/MIT | ||||
|  */ | ||||
|  | ||||
| #include "packetqueue.h" | ||||
| #include "assert/assert.h" | ||||
| #include "util/memory.h" | ||||
|  | ||||
| void packetQueueInit(packetqueue_t *queue) { | ||||
|   assertNotNull(queue, "Packet queue is NULL"); | ||||
|   memoryZero(queue, sizeof(packetqueue_t)); | ||||
| } | ||||
|  | ||||
| void packetQueuePushIn(packetqueue_t *queue, const packet_t *packet) { | ||||
|   assertNotNull(queue, "Packet queue is NULL"); | ||||
|   assertNotNull(packet, "Packet is NULL"); | ||||
|   assertTrue( | ||||
|     queue->packetsInCount < PACKET_QUEUE_MAX_SIZE, "Packet queue is full" | ||||
|   ); | ||||
|  | ||||
|   queue->packetsIn[queue->packetsInCount++] = *packet; | ||||
| } | ||||
|  | ||||
| void packetQueuePushOut(packetqueue_t *queue, const packet_t *packet) { | ||||
|   assertNotNull(queue, "Packet queue is NULL"); | ||||
|   assertNotNull(packet, "Packet is NULL"); | ||||
|   assertTrue( | ||||
|     queue->packetsOutCount < PACKET_QUEUE_MAX_SIZE, "Packet queue is full" | ||||
|   ); | ||||
|  | ||||
|   queue->packetsOut[queue->packetsOutCount++] = *packet; | ||||
| } | ||||
							
								
								
									
										49
									
								
								src/packet/packetqueue.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/packet/packetqueue.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| /** | ||||
|  * Copyright (c) 2025 Dominic Masters | ||||
|  *  | ||||
|  * This software is released under the MIT License. | ||||
|  * https://opensource.org/licenses/MIT | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
| #include "packet/packet.h" | ||||
|  | ||||
| #define PACKET_QUEUE_MAX_SIZE 512 | ||||
|  | ||||
| typedef struct { | ||||
|   packet_t packetsIn[PACKET_QUEUE_MAX_SIZE]; | ||||
|   uint32_t packetsInCount; | ||||
|   packet_t packetsOut[PACKET_QUEUE_MAX_SIZE]; | ||||
|   uint32_t packetsOutCount; | ||||
|  | ||||
|   // Mutex for thread safety | ||||
|   pthread_mutex_t mutex; | ||||
|   // Condition variables for signaling | ||||
|   pthread_cond_t condIn; | ||||
|   pthread_cond_t condOut; | ||||
|   // Thread ID of the thread that owns the queue | ||||
|    | ||||
| } packetqueue_t; | ||||
|  | ||||
| /** | ||||
|  * Initializes the packet queue. | ||||
|  *  | ||||
|  * @param queue Pointer to the packet queue structure. | ||||
|  */ | ||||
| void packetQueueInit(packetqueue_t *queue); | ||||
|  | ||||
| /** | ||||
|  * Pushes a packet into the inbound packet queue. | ||||
|  *  | ||||
|  * @param queue Pointer to the packet queue structure. | ||||
|  * @param packet Pointer to the packet to be pushed. | ||||
|  */ | ||||
| void packetQueuePushIn(packetqueue_t *queue, const packet_t *packet); | ||||
|  | ||||
| /** | ||||
|  * Pushes a packet from the outbound packet queue. | ||||
|  *  | ||||
|  * @param queue Pointer to the packet queue structure. | ||||
|  * @param packet Pointer to the packet to be pushed. | ||||
|  */ | ||||
| void packetQueuePushOut(packetqueue_t *queue, const packet_t *packet); | ||||
| @@ -11,6 +11,5 @@ target_sources(${DUSK_TARGET_NAME} | ||||
| ) | ||||
|  | ||||
| # Subdirs | ||||
| add_subdirectory(packet) | ||||
| add_subdirectory(networked) | ||||
| add_subdirectory(singleplayer) | ||||
| @@ -113,7 +113,7 @@ void networkedServerClientCloseOnThread( | ||||
| } | ||||
|  | ||||
| ssize_t networkedServerClientRead( | ||||
|   serverclient_t * client, | ||||
|   const serverclient_t * client, | ||||
|   uint8_t *buffer, | ||||
|   const size_t len | ||||
| ) { | ||||
| @@ -136,6 +136,60 @@ ssize_t networkedServerClientRead( | ||||
|   return recv(client->networked.socket, buffer, len, 0); | ||||
| } | ||||
|  | ||||
| errorret_t networkedServerClientReadPacket( | ||||
|   const serverclient_t * client, | ||||
|   packet_t *packet | ||||
| ) { | ||||
|   uint8_t buffer[sizeof(packet_t)]; | ||||
|   ssize_t read; | ||||
|  | ||||
|   assertNotNull(client, "Client is NULL"); | ||||
|   assertNotNull(packet, "Packet is NULL"); | ||||
|   assertTrue( | ||||
|     client->server->type == SERVER_TYPE_NETWORKED, | ||||
|     "Server is not networked" | ||||
|   ); | ||||
|  | ||||
|   // Read packet ID | ||||
|   read = networkedServerClientRead(client, buffer, sizeof(packettype_t)); | ||||
|   if(read != sizeof(packettype_t)) { | ||||
|     return error("Failed to read packet ID"); | ||||
|   } | ||||
|  | ||||
|   packet->type = *(packettype_t *)buffer; | ||||
|   if(packet->type == PACKET_TYPE_INVALID) { | ||||
|     return error("Invalid packet type"); | ||||
|   } | ||||
|  | ||||
|   // Read length | ||||
|   read = networkedServerClientRead( | ||||
|     client, | ||||
|     buffer, | ||||
|     sizeof(uint32_t) | ||||
|   ); | ||||
|  | ||||
|   if(read != sizeof(uint32_t)) { | ||||
|     return error("Failed to read packet length"); | ||||
|   } | ||||
|  | ||||
|   packet->length = *(uint32_t *)buffer; | ||||
|   if(packet->length > sizeof(packetdata_t)) { | ||||
|     return error("Packet length is too large"); | ||||
|   } | ||||
|  | ||||
|   // Read data | ||||
|   read = networkedServerClientRead( | ||||
|     client, | ||||
|     (uint8_t *)&packet->data, | ||||
|     packet->length | ||||
|   ); | ||||
|   if(read != packet->length) { | ||||
|     return error("Failed to read packet data"); | ||||
|   } | ||||
|  | ||||
|   return ERROR_OK; | ||||
| } | ||||
|  | ||||
| errorret_t networkedServerClientWrite( | ||||
|   serverclient_t * client, | ||||
|   const uint8_t *data, | ||||
| @@ -225,18 +279,17 @@ void * networkedServerClientThread(void *arg) { | ||||
|     return NULL; | ||||
|   } | ||||
|  | ||||
|   // Start listening for packets. | ||||
|   client->state = SERVER_CLIENT_STATE_CONNECTED; | ||||
|   while(client->state == SERVER_CLIENT_STATE_CONNECTED) { | ||||
|     read = networkedServerClientRead(client, buffer, sizeof(buffer)); | ||||
|  | ||||
|     if(read <= 0) { | ||||
|       networkedServerClientCloseOnThread(client, "Failed to receive data"); | ||||
|     err = networkedServerClientReadPacket(client, &packet); | ||||
|     if(err != ERROR_OK) { | ||||
|       errorPrint(); | ||||
|       networkedServerClientCloseOnThread(client, "Failed to read packet"); | ||||
|       break; | ||||
|     } | ||||
|  | ||||
|     buffer[read] = '\0'; // Null-terminate the string | ||||
|     consolePrint("Received: %s", buffer); | ||||
|  | ||||
|     printf("Received packet type %d\n", packet.type); | ||||
|      | ||||
|     if(SERVER.state != SERVER_STATE_RUNNING) { | ||||
|       packetDisconnectCreate( | ||||
|   | ||||
| @@ -60,7 +60,7 @@ void networkedServerClientCloseOnThread( | ||||
|  * @return Number of bytes received. 0 or less indicates an error. | ||||
|  */ | ||||
| ssize_t networkedServerClientRead( | ||||
|   serverclient_t * client, | ||||
|   const serverclient_t * client, | ||||
|   uint8_t *buffer, | ||||
|   const size_t len | ||||
| ); | ||||
|   | ||||
| @@ -20,10 +20,12 @@ errorret_t serverClientAccept( | ||||
|   assertNotMainThread("Server client accept must not be main thread"); | ||||
|  | ||||
|   client->server = accept.server; | ||||
|  | ||||
|   packetQueueInit(&client->packetQueue); | ||||
|    | ||||
|   switch(accept.server->type) { | ||||
|     case SERVER_TYPE_NETWORKED: | ||||
|       ret = networkedServerClientAccept(client, accept); | ||||
|       break; | ||||
|  | ||||
|     default: | ||||
|       assertUnreachable("Unknown server type"); | ||||
|   | ||||
| @@ -6,8 +6,9 @@ | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
| #include "server/packet/packet.h" | ||||
| #include "packet/packet.h" | ||||
| #include "server/networked/networkedserverclient.h" | ||||
| #include "packet/packetqueue.h" | ||||
|  | ||||
| typedef struct server_s server_t; | ||||
|  | ||||
| @@ -28,6 +29,8 @@ typedef struct serverclientaccept_s { | ||||
| typedef struct serverclient_s { | ||||
|   server_t *server; | ||||
|   serverclientstate_t state; | ||||
|   packetqueue_t packetQueue; | ||||
|    | ||||
|   union { | ||||
|     networkedserverclient_t networked; | ||||
|   }; | ||||
|   | ||||
| @@ -8,4 +8,5 @@ target_sources(${DUSK_TARGET_NAME} | ||||
|   PRIVATE | ||||
|     memory.c | ||||
|     string.c | ||||
|     random.c | ||||
| ) | ||||
							
								
								
									
										27
									
								
								src/util/random.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/util/random.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| /** | ||||
|  * Copyright (c) 2025 Dominic Masters | ||||
|  *  | ||||
|  * This software is released under the MIT License. | ||||
|  * https://opensource.org/licenses/MIT | ||||
|  */ | ||||
|  | ||||
| #include "random.h" | ||||
| #include "assert/assert.h" | ||||
|  | ||||
| void randomInit() { | ||||
|   randomSeed(time(NULL)); | ||||
| } | ||||
|  | ||||
| void randomSeed(const uint32_t seed) { | ||||
|   srand(seed); | ||||
| } | ||||
|  | ||||
| int32_t randomI32(const int32_t min, const int32_t max) { | ||||
|   assertTrue(min < max, "Min is not less than max"); | ||||
|   return (rand() % (max - min)) + min; | ||||
| } | ||||
|  | ||||
| float_t randomF32(const float_t min, const float_t max) { | ||||
|   assertTrue(min < max, "Min is not less than max"); | ||||
|   return ((float_t)rand() / (float_t)RAND_MAX) * (max - min) + min; | ||||
| } | ||||
							
								
								
									
										43
									
								
								src/util/random.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/util/random.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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" | ||||
|  | ||||
| /** | ||||
|  * Initializes the random number generator with a random seed. | ||||
|  *  | ||||
|  * This function should be called once at the start of the program to | ||||
|  * initialize the random number generator with a random seed. It uses | ||||
|  * the current time to generate a seed. | ||||
|  */ | ||||
| void randomInit(); | ||||
|  | ||||
| /** | ||||
|  * Sets the random seed for the random number generator. | ||||
|  *  | ||||
|  * @param seed The seed to set. | ||||
|  */ | ||||
| void randomSeed(const uint32_t seed); | ||||
|  | ||||
| /** | ||||
|  * Generates a random integer between min and max. | ||||
|  *  | ||||
|  * @param min The minimum value (inclusive). | ||||
|  * @param max The maximum value (exclusive). | ||||
|  * @return A random integer between min and max. | ||||
|  */ | ||||
| int32_t randomI32(const int32_t min, const int32_t max); | ||||
|  | ||||
| /** | ||||
|  * Generates a random float between min and max. | ||||
|  *  | ||||
|  * @param min The minimum value (inclusive). | ||||
|  * @param max The maximum value (exclusive). | ||||
|  * @return A random float between min and max. | ||||
|  */ | ||||
| float_t randomF32(const float_t min, const float_t max); | ||||
		Reference in New Issue
	
	Block a user