net changes?
This commit is contained in:
@ -9,5 +9,4 @@ target_sources(${DUSK_TARGET_NAME}
|
||||
client.c
|
||||
)
|
||||
|
||||
# Subdirs
|
||||
add_subdirectory(networked)
|
||||
# Subdirs
|
@ -6,122 +6,91 @@
|
||||
*/
|
||||
|
||||
#include "client.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "console/console.h"
|
||||
#include "network/server/server.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
client_t CLIENT;
|
||||
|
||||
void cmdJoin(const consolecmdexec_t *exec) {
|
||||
clientconnect_t connect;
|
||||
|
||||
if(exec->argc > 0) {
|
||||
connect.type = CLIENT_TYPE_NETWORKED;
|
||||
connect.networked.port = SERVER_DEFAULT_PORT;
|
||||
} else {
|
||||
connect.type = CLIENT_TYPE_SINGLE_PLAYER;
|
||||
}
|
||||
|
||||
errorret_t ret = clientConnect(connect);
|
||||
if(ret == ERROR_OK) {
|
||||
consolePrint("Connected to server");
|
||||
} else {
|
||||
consolePrint("Failed to connect to server: %s", errorString());
|
||||
}
|
||||
errorFlush();
|
||||
}
|
||||
|
||||
void cmdLeave(const consolecmdexec_t *exec) {
|
||||
clientDisconnect();
|
||||
}
|
||||
|
||||
void clientInit() {
|
||||
memoryZero(&CLIENT, sizeof(client_t));
|
||||
consoleRegCmd("join", cmdJoin);
|
||||
consoleRegCmd("leave", cmdLeave);
|
||||
}
|
||||
|
||||
errorret_t clientConnect(const clientconnect_t connect) {
|
||||
errorret_t ret;
|
||||
void clientConnect(const clientconnect_t connect) {
|
||||
int32_t error;
|
||||
|
||||
// Don't connect if already connected.
|
||||
if(CLIENT.state != CLIENT_STATE_DISCONNECTED) {
|
||||
return error("Client is already connected");
|
||||
}
|
||||
|
||||
// Init the client.
|
||||
assertTrue(
|
||||
CLIENT.state == CLIENT_STATE_DISCONNECTED,
|
||||
"Client is not in a disconnected state."
|
||||
);
|
||||
|
||||
// Reset state
|
||||
packetQueueInit(&CLIENT.queue);
|
||||
CLIENT.error = CLIENT_ERROR_NO_ERROR;
|
||||
CLIENT.state = CLIENT_STATE_CONNECTING;
|
||||
CLIENT.type = connect.type;
|
||||
packetQueueInit(&CLIENT.packetQueue);
|
||||
|
||||
// Change how we connect based on the type.
|
||||
|
||||
// Init the properties based on the type of client.
|
||||
switch(connect.type) {
|
||||
case CLIENT_TYPE_NETWORKED:
|
||||
ret = networkedClientConnect(&CLIENT, connect.networked);
|
||||
networkClientConnect(&CLIENT, connect);
|
||||
break;
|
||||
|
||||
case CLIENT_TYPE_SINGLE_PLAYER:
|
||||
break;
|
||||
|
||||
default:
|
||||
assertUnreachable("Invalid client type");
|
||||
assertUnreachable("Invalid client connection type.");
|
||||
break;
|
||||
}
|
||||
|
||||
// Should be connected now.
|
||||
if(ret != ERROR_OK) CLIENT.state = CLIENT_STATE_DISCONNECTED;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void clientUpdate() {
|
||||
packet_t packet;
|
||||
int32_t ret;
|
||||
errorret_t err;
|
||||
|
||||
// Don't do anything if not connected
|
||||
if(CLIENT.state == CLIENT_STATE_DISCONNECTED) return;
|
||||
|
||||
do {
|
||||
// Pop the packet off the queue.
|
||||
ret = packetQueuePopIn(
|
||||
&CLIENT.packetQueue,
|
||||
&packet
|
||||
);
|
||||
|
||||
// No more packets to process, just break out.
|
||||
if(ret == 0) break;
|
||||
|
||||
// An error occured?
|
||||
if(ret < 0) {
|
||||
clientDisconnect();
|
||||
consolePrint("Failed to pop packet");
|
||||
return;
|
||||
}
|
||||
|
||||
// Process the packet
|
||||
err = packetClientProcess(&packet, &CLIENT);
|
||||
if(err != ERROR_OK) {
|
||||
clientDisconnect();
|
||||
consolePrint("Failed to process packet %s", errorString());
|
||||
errorFlush();
|
||||
return;
|
||||
}
|
||||
} while(true);
|
||||
// Spawn the connect thread which will be responsible from here on out.
|
||||
error = pthread_create(
|
||||
&CLIENT.connectThread,
|
||||
NULL,
|
||||
(void *(*)(void *))clientThreadConnect,
|
||||
&CLIENT
|
||||
);
|
||||
if(error != 0) {
|
||||
CLIENT.state = CLIENT_STATE_DISCONNECTED;
|
||||
CLIENT.error = CLIENT_ERROR_CONNECT_THREAD_SPAWN_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
void clientDisconnect() {
|
||||
// Don't disconnect if already disconnected.
|
||||
if(CLIENT.state == CLIENT_STATE_DISCONNECTED) return;
|
||||
if(CLIENT.state == CLIENT_STATE_DISCONNECTING) return;
|
||||
}
|
||||
|
||||
// Disconnect the client.
|
||||
switch(CLIENT.type) {
|
||||
case CLIENT_TYPE_NETWORKED:
|
||||
networkedClientDisconnect(&CLIENT);
|
||||
break;
|
||||
|
||||
default:
|
||||
assertUnreachable("Invalid client type");
|
||||
}
|
||||
|
||||
consolePrint("Client disconnected");
|
||||
void clientUpdate() {
|
||||
assertIsMainThread("Client update called from non-main thread.");
|
||||
}
|
||||
|
||||
void clientDispose() {
|
||||
clientDisconnect();
|
||||
if(CLIENT.state != CLIENT_STATE_DISCONNECTED) {
|
||||
clientDisconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void * clientThreadConnect(void *arg) {
|
||||
assertNotNull(arg, "Client thread connect argument is null.");
|
||||
assertNotMainThread("Client thread connect called from main thread.");
|
||||
|
||||
client_t *client = (client_t *)arg;
|
||||
assertTrue(client == &CLIENT, "Invalid client thread arg.");
|
||||
|
||||
if(client->state) return NULL;
|
||||
|
||||
switch(client->type) {
|
||||
case CLIENT_TYPE_NETWORKED:
|
||||
break;
|
||||
|
||||
case CLIENT_TYPE_SINGLE_PLAYER:
|
||||
break;
|
||||
|
||||
default:
|
||||
assertUnreachable("Invalid client type.");
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
}
|
@ -6,14 +6,20 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "network/client/networked/networkedclient.h"
|
||||
#include "network/packet/packetqueue.h"
|
||||
#include "networkclient.h"
|
||||
|
||||
/**
|
||||
* Type of connection used for a client.
|
||||
*/
|
||||
typedef enum {
|
||||
CLIENT_TYPE_NETWORKED,
|
||||
CLIENT_TYPE_SINGLE_PLAYER,
|
||||
CLIENT_TYPE_SINGLE_PLAYER = 0,
|
||||
CLIENT_TYPE_NETWORKED = 1,
|
||||
} clienttype_t;
|
||||
|
||||
/**
|
||||
* The state of the client and what it is doing.
|
||||
*/
|
||||
typedef enum {
|
||||
CLIENT_STATE_DISCONNECTED = 0,
|
||||
CLIENT_STATE_CONNECTING,
|
||||
@ -21,50 +27,91 @@ typedef enum {
|
||||
CLIENT_STATE_DISCONNECTING,
|
||||
} clientstate_t;
|
||||
|
||||
/**
|
||||
* Information used to connect to a server.
|
||||
*/
|
||||
typedef struct clientconnect_s {
|
||||
clienttype_t type;
|
||||
union {
|
||||
networkedclientconnect_t networked;
|
||||
};
|
||||
} clientconnect_t;
|
||||
|
||||
/**
|
||||
* Error codes for the client.
|
||||
*/
|
||||
typedef enum {
|
||||
CLIENT_ERROR_NO_ERROR = 0,
|
||||
CLIENT_ERROR_CONNECT_THREAD_SPAWN_FAILED,
|
||||
} clienterror_t;
|
||||
|
||||
/**
|
||||
* The clients state and information.
|
||||
*/
|
||||
typedef struct client_s {
|
||||
clientstate_t state;
|
||||
clienttype_t type;
|
||||
packetqueue_t packetQueue;
|
||||
packetqueue_t queue;
|
||||
clienterror_t error;
|
||||
|
||||
pthread_t connectThread;
|
||||
|
||||
union {
|
||||
networkedclient_t networked;
|
||||
networkclient_t network;
|
||||
};
|
||||
} client_t;
|
||||
|
||||
extern client_t CLIENT;
|
||||
|
||||
/**
|
||||
* Initializes the client.
|
||||
*
|
||||
* @return Error code indicating success or failure.
|
||||
* Initializes the client object, does not perform any connection.
|
||||
*/
|
||||
void clientInit();
|
||||
|
||||
/**
|
||||
* Connects to a server.
|
||||
* Initiates a client connection, this function will not block and will return
|
||||
* immediately even if a connection issue occurs. It is up to the caller to
|
||||
* handle the connection state and any errors that occur.
|
||||
*
|
||||
* @param connect Connection information.
|
||||
* @return Error code indicating success or failure.
|
||||
* Client must be already disconnected before calling this function.
|
||||
*
|
||||
* @param connect The connection information to use.
|
||||
*/
|
||||
errorret_t clientConnect(const clientconnect_t connect);
|
||||
void clientConnect(const clientconnect_t connect);
|
||||
|
||||
/**
|
||||
* Updates the client state.
|
||||
*/
|
||||
void clientUpdate();
|
||||
|
||||
/**
|
||||
* Disconnects the client from the server.
|
||||
* Requests the client to disconnect from the server. This will not block and
|
||||
* will return immediately. The client will handle the disconnection in the
|
||||
* background.
|
||||
*/
|
||||
void clientDisconnect();
|
||||
|
||||
/**
|
||||
* Cleans up the client resources.
|
||||
* Internal method that writes a packet to the client immediately.
|
||||
*/
|
||||
void clientDispose();
|
||||
clienterror_t clientWritePacket(const packet_t *packet);
|
||||
|
||||
/**
|
||||
* Internal method that reads a packet from the client immediately.
|
||||
*
|
||||
* @param packet The packet to read into.
|
||||
* @return Error code indicating success or failure.
|
||||
*/
|
||||
clienterror_t clientReadPacket(const packet_t *packet);
|
||||
|
||||
/**
|
||||
* Main update loop for the client, this will handle all incoming packets and
|
||||
* update the client state.
|
||||
*/
|
||||
void clientUpdate();
|
||||
|
||||
/**
|
||||
* Disposes of the client, cleans up any resources and will force disconnect if
|
||||
* not already disconnected.
|
||||
*/
|
||||
void clientDispose();
|
||||
|
||||
/**
|
||||
* Thread function responsible for initiating and handshaking the connection.
|
||||
*
|
||||
* @param arg The client connection information.
|
||||
* @return Returns NULL, unless an error occurs.
|
||||
*/
|
||||
void * clientThreadConnect(void *arg);
|
8
src/network/client/networkclient.c
Normal file
8
src/network/client/networkclient.c
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "networkclient.h"
|
24
src/network/client/networkclient.h
Normal file
24
src/network/client/networkclient.h
Normal file
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* 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 client_s client_t;
|
||||
typedef struct clientconnect_s clientconnect_t;
|
||||
|
||||
typedef struct {
|
||||
void *nothing;
|
||||
} networkclient_t;
|
||||
|
||||
/**
|
||||
* Initialize the client.
|
||||
*
|
||||
* @param client The client to initialize.
|
||||
* @param connect The connection information.
|
||||
*/
|
||||
void networkClientConnect(client_t *client, const clientconnect_t connect);
|
@ -1,10 +0,0 @@
|
||||
# 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
|
||||
networkedclient.c
|
||||
)
|
@ -1,417 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "network/client/client.h"
|
||||
#include "assert/assert.h"
|
||||
#include "console/console.h"
|
||||
|
||||
errorret_t networkedClientConnect(
|
||||
client_t *client,
|
||||
const networkedclientconnect_t connInfo
|
||||
) {
|
||||
int32_t ret;
|
||||
packet_t packet;
|
||||
errorret_t err;
|
||||
char_t *ip = "127.0.0.1";
|
||||
|
||||
// Validate the params
|
||||
assertNotNull(client, "Client is NULL");
|
||||
assertTrue(client->type == CLIENT_TYPE_NETWORKED, "Client is not networked");
|
||||
assertIsMainThread("Client connect must be on main thread");
|
||||
|
||||
// Set the state to connecting.
|
||||
client->state = CLIENT_STATE_CONNECTING;
|
||||
consolePrint("Connecting to server %s:%d", ip, connInfo.port);
|
||||
|
||||
// Create a socket
|
||||
client->networked.socket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if(client->networked.socket < 0) {
|
||||
return error(
|
||||
"Failed to create socket %s",
|
||||
errno > 0 ? strerror(errno) : "Unknown error"
|
||||
);
|
||||
}
|
||||
|
||||
// Set ip address and port
|
||||
client->networked.address.sin_family = AF_INET;
|
||||
client->networked.address.sin_port = htons(connInfo.port);
|
||||
client->networked.address.sin_addr.s_addr = inet_addr(ip);
|
||||
ret = inet_pton(AF_INET, ip, &client->networked.address.sin_addr);
|
||||
if(ret <= 0) {
|
||||
close(client->networked.socket);
|
||||
return error(
|
||||
"Invalid or bad IP address %s: %s",
|
||||
ip,
|
||||
errno > 0 ? strerror(errno) : "Unknown error"
|
||||
);
|
||||
}
|
||||
|
||||
// Connect to the server
|
||||
ret = connect(
|
||||
client->networked.socket,
|
||||
(struct sockaddr *)&client->networked.address,
|
||||
sizeof(client->networked.address)
|
||||
);
|
||||
if(ret < 0) {
|
||||
close(client->networked.socket);
|
||||
switch(errno) {
|
||||
case ECONNREFUSED:
|
||||
return error("Failed to connect: Connection refused");
|
||||
case ETIMEDOUT:
|
||||
return error("Failed to connect: Connection timed out");
|
||||
case ENETUNREACH:
|
||||
return error("Failed to connect: Network unreachable");
|
||||
default:
|
||||
return error("Failed to connect: Unknown error");
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize mutexes and locks and condition variable
|
||||
pthread_mutex_init(&client->networked.lock, NULL);
|
||||
pthread_cond_init(&client->networked.cond, NULL);
|
||||
pthread_mutex_init(&client->networked.readLock, NULL);
|
||||
pthread_mutex_init(&client->networked.writeLock, NULL);
|
||||
|
||||
// Send the version
|
||||
{
|
||||
const char_t *message = "DUSK|"DUSK_VERSION;
|
||||
ssize_t sent = send(
|
||||
client->networked.socket,
|
||||
message,
|
||||
strlen(message),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
// We should now receive a welcome packet or a disconnect packet.
|
||||
err = networkedClientReadPacket(client, &packet);
|
||||
if(err) return err;
|
||||
switch(packet.type) {
|
||||
case PACKET_TYPE_WELCOME:
|
||||
err = packetWelcomeClientProcess(&packet, client);
|
||||
if(err) return err;
|
||||
break;
|
||||
|
||||
case PACKET_TYPE_DISCONNECT:
|
||||
err = packetDisconnectClientProcess(&packet, client);
|
||||
if(err) return err;
|
||||
return error("Server disconnected");
|
||||
|
||||
default:
|
||||
return error("Server did not send welcome message.");
|
||||
}
|
||||
|
||||
// Connection was established, hand off to read thread
|
||||
ret = pthread_create(
|
||||
&client->networked.readThread,
|
||||
NULL,
|
||||
networkedClientReadThread,
|
||||
client
|
||||
);
|
||||
if(ret != 0) {
|
||||
close(client->networked.socket);
|
||||
pthread_mutex_destroy(&client->networked.readLock);
|
||||
pthread_mutex_destroy(&client->networked.writeLock);
|
||||
return error(
|
||||
"Failed to create client read thread %s",
|
||||
errno > 0 ? strerror(errno) : "Unknown error"
|
||||
);
|
||||
}
|
||||
|
||||
// Wait for the read thread to signal that it has started
|
||||
pthread_mutex_lock(&client->networked.lock);
|
||||
while (client->state == CLIENT_STATE_CONNECTING) {
|
||||
pthread_cond_wait(&client->networked.cond, &client->networked.lock);
|
||||
}
|
||||
pthread_mutex_unlock(&client->networked.lock);
|
||||
|
||||
// Start the write thread after the handshake
|
||||
ret = pthread_create(
|
||||
&client->networked.writeThread,
|
||||
NULL,
|
||||
networkedClientWriteThread, // Renamed from networkedClientRightThread
|
||||
client
|
||||
);
|
||||
if(ret != 0) {
|
||||
close(client->networked.socket);
|
||||
pthread_mutex_destroy(&client->networked.readLock);
|
||||
pthread_mutex_destroy(&client->networked.writeLock);
|
||||
return error(
|
||||
"Failed to create client write thread %s",
|
||||
errno > 0 ? strerror(errno) : "Unknown error"
|
||||
);
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
void networkedClientDisconnect(client_t *client) {
|
||||
assertNotNull(client, "Client is NULL");
|
||||
assertIsMainThread("Client disconnect must be on main thread");
|
||||
assertTrue(client->type == CLIENT_TYPE_NETWORKED, "Client is not networked");
|
||||
assertTrue(client->state == CLIENT_STATE_CONNECTED, "Client not connected");
|
||||
|
||||
pthread_mutex_lock(&client->networked.lock);
|
||||
client->state = CLIENT_STATE_DISCONNECTING;
|
||||
|
||||
struct timespec timeout;
|
||||
clock_gettime(CLOCK_REALTIME, &timeout);
|
||||
timeout.tv_sec += 1; // Wait for up to 1 second
|
||||
|
||||
while (client->state == CLIENT_STATE_DISCONNECTING) {
|
||||
if(pthread_cond_timedwait(
|
||||
&client->networked.cond,
|
||||
&client->networked.lock,
|
||||
&timeout
|
||||
) == ETIMEDOUT) {
|
||||
consolePrint("Client disconnect timed out, force closing");
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&client->networked.lock);
|
||||
|
||||
client->state = CLIENT_STATE_DISCONNECTED;
|
||||
|
||||
// Wait for the threads to finish
|
||||
if (client->networked.readThread) {
|
||||
pthread_join(client->networked.readThread, NULL);
|
||||
client->networked.readThread = 0;
|
||||
}
|
||||
|
||||
if (client->networked.writeThread) {
|
||||
pthread_join(client->networked.writeThread, NULL);
|
||||
client->networked.writeThread = 0;
|
||||
}
|
||||
|
||||
// Destroy read and write locks
|
||||
pthread_mutex_destroy(&client->networked.readLock);
|
||||
pthread_mutex_destroy(&client->networked.writeLock);
|
||||
|
||||
// Close the socket
|
||||
if (client->networked.socket) {
|
||||
shutdown(client->networked.socket, SHUT_RDWR);
|
||||
close(client->networked.socket);
|
||||
client->networked.socket = 0;
|
||||
}
|
||||
|
||||
// Destroy mutex and condition variable
|
||||
pthread_mutex_destroy(&client->networked.lock);
|
||||
pthread_cond_destroy(&client->networked.cond);
|
||||
}
|
||||
|
||||
errorret_t networkedClientWrite(
|
||||
const client_t *client,
|
||||
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");
|
||||
assertTrue(client->type == CLIENT_TYPE_NETWORKED, "Client is not networked");
|
||||
|
||||
if(client->state == CLIENT_STATE_DISCONNECTED) {
|
||||
return error("Client is disconnected");
|
||||
}
|
||||
|
||||
ssize_t sent = send(client->networked.socket, data, len, 0);
|
||||
if(sent < 0) return error("Failed to send data");
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
errorret_t networkedClientWritePacket(
|
||||
const client_t *client,
|
||||
const packet_t *packet
|
||||
) {
|
||||
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 networkedClientWrite(client, (const uint8_t *)packet, fullSize);
|
||||
}
|
||||
|
||||
errorret_t networkedClientReadPacket(
|
||||
const client_t *client,
|
||||
packet_t *packet
|
||||
) {
|
||||
uint8_t buffer[sizeof(packet_t)];
|
||||
|
||||
assertNotNull(client, "Client is NULL");
|
||||
assertNotNull(packet, "Packet is NULL");
|
||||
assertTrue(client->type == CLIENT_TYPE_NETWORKED, "Client is not networked");
|
||||
|
||||
if(client->state == CLIENT_STATE_DISCONNECTED) {
|
||||
return error("Client is disconnected");
|
||||
}
|
||||
|
||||
// Read the packet header
|
||||
ssize_t read = recv(
|
||||
client->networked.socket,
|
||||
buffer,
|
||||
sizeof(packettype_t),
|
||||
0
|
||||
);
|
||||
if(read != sizeof(packettype_t)) {
|
||||
return error(
|
||||
"Failed to read packet header %s",
|
||||
errno > 0 ? strerror(errno) : "Unknown error"
|
||||
);
|
||||
}
|
||||
|
||||
packet->type = *(packettype_t *)buffer;
|
||||
|
||||
// Read the packet length
|
||||
read = recv(
|
||||
client->networked.socket,
|
||||
buffer,
|
||||
sizeof(uint32_t),
|
||||
0
|
||||
);
|
||||
if(read != sizeof(uint32_t)) {
|
||||
return error(
|
||||
"Failed to read packet length %s",
|
||||
errno > 0 ? strerror(errno) : "Unknown error"
|
||||
);
|
||||
}
|
||||
if(read > sizeof(packetdata_t)) {
|
||||
return error("Packet length is too large");
|
||||
}
|
||||
packet->length = *(uint32_t *)buffer;
|
||||
|
||||
// Now, read the packet data
|
||||
read = recv(
|
||||
client->networked.socket,
|
||||
(uint8_t *)&packet->data,
|
||||
packet->length,
|
||||
0
|
||||
);
|
||||
if(read != packet->length) {
|
||||
return error(
|
||||
"Failed to read packet data %s",
|
||||
errno > 0 ? strerror(errno) : "Unknown error"
|
||||
);
|
||||
}
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
void * networkedClientReadThread(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,
|
||||
"Client thread argument is not networked"
|
||||
);
|
||||
assertTrue(
|
||||
client->state == CLIENT_STATE_CONNECTING,
|
||||
"Client thread argument is not connecting"
|
||||
);
|
||||
|
||||
// Notify the main thread that we are connected
|
||||
pthread_mutex_lock(&client->networked.lock);
|
||||
client->state = CLIENT_STATE_CONNECTED;
|
||||
pthread_cond_signal(&client->networked.cond);
|
||||
pthread_mutex_unlock(&client->networked.lock);
|
||||
|
||||
while(client->state == CLIENT_STATE_CONNECTED) {
|
||||
pthread_mutex_lock(&client->networked.readLock);
|
||||
|
||||
// Read a packet from the server
|
||||
errorret_t err = networkedClientReadPacket(client, &packet);
|
||||
if(err) {
|
||||
consolePrint("Failed to read packet %s", errorString());
|
||||
errorFlush();
|
||||
pthread_mutex_unlock(&client->networked.readLock);
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle disconnect packets here
|
||||
if(packet.type == PACKET_TYPE_DISCONNECT) {
|
||||
err = packetDisconnectClientProcess(&packet, client);
|
||||
if(err) {
|
||||
consolePrint(errorString());
|
||||
errorFlush();
|
||||
} else {
|
||||
consolePrint("Server disconnected");
|
||||
}
|
||||
client->state = CLIENT_STATE_DISCONNECTING;
|
||||
pthread_mutex_unlock(&client->networked.readLock);
|
||||
break;
|
||||
}
|
||||
|
||||
packetQueuePushIn(
|
||||
&client->packetQueue,
|
||||
&packet
|
||||
);
|
||||
|
||||
pthread_mutex_unlock(&client->networked.readLock);
|
||||
}
|
||||
|
||||
client->state = CLIENT_STATE_DISCONNECTED;
|
||||
|
||||
pthread_mutex_lock(&client->networked.lock);
|
||||
client->state = CLIENT_STATE_DISCONNECTED;
|
||||
pthread_cond_signal(&client->networked.cond); // Notify the main thread
|
||||
pthread_mutex_unlock(&client->networked.lock);
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
client_t *client = (client_t *)arg;
|
||||
assertTrue(
|
||||
client->type == CLIENT_TYPE_NETWORKED,
|
||||
"Write thread argument is not networked"
|
||||
);
|
||||
|
||||
while(client->state == 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 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
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include <arpa/inet.h>
|
||||
#include "network/packet/packetqueue.h"
|
||||
|
||||
typedef struct client_s client_t;
|
||||
typedef struct clientconnect_s clientconnect_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t port;
|
||||
} networkedclientconnect_t;
|
||||
|
||||
typedef struct {
|
||||
int32_t socket;
|
||||
struct sockaddr_in address;
|
||||
pthread_t readThread;
|
||||
pthread_t writeThread;
|
||||
pthread_mutex_t lock;
|
||||
pthread_cond_t cond;
|
||||
pthread_mutex_t readLock;
|
||||
pthread_mutex_t writeLock;
|
||||
} networkedclient_t;
|
||||
|
||||
/**
|
||||
* Connects to a networked server.
|
||||
*
|
||||
* @param client Pointer to the client structure.
|
||||
* @param connect Connection information.
|
||||
*/
|
||||
errorret_t networkedClientConnect(
|
||||
client_t *client,
|
||||
const networkedclientconnect_t connect
|
||||
);
|
||||
|
||||
/**
|
||||
* Closes the connection to a networked server.
|
||||
*
|
||||
* @param client Pointer to the client structure.
|
||||
*/
|
||||
void networkedClientDisconnect(client_t *client);
|
||||
|
||||
/**
|
||||
* Writes data to the networked server.
|
||||
*
|
||||
* @param client Pointer to the client structure.
|
||||
* @param data Data to write.
|
||||
* @param len Length of the data.
|
||||
* @return Error code.
|
||||
*/
|
||||
errorret_t networkedClientWrite(
|
||||
const client_t *client,
|
||||
const uint8_t *data,
|
||||
const size_t len
|
||||
);
|
||||
|
||||
/**
|
||||
* Writes a packet to the networked server.
|
||||
*
|
||||
* @param client Pointer to the client structure.
|
||||
* @param packet Pointer to the packet structure.
|
||||
* @return Error code.
|
||||
*/
|
||||
errorret_t networkedClientWritePacket(
|
||||
const client_t *client,
|
||||
const packet_t *packet
|
||||
);
|
||||
|
||||
/**
|
||||
* Reads a packet from the networked server.
|
||||
*
|
||||
* @param client Pointer to the client structure.
|
||||
* @param packet Pointer to the packet structure to read into.
|
||||
* @return Error code.
|
||||
*/
|
||||
errorret_t networkedClientReadPacket(const client_t *client, packet_t *packet);
|
||||
|
||||
/**
|
||||
* Thread function for handling networked client connections.
|
||||
*
|
||||
* @param arg Pointer to the client structure.
|
||||
* @return NULL.
|
||||
*/
|
||||
void * networkedClientReadThread(void *arg); // Renamed from networkedClientThread
|
||||
|
||||
/**
|
||||
* Thread function for handling additional client operations.
|
||||
*
|
||||
* @param arg Pointer to the client structure.
|
||||
* @return NULL.
|
||||
*/
|
||||
void * networkedClientWriteThread(void *arg); // Renamed from networkedClientRightThread
|
Reference in New Issue
Block a user