diff --git a/src/client/client.c b/src/client/client.c index 1e30f48..14ffdd4 100644 --- a/src/client/client.c +++ b/src/client/client.c @@ -50,6 +50,7 @@ errorret_t clientConnect(const clientconnect_t connect) { } CLIENT.type = connect.type; + packetQueueInit(&CLIENT.packetQueue); switch(connect.type) { case CLIENT_TYPE_NETWORKED: @@ -64,6 +65,38 @@ errorret_t clientConnect(const clientconnect_t connect) { return ret; } +void clientUpdate() { + packet_t packet; + int32_t ret; + errorret_t err; + + if(CLIENT.state == CLIENT_STATE_DISCONNECTED) return; + + do { + ret = packetQueuePopIn( + &CLIENT.packetQueue, + &packet + ); + + if(ret == 0) break; + + if(ret < 0) { + clientDisconnect(); + consolePrint("Failed to pop packet"); + return; + } + + err = packetClientProcess(&packet, &CLIENT); + + if(err != ERROR_OK) { + clientDisconnect(); + consolePrint("Failed to process packet %s", errorString()); + errorFlush(); + return; + } + } while(true); +} + void clientDisconnect() { if(CLIENT.state == CLIENT_STATE_DISCONNECTED) return; diff --git a/src/client/client.h b/src/client/client.h index ad31e71..164dcc0 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -55,6 +55,11 @@ void clientInit(); */ errorret_t clientConnect(const clientconnect_t connect); +/** + * Updates the client state. + */ +void clientUpdate(); + /** * Disconnects the client from the server. */ diff --git a/src/client/networked/networkedclient.c b/src/client/networked/networkedclient.c index 52342f1..985e274 100644 --- a/src/client/networked/networkedclient.c +++ b/src/client/networked/networkedclient.c @@ -18,7 +18,6 @@ errorret_t networkedClientConnect( errorret_t err; char_t *ip = "127.0.0.1"; - assertNotNull(client, "Client is NULL"); assertTrue(client->type == CLIENT_TYPE_NETWORKED, "Client is not networked"); assertIsMainThread("Client connect must be on main thread"); @@ -328,7 +327,6 @@ void * networkedClientReadThread(void *arg) { pthread_cond_signal(&client->networked.cond); pthread_mutex_unlock(&client->networked.lock); - // Main loop while(client->state == CLIENT_STATE_CONNECTED) { pthread_mutex_lock(&client->networked.readLock); @@ -340,7 +338,10 @@ void * networkedClientReadThread(void *arg) { break; } - printf("Received packet type: %d, length: %d\n", packet.type, packet.length); + packetQueuePushIn( + &client->packetQueue, + &packet + ); pthread_mutex_unlock(&client->networked.readLock); } @@ -353,7 +354,11 @@ void * networkedClientReadThread(void *arg) { pthread_mutex_unlock(&client->networked.lock); } -void * networkedClientWriteThread(void *arg) { // Renamed from networkedClientRightThread +void * networkedClientWriteThread(void *arg) { + packet_t packet; + int32_t ret; + errorret_t err; + assertNotNull(arg, "Write thread argument is NULL"); assertNotMainThread("Write thread must not be on main thread"); @@ -363,13 +368,35 @@ void * networkedClientWriteThread(void *arg) { // Renamed from networkedClientRi "Write thread argument is not networked" ); - // Perform additional operations while(client->state == CLIENT_STATE_CONNECTED) { - pthread_mutex_lock(&client->networked.writeLock); // Lock before writing + pthread_mutex_lock(&client->networked.writeLock); - // Add logic for the write thread here - // Example: Writing packets to the client - // networkedClientWritePacket(client, &somePacket); + ret = packetQueuePopOut( + &client->packetQueue, + &packet + ); + + if(ret == 0) { + pthread_mutex_unlock(&client->networked.writeLock); + continue; + } + + if(ret < 0) { + consolePrint("Failed to pop packet from queue %s", errorString()); + errorFlush(); + client->state = CLIENT_STATE_DISCONNECTING; + pthread_mutex_unlock(&client->networked.writeLock); + break; + } + + err = networkedClientWritePacket(client, &packet); + if(err) { + consolePrint("Failed to write packet %s", errorString()); + errorFlush(); + client->state = CLIENT_STATE_DISCONNECTING; + pthread_mutex_unlock(&client->networked.writeLock); + break; + } pthread_mutex_unlock(&client->networked.writeLock); // Unlock after writing } diff --git a/src/client/networked/networkedclient.h b/src/client/networked/networkedclient.h index 0bbe09d..cb41f04 100644 --- a/src/client/networked/networkedclient.h +++ b/src/client/networked/networkedclient.h @@ -8,7 +8,7 @@ #pragma once #include "error/error.h" #include -#include "packet/packet.h" +#include "packet/packetqueue.h" typedef struct client_s client_t; typedef struct clientconnect_s clientconnect_t; diff --git a/src/main.c b/src/main.c index 0052568..4984819 100644 --- a/src/main.c +++ b/src/main.c @@ -34,9 +34,14 @@ int32_t main(const int32_t argc, const char **argv) { entityInit(&GAME.entities[GAME.entityCount++], ENTITY_TYPE_PLAYER); + float_t lastPing = -1; + float_t time = 0; + while(true) { inputUpdate(); consoleUpdate(); + serverUpdate(); + clientUpdate(); if(!CONSOLE.open) { for(uint32_t i = 0; i < GAME.entityCount; i++) { @@ -47,8 +52,18 @@ int32_t main(const int32_t argc, const char **argv) { renderresult_t result = renderDraw(); if(result != RENDER_OK) break; - if(exitRequested) break; + + time += GetFrameTime(); + if(time - lastPing > 1.0f) { + lastPing = time; + + if(CLIENT.state != CLIENT_STATE_CONNECTED) continue; + packet_t packet; + packetPingCreate(&packet); + packetQueuePushOut(&CLIENT.packetQueue, &packet); + lastPing = time; + } } serverDispose(); diff --git a/src/packet/packet.c b/src/packet/packet.c index 67b33f8..2029d97 100644 --- a/src/packet/packet.c +++ b/src/packet/packet.c @@ -8,6 +8,8 @@ #include "packet.h" #include "assert/assert.h" #include "util/memory.h" +#include "client/client.h" +#include "server/server.h" void packetInit( packet_t *packet, @@ -26,4 +28,46 @@ void packetInit( packet->type = type; packet->length = length; +} + +errorret_t packetClientProcess( + const packet_t *packet, + client_t *client +) { + assertNotNull(packet, "Packet is NULL"); + assertNotNull(client, "Client is NULL"); + assertTrue( + client->type == CLIENT_TYPE_NETWORKED, + "Client is not networked" + ); + assertIsMainThread("Client process must be on main thread"); + + switch(packet->type) { + case PACKET_TYPE_PING: + return packetPingClientProcess(packet, client); + + default: + return error("Unknown packet type %d", packet->type); + } +} + +errorret_t packetServerProcess( + const packet_t *packet, + serverclient_t *client +) { + assertNotNull(packet, "Packet is NULL"); + assertNotNull(client, "Client is NULL"); + assertTrue( + client->server->type == SERVER_TYPE_NETWORKED, + "Server is not networked" + ); + assertIsMainThread("Server client process must be on main thread"); + + switch(packet->type) { + case PACKET_TYPE_PING: + return packetPingServerProcess(packet, client); + + default: + return error("Unknown packet type %d", packet->type); + } } \ No newline at end of file diff --git a/src/packet/packet.h b/src/packet/packet.h index fd79d6d..51aeed0 100644 --- a/src/packet/packet.h +++ b/src/packet/packet.h @@ -42,4 +42,30 @@ void packetInit( packet_t *packet, const packettype_t type, const uint32_t length +); + +/** + * Processes a packet for a given client. Will auto-decide the correct method to + * handle the process + * + * @param packet Pointer to the packet structure to process. + * @param client Pointer to the client structure. + * @return ERROR_OK on success, or an error code on failure. + */ +errorret_t packetClientProcess( + const packet_t *packet, + client_t *client +); + +/** + * Processes a packet for a given server client. Will auto-decide the correct + * method to handle the process + * + * @param packet Pointer to the packet structure to process. + * @param client Pointer to the server client structure. + * @return ERROR_OK on success, or an error code on failure. + */ +errorret_t packetServerProcess( + const packet_t *packet, + serverclient_t *client ); \ No newline at end of file diff --git a/src/packet/packetping.c b/src/packet/packetping.c index 7d0c143..961bf3f 100644 --- a/src/packet/packetping.c +++ b/src/packet/packetping.c @@ -8,19 +8,33 @@ #include "packet.h" #include "util/memory.h" #include "assert/assert.h" +#include "server/server.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"); +errorret_t packetPingClientProcess( + const packet_t *packet, + client_t *client +) { + printf("Client got Pong!\n"); + return ERROR_OK; +} +errorret_t packetPingServerProcess( + const packet_t *packet, + serverclient_t *client +) { if(packet->length != sizeof(packetping_t)) { return error("Ping packet length is not %d", sizeof(packetping_t)); } + printf("Server got Ping!\n"); + + packet_t pong; + packetPingCreate(&pong); + packetQueuePushOut(&client->packetQueue, &pong); return ERROR_OK; } \ No newline at end of file diff --git a/src/packet/packetping.h b/src/packet/packetping.h index ca3c2ac..8735c19 100644 --- a/src/packet/packetping.h +++ b/src/packet/packetping.h @@ -8,6 +8,9 @@ #pragma once #include "error/error.h" +typedef struct client_s client_t; +typedef struct serverclient_s serverclient_t; + typedef struct { int32_t number; } packetping_t; @@ -20,9 +23,25 @@ typedef struct { void packetPingCreate(packet_t *packet); /** - * Handles a ping packet received FROM a server INTO a client. + * Validates a ping packet received FROM a client INTO a server. * - * @param packet Pointer to the packet structure to handle. + * @param packet Pointer to the packet structure to validate. + * @param client Pointer to the server client structure. * @return ERROR_OK on success, or an error code on failure. */ -errorret_t packetPingClient(packet_t *packet); \ No newline at end of file +errorret_t packetPingClientProcess( + const packet_t *packet, + client_t *client +); + +/** + * Handles a ping packet received FROM a client INTO a server. + * + * @param packet Pointer to the packet structure to handle. + * @param client Pointer to the server client structure. + * @return ERROR_OK on success, or an error code on failure. + */ +errorret_t packetPingServerProcess( + const packet_t *packet, + serverclient_t *client +); \ No newline at end of file diff --git a/src/packet/packetqueue.c b/src/packet/packetqueue.c index 318f746..df89e27 100644 --- a/src/packet/packetqueue.c +++ b/src/packet/packetqueue.c @@ -12,52 +12,75 @@ void packetQueueInit(packetqueue_t *queue) { assertNotNull(queue, "Packet queue is NULL"); memoryZero(queue, sizeof(packetqueue_t)); + pthread_mutex_init(&queue->lock, NULL); } void packetQueuePushIn(packetqueue_t *queue, const packet_t *packet) { assertNotNull(queue, "Packet queue is NULL"); assertNotNull(packet, "Packet is NULL"); + pthread_mutex_lock(&queue->lock); assertTrue( queue->packetsInCount < PACKET_QUEUE_MAX_SIZE, "Inbound packet queue is full" ); queue->packetsIn[queue->packetsInCount++] = *packet; + pthread_mutex_unlock(&queue->lock); } void packetQueuePushOut(packetqueue_t *queue, const packet_t *packet) { assertNotNull(queue, "Packet queue is NULL"); assertNotNull(packet, "Packet is NULL"); + pthread_mutex_lock(&queue->lock); assertTrue( queue->packetsOutCount < PACKET_QUEUE_MAX_SIZE, "Outbound packet queue is full" ); queue->packetsOut[queue->packetsOutCount++] = *packet; + pthread_mutex_unlock(&queue->lock); } -int packetQueuePopIn(packetqueue_t *queue, packet_t *packet) { +int32_t packetQueuePopIn(packetqueue_t *queue, packet_t *packet) { assertNotNull(queue, "Packet queue is NULL"); assertNotNull(packet, "Packet is NULL"); - if(queue->packetsInCount == 0) return 0; + pthread_mutex_lock(&queue->lock); + if(queue->packetsInCount == 0) { + pthread_mutex_unlock(&queue->lock); + return 0; + } *packet = queue->packetsIn[0]; - for(uint32_t i = 1; i < queue->packetsInCount; i++) { - queue->packetsIn[i - 1] = queue->packetsIn[i]; + if(queue->packetsInCount > 1) { + memoryCopy( + &queue->packetsIn[0], + &queue->packetsIn[1], + (queue->packetsInCount - 1) * sizeof(packet_t) + ); } queue->packetsInCount--; + pthread_mutex_unlock(&queue->lock); return 1; } -int packetQueuePopOut(packetqueue_t *queue, packet_t *packet) { +int32_t packetQueuePopOut(packetqueue_t *queue, packet_t *packet) { assertNotNull(queue, "Packet queue is NULL"); assertNotNull(packet, "Packet is NULL"); - if(queue->packetsOutCount == 0) return 0; + pthread_mutex_lock(&queue->lock); + if(queue->packetsOutCount == 0) { + pthread_mutex_unlock(&queue->lock); + return 0; + } *packet = queue->packetsOut[0]; - for(uint32_t i = 1; i < queue->packetsOutCount; i++) { - queue->packetsOut[i - 1] = queue->packetsOut[i]; + if(queue->packetsOutCount > 1) { + memoryCopy( + &queue->packetsOut[0], + &queue->packetsOut[1], + (queue->packetsOutCount - 1) * sizeof(packet_t) + ); } queue->packetsOutCount--; + pthread_mutex_unlock(&queue->lock); return 1; } \ No newline at end of file diff --git a/src/packet/packetqueue.h b/src/packet/packetqueue.h index 528ca99..1e92b5d 100644 --- a/src/packet/packetqueue.h +++ b/src/packet/packetqueue.h @@ -7,6 +7,7 @@ #pragma once #include "packet/packet.h" +#include #define PACKET_QUEUE_MAX_SIZE 512 @@ -15,6 +16,7 @@ typedef struct { uint32_t packetsInCount; packet_t packetsOut[PACKET_QUEUE_MAX_SIZE]; uint32_t packetsOutCount; + pthread_mutex_t lock; } packetqueue_t; /** @@ -47,7 +49,7 @@ void packetQueuePushOut(packetqueue_t *queue, const packet_t *packet); * @param packet Pointer to the packet to store the popped packet. * @return 1 if a packet was popped, 0 otherwise. */ -int packetQueuePopIn(packetqueue_t *queue, packet_t *packet); +int32_t packetQueuePopIn(packetqueue_t *queue, packet_t *packet); /** * Pops a packet from the outbound packet queue. @@ -56,4 +58,4 @@ int packetQueuePopIn(packetqueue_t *queue, packet_t *packet); * @param packet Pointer to the packet to store the popped packet. * @return 1 if a packet was popped, 0 otherwise. */ -int packetQueuePopOut(packetqueue_t *queue, packet_t *packet); \ No newline at end of file +int32_t packetQueuePopOut(packetqueue_t *queue, packet_t *packet); \ No newline at end of file diff --git a/src/server/networked/networkedserverclient.c b/src/server/networked/networkedserverclient.c index 20a253e..6455b04 100644 --- a/src/server/networked/networkedserverclient.c +++ b/src/server/networked/networkedserverclient.c @@ -26,7 +26,7 @@ errorret_t networkedServerClientAccept( client->networked.socket = accept.networked.socket; // Set timeout to 8 seconds - client->networked.timeout.tv_sec = 8; + client->networked.timeout.tv_sec = 4; client->networked.timeout.tv_usec = 0; // Initialize mutexs @@ -282,7 +282,9 @@ void * networkedServerClientReadThread(void *arg) { packetWelcomeCreate(&packet); err = networkedServerClientWritePacket(client, &packet); if(err != ERROR_OK) { - networkedServerClientCloseOnThread(client, "Failed to send welcome message"); + networkedServerClientCloseOnThread( + client, "Failed to send welcome message" + ); return NULL; } @@ -305,6 +307,16 @@ void * networkedServerClientReadThread(void *arg) { while(client->state == SERVER_CLIENT_STATE_CONNECTED) { pthread_mutex_lock(&client->networked.readLock); + err = networkedServerClientReadPacket(client, &packet); + if(err != ERROR_OK) { + consolePrint("Failed to read packet %s", errorString()); + errorFlush(); + pthread_mutex_unlock(&client->networked.readLock); + break; + } + + packetQueuePushIn(&client->packetQueue, &packet); + pthread_mutex_unlock(&client->networked.readLock); } @@ -316,6 +328,10 @@ void * networkedServerClientReadThread(void *arg) { } void * networkedServerClientWriteThread(void *arg) { + packet_t packet; + int32_t ret; + errorret_t err; + assertNotNull(arg, "Client is NULL"); assertNotMainThread("Client thread must not be main thread"); assertTrue( @@ -332,6 +348,33 @@ void * networkedServerClientWriteThread(void *arg) { while(client->state == SERVER_CLIENT_STATE_CONNECTED) { pthread_mutex_lock(&client->networked.writeLock); + ret = packetQueuePopOut( + &client->packetQueue, + &packet + ); + + if(ret == 0) { + pthread_mutex_unlock(&client->networked.writeLock); + continue; + } + + if(ret < 0) { + consolePrint("Failed to pop packet %s", errorString()); + errorFlush(); + client->state = SERVER_CLIENT_STATE_DISCONNECTING; + pthread_mutex_unlock(&client->networked.writeLock); + break; + } + + err = networkedServerClientWritePacket(client, &packet); + if(err != ERROR_OK) { + consolePrint("Failed to write packet %s", errorString()); + errorFlush(); + client->state = SERVER_CLIENT_STATE_DISCONNECTING; + pthread_mutex_unlock(&client->networked.writeLock); + break; + } + pthread_mutex_unlock(&client->networked.writeLock); } diff --git a/src/server/server.c b/src/server/server.c index 996da41..f5fb9c1 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -96,6 +96,16 @@ uint8_t serverGetClientCount() { return clientCount; } +void serverUpdate() { + assertIsMainThread("Server update must be on main thread"); + if(SERVER.state != SERVER_STATE_RUNNING) return; + + // Update all clients + for(uint8_t i = 0; i < SERVER_MAX_CLIENTS; i++) { + serverClientUpdate(&SERVER.clients[i]); + } +} + void serverStop() { assertIsMainThread("Server stop must be on main thread"); if(SERVER.state == SERVER_STATE_STOPPED) return; diff --git a/src/server/server.h b/src/server/server.h index 13e83ce..55a81b2 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -72,6 +72,11 @@ errorret_t serverStart(const serverstart_t start); */ uint8_t serverGetClientCount(); +/** + * Performs a (main thread) update to process clients on the server. + */ +void serverUpdate(); + /** * Stop the server * diff --git a/src/server/serverclient.c b/src/server/serverclient.c index efc46ff..2054c5c 100644 --- a/src/server/serverclient.c +++ b/src/server/serverclient.c @@ -35,6 +35,34 @@ errorret_t serverClientAccept( return ret; } +void serverClientUpdate(serverclient_t *client) { + assertNotNull(client, "Client is NULL"); + assertIsMainThread("Server client update must be on main thread"); + + if(client->state != SERVER_CLIENT_STATE_CONNECTED) return; + + packet_t packet; + int32_t ret = 1; + + // Process packets + do { + ret = packetQueuePopIn( + &client->packetQueue, + &packet + ); + + if(ret == 0) break; + + if(ret < 0) { + serverClientClose(client); + consolePrint("Failed to pop packet"); + return; + } + + packetServerProcess(&packet, client); + } while(true); +} + void serverClientClose(serverclient_t *client) { assertNotNull(client, "Client is NULL"); assertIsMainThread("Server client close must be on main thread"); diff --git a/src/server/serverclient.h b/src/server/serverclient.h index 6aa2653..e4c54a8 100644 --- a/src/server/serverclient.h +++ b/src/server/serverclient.h @@ -48,6 +48,14 @@ errorret_t serverClientAccept( const serverclientaccept_t accept ); +/** + * Handles the main thread updating of this server client. This is where all + * the packets will be handled and processed. + * + * @param client Pointer to the server client structure. + */ +void serverClientUpdate(serverclient_t *client); + /** * Closes the connection to a server client. Waits for the thread to finish. *