Basic packet system done.

This commit is contained in:
2025-04-22 20:03:23 -05:00
parent 6b523301ab
commit d0a8f3813d
16 changed files with 331 additions and 29 deletions

View File

@ -50,6 +50,7 @@ errorret_t clientConnect(const clientconnect_t connect) {
} }
CLIENT.type = connect.type; CLIENT.type = connect.type;
packetQueueInit(&CLIENT.packetQueue);
switch(connect.type) { switch(connect.type) {
case CLIENT_TYPE_NETWORKED: case CLIENT_TYPE_NETWORKED:
@ -64,6 +65,38 @@ errorret_t clientConnect(const clientconnect_t connect) {
return ret; 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() { void clientDisconnect() {
if(CLIENT.state == CLIENT_STATE_DISCONNECTED) return; if(CLIENT.state == CLIENT_STATE_DISCONNECTED) return;

View File

@ -55,6 +55,11 @@ void clientInit();
*/ */
errorret_t clientConnect(const clientconnect_t connect); errorret_t clientConnect(const clientconnect_t connect);
/**
* Updates the client state.
*/
void clientUpdate();
/** /**
* Disconnects the client from the server. * Disconnects the client from the server.
*/ */

View File

@ -18,7 +18,6 @@ errorret_t networkedClientConnect(
errorret_t err; errorret_t err;
char_t *ip = "127.0.0.1"; char_t *ip = "127.0.0.1";
assertNotNull(client, "Client is NULL"); assertNotNull(client, "Client is NULL");
assertTrue(client->type == CLIENT_TYPE_NETWORKED, "Client is not networked"); assertTrue(client->type == CLIENT_TYPE_NETWORKED, "Client is not networked");
assertIsMainThread("Client connect must be on main thread"); assertIsMainThread("Client connect must be on main thread");
@ -328,7 +327,6 @@ void * networkedClientReadThread(void *arg) {
pthread_cond_signal(&client->networked.cond); pthread_cond_signal(&client->networked.cond);
pthread_mutex_unlock(&client->networked.lock); pthread_mutex_unlock(&client->networked.lock);
// Main loop
while(client->state == CLIENT_STATE_CONNECTED) { while(client->state == CLIENT_STATE_CONNECTED) {
pthread_mutex_lock(&client->networked.readLock); pthread_mutex_lock(&client->networked.readLock);
@ -340,7 +338,10 @@ void * networkedClientReadThread(void *arg) {
break; break;
} }
printf("Received packet type: %d, length: %d\n", packet.type, packet.length); packetQueuePushIn(
&client->packetQueue,
&packet
);
pthread_mutex_unlock(&client->networked.readLock); pthread_mutex_unlock(&client->networked.readLock);
} }
@ -353,7 +354,11 @@ void * networkedClientReadThread(void *arg) {
pthread_mutex_unlock(&client->networked.lock); 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"); assertNotNull(arg, "Write thread argument is NULL");
assertNotMainThread("Write thread must not be on main thread"); 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" "Write thread argument is not networked"
); );
// Perform additional operations
while(client->state == CLIENT_STATE_CONNECTED) { 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 ret = packetQueuePopOut(
// Example: Writing packets to the client &client->packetQueue,
// networkedClientWritePacket(client, &somePacket); &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 pthread_mutex_unlock(&client->networked.writeLock); // Unlock after writing
} }

View File

@ -8,7 +8,7 @@
#pragma once #pragma once
#include "error/error.h" #include "error/error.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include "packet/packet.h" #include "packet/packetqueue.h"
typedef struct client_s client_t; typedef struct client_s client_t;
typedef struct clientconnect_s clientconnect_t; typedef struct clientconnect_s clientconnect_t;

View File

@ -34,9 +34,14 @@ int32_t main(const int32_t argc, const char **argv) {
entityInit(&GAME.entities[GAME.entityCount++], ENTITY_TYPE_PLAYER); entityInit(&GAME.entities[GAME.entityCount++], ENTITY_TYPE_PLAYER);
float_t lastPing = -1;
float_t time = 0;
while(true) { while(true) {
inputUpdate(); inputUpdate();
consoleUpdate(); consoleUpdate();
serverUpdate();
clientUpdate();
if(!CONSOLE.open) { if(!CONSOLE.open) {
for(uint32_t i = 0; i < GAME.entityCount; i++) { 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(); renderresult_t result = renderDraw();
if(result != RENDER_OK) break; if(result != RENDER_OK) break;
if(exitRequested) 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(); serverDispose();

View File

@ -8,6 +8,8 @@
#include "packet.h" #include "packet.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "util/memory.h" #include "util/memory.h"
#include "client/client.h"
#include "server/server.h"
void packetInit( void packetInit(
packet_t *packet, packet_t *packet,
@ -27,3 +29,45 @@ void packetInit(
packet->type = type; packet->type = type;
packet->length = length; 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);
}
}

View File

@ -43,3 +43,29 @@ void packetInit(
const packettype_t type, const packettype_t type,
const uint32_t length 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
);

View File

@ -8,19 +8,33 @@
#include "packet.h" #include "packet.h"
#include "util/memory.h" #include "util/memory.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "server/server.h"
void packetPingCreate(packet_t *packet) { void packetPingCreate(packet_t *packet) {
packetInit(packet, PACKET_TYPE_PING, sizeof(packetping_t)); packetInit(packet, PACKET_TYPE_PING, sizeof(packetping_t));
packet->data.ping.number = GetRandomValue(0, INT32_MAX); packet->data.ping.number = GetRandomValue(0, INT32_MAX);
} }
errorret_t packetPingClient(packet_t *packet) { errorret_t packetPingClientProcess(
assertNotNull(packet, "Packet is NULL"); const packet_t *packet,
assertTrue(packet->type == PACKET_TYPE_PING, "Packet type is not PING"); 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)) { if(packet->length != sizeof(packetping_t)) {
return error("Ping packet length is not %d", 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; return ERROR_OK;
} }

View File

@ -8,6 +8,9 @@
#pragma once #pragma once
#include "error/error.h" #include "error/error.h"
typedef struct client_s client_t;
typedef struct serverclient_s serverclient_t;
typedef struct { typedef struct {
int32_t number; int32_t number;
} packetping_t; } packetping_t;
@ -20,9 +23,25 @@ typedef struct {
void packetPingCreate(packet_t *packet); 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. * @return ERROR_OK on success, or an error code on failure.
*/ */
errorret_t packetPingClient(packet_t *packet); 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
);

View File

@ -12,52 +12,75 @@
void packetQueueInit(packetqueue_t *queue) { void packetQueueInit(packetqueue_t *queue) {
assertNotNull(queue, "Packet queue is NULL"); assertNotNull(queue, "Packet queue is NULL");
memoryZero(queue, sizeof(packetqueue_t)); memoryZero(queue, sizeof(packetqueue_t));
pthread_mutex_init(&queue->lock, NULL);
} }
void packetQueuePushIn(packetqueue_t *queue, const packet_t *packet) { void packetQueuePushIn(packetqueue_t *queue, const packet_t *packet) {
assertNotNull(queue, "Packet queue is NULL"); assertNotNull(queue, "Packet queue is NULL");
assertNotNull(packet, "Packet is NULL"); assertNotNull(packet, "Packet is NULL");
pthread_mutex_lock(&queue->lock);
assertTrue( assertTrue(
queue->packetsInCount < PACKET_QUEUE_MAX_SIZE, "Inbound packet queue is full" queue->packetsInCount < PACKET_QUEUE_MAX_SIZE, "Inbound packet queue is full"
); );
queue->packetsIn[queue->packetsInCount++] = *packet; queue->packetsIn[queue->packetsInCount++] = *packet;
pthread_mutex_unlock(&queue->lock);
} }
void packetQueuePushOut(packetqueue_t *queue, const packet_t *packet) { void packetQueuePushOut(packetqueue_t *queue, const packet_t *packet) {
assertNotNull(queue, "Packet queue is NULL"); assertNotNull(queue, "Packet queue is NULL");
assertNotNull(packet, "Packet is NULL"); assertNotNull(packet, "Packet is NULL");
pthread_mutex_lock(&queue->lock);
assertTrue( assertTrue(
queue->packetsOutCount < PACKET_QUEUE_MAX_SIZE, "Outbound packet queue is full" queue->packetsOutCount < PACKET_QUEUE_MAX_SIZE, "Outbound packet queue is full"
); );
queue->packetsOut[queue->packetsOutCount++] = *packet; 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(queue, "Packet queue is NULL");
assertNotNull(packet, "Packet 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]; *packet = queue->packetsIn[0];
for(uint32_t i = 1; i < queue->packetsInCount; i++) { if(queue->packetsInCount > 1) {
queue->packetsIn[i - 1] = queue->packetsIn[i]; memoryCopy(
&queue->packetsIn[0],
&queue->packetsIn[1],
(queue->packetsInCount - 1) * sizeof(packet_t)
);
} }
queue->packetsInCount--; queue->packetsInCount--;
pthread_mutex_unlock(&queue->lock);
return 1; 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(queue, "Packet queue is NULL");
assertNotNull(packet, "Packet 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]; *packet = queue->packetsOut[0];
for(uint32_t i = 1; i < queue->packetsOutCount; i++) { if(queue->packetsOutCount > 1) {
queue->packetsOut[i - 1] = queue->packetsOut[i]; memoryCopy(
&queue->packetsOut[0],
&queue->packetsOut[1],
(queue->packetsOutCount - 1) * sizeof(packet_t)
);
} }
queue->packetsOutCount--; queue->packetsOutCount--;
pthread_mutex_unlock(&queue->lock);
return 1; return 1;
} }

View File

@ -7,6 +7,7 @@
#pragma once #pragma once
#include "packet/packet.h" #include "packet/packet.h"
#include <pthread.h>
#define PACKET_QUEUE_MAX_SIZE 512 #define PACKET_QUEUE_MAX_SIZE 512
@ -15,6 +16,7 @@ typedef struct {
uint32_t packetsInCount; uint32_t packetsInCount;
packet_t packetsOut[PACKET_QUEUE_MAX_SIZE]; packet_t packetsOut[PACKET_QUEUE_MAX_SIZE];
uint32_t packetsOutCount; uint32_t packetsOutCount;
pthread_mutex_t lock;
} packetqueue_t; } 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. * @param packet Pointer to the packet to store the popped packet.
* @return 1 if a packet was popped, 0 otherwise. * @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. * 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. * @param packet Pointer to the packet to store the popped packet.
* @return 1 if a packet was popped, 0 otherwise. * @return 1 if a packet was popped, 0 otherwise.
*/ */
int packetQueuePopOut(packetqueue_t *queue, packet_t *packet); int32_t packetQueuePopOut(packetqueue_t *queue, packet_t *packet);

View File

@ -26,7 +26,7 @@ errorret_t networkedServerClientAccept(
client->networked.socket = accept.networked.socket; client->networked.socket = accept.networked.socket;
// Set timeout to 8 seconds // Set timeout to 8 seconds
client->networked.timeout.tv_sec = 8; client->networked.timeout.tv_sec = 4;
client->networked.timeout.tv_usec = 0; client->networked.timeout.tv_usec = 0;
// Initialize mutexs // Initialize mutexs
@ -282,7 +282,9 @@ void * networkedServerClientReadThread(void *arg) {
packetWelcomeCreate(&packet); packetWelcomeCreate(&packet);
err = networkedServerClientWritePacket(client, &packet); err = networkedServerClientWritePacket(client, &packet);
if(err != ERROR_OK) { if(err != ERROR_OK) {
networkedServerClientCloseOnThread(client, "Failed to send welcome message"); networkedServerClientCloseOnThread(
client, "Failed to send welcome message"
);
return NULL; return NULL;
} }
@ -305,6 +307,16 @@ void * networkedServerClientReadThread(void *arg) {
while(client->state == SERVER_CLIENT_STATE_CONNECTED) { while(client->state == SERVER_CLIENT_STATE_CONNECTED) {
pthread_mutex_lock(&client->networked.readLock); 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); pthread_mutex_unlock(&client->networked.readLock);
} }
@ -316,6 +328,10 @@ void * networkedServerClientReadThread(void *arg) {
} }
void * networkedServerClientWriteThread(void *arg) { void * networkedServerClientWriteThread(void *arg) {
packet_t packet;
int32_t ret;
errorret_t err;
assertNotNull(arg, "Client is NULL"); assertNotNull(arg, "Client is NULL");
assertNotMainThread("Client thread must not be main thread"); assertNotMainThread("Client thread must not be main thread");
assertTrue( assertTrue(
@ -332,6 +348,33 @@ void * networkedServerClientWriteThread(void *arg) {
while(client->state == SERVER_CLIENT_STATE_CONNECTED) { while(client->state == SERVER_CLIENT_STATE_CONNECTED) {
pthread_mutex_lock(&client->networked.writeLock); 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); pthread_mutex_unlock(&client->networked.writeLock);
} }

View File

@ -96,6 +96,16 @@ uint8_t serverGetClientCount() {
return clientCount; 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() { void serverStop() {
assertIsMainThread("Server stop must be on main thread"); assertIsMainThread("Server stop must be on main thread");
if(SERVER.state == SERVER_STATE_STOPPED) return; if(SERVER.state == SERVER_STATE_STOPPED) return;

View File

@ -72,6 +72,11 @@ errorret_t serverStart(const serverstart_t start);
*/ */
uint8_t serverGetClientCount(); uint8_t serverGetClientCount();
/**
* Performs a (main thread) update to process clients on the server.
*/
void serverUpdate();
/** /**
* Stop the server * Stop the server
* *

View File

@ -35,6 +35,34 @@ errorret_t serverClientAccept(
return ret; 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) { void serverClientClose(serverclient_t *client) {
assertNotNull(client, "Client is NULL"); assertNotNull(client, "Client is NULL");
assertIsMainThread("Server client close must be on main thread"); assertIsMainThread("Server client close must be on main thread");

View File

@ -48,6 +48,14 @@ errorret_t serverClientAccept(
const serverclientaccept_t accept 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. * Closes the connection to a server client. Waits for the thread to finish.
* *