prog
This commit is contained in:
@ -8,6 +8,8 @@
|
|||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "console/console.h"
|
#include "console/console.h"
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
void* clientRemoteThreadFunc(void* arg) {
|
void* clientRemoteThreadFunc(void* arg) {
|
||||||
client_t *client = (client_t*)arg;
|
client_t *client = (client_t*)arg;
|
||||||
@ -15,10 +17,24 @@ void* clientRemoteThreadFunc(void* arg) {
|
|||||||
|
|
||||||
client->state = CLIENT_STATE_CONNECTED;
|
client->state = CLIENT_STATE_CONNECTED;
|
||||||
|
|
||||||
// Send some data
|
// Set socket to non-blocking
|
||||||
write(client->remote.clientSockDesc, "Hello, World!", 13);
|
int flags = fcntl(client->remote.clientSockDesc, F_GETFL, 0);
|
||||||
|
fcntl(client->remote.clientSockDesc, F_SETFL, flags | O_NONBLOCK);
|
||||||
printf("Thread func\n");
|
|
||||||
|
char buffer[1024];
|
||||||
|
while (client->state == CLIENT_STATE_CONNECTED) {
|
||||||
|
ssize_t bytesRead = read(client->remote.clientSockDesc, buffer, sizeof(buffer));
|
||||||
|
if (bytesRead > 0) {
|
||||||
|
// Process the received data
|
||||||
|
printf("Received data: %.*s\n", (int)bytesRead, buffer);
|
||||||
|
} else if (bytesRead == -1 && errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||||
|
// An error occurred
|
||||||
|
perror("Read error");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Sleep for a short duration to prevent busy-waiting
|
||||||
|
usleep(10000); // 10ms
|
||||||
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
@ -17,10 +17,6 @@ void serverLocalStart(const serverlocalstart_t start) {
|
|||||||
assertUnreachable("Failed to create socket");
|
assertUnreachable("Failed to create socket");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the socket to non-blocking mode
|
|
||||||
int flags = fcntl(SERVER.local.sockDesc, F_GETFL, 0);
|
|
||||||
fcntl(SERVER.local.sockDesc, F_SETFL, flags | O_NONBLOCK);
|
|
||||||
|
|
||||||
//Bind the socket
|
//Bind the socket
|
||||||
SERVER.local.sockAddr.sin_family = AF_INET;
|
SERVER.local.sockAddr.sin_family = AF_INET;
|
||||||
SERVER.local.sockAddr.sin_addr.s_addr = INADDR_ANY;
|
SERVER.local.sockAddr.sin_addr.s_addr = INADDR_ANY;
|
||||||
@ -78,6 +74,10 @@ void * serverLocalThread(void *arg) {
|
|||||||
timeout.tv_sec = 0;
|
timeout.tv_sec = 0;
|
||||||
timeout.tv_usec = SERVER_LOCAL_THREAD_ACCEPT_SLEEP_TIME;
|
timeout.tv_usec = SERVER_LOCAL_THREAD_ACCEPT_SLEEP_TIME;
|
||||||
|
|
||||||
|
// Set the socket to non-blocking mode
|
||||||
|
int flags = fcntl(SERVER.local.sockDesc, F_GETFL, 0);
|
||||||
|
fcntl(SERVER.local.sockDesc, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
|
||||||
while(SERVER.local.threadState == SERVER_LOCAL_THREAD_STATE_RUNNING) {
|
while(SERVER.local.threadState == SERVER_LOCAL_THREAD_STATE_RUNNING) {
|
||||||
FD_ZERO(&readfds);
|
FD_ZERO(&readfds);
|
||||||
FD_SET(SERVER.local.sockDesc, &readfds);
|
FD_SET(SERVER.local.sockDesc, &readfds);
|
@ -26,7 +26,6 @@ target_sources(${DUSK_TARGET_NAME}
|
|||||||
# Subdirs
|
# Subdirs
|
||||||
add_subdirectory(assert)
|
add_subdirectory(assert)
|
||||||
add_subdirectory(console)
|
add_subdirectory(console)
|
||||||
add_subdirectory(client)
|
add_subdirectory(net)
|
||||||
add_subdirectory(server)
|
|
||||||
add_subdirectory(entity)
|
add_subdirectory(entity)
|
||||||
add_subdirectory(display)
|
add_subdirectory(display)
|
@ -22,4 +22,6 @@
|
|||||||
typedef float float_t;
|
typedef float float_t;
|
||||||
typedef double double_t;
|
typedef double double_t;
|
||||||
typedef bool bool_t;
|
typedef bool bool_t;
|
||||||
typedef char char_t;
|
typedef char char_t;
|
||||||
|
|
||||||
|
#define DUSK_VERSION "1.0.0"
|
@ -9,7 +9,7 @@
|
|||||||
#include "gametime.h"
|
#include "gametime.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "console/console.h"
|
#include "console/console.h"
|
||||||
#include "server/server.h"
|
#include "net/server/server.h"
|
||||||
|
|
||||||
#include "entity/entity.h"
|
#include "entity/entity.h"
|
||||||
|
|
||||||
@ -18,11 +18,10 @@ void gameInit() {
|
|||||||
gameTimeInit();
|
gameTimeInit();
|
||||||
inputInit();
|
inputInit();
|
||||||
|
|
||||||
serverInit();
|
serverInit((serverinit_t){
|
||||||
serverStart((serverstart_t){
|
.type = SERVER_TYPE_ONLINE,
|
||||||
.type = SERVER_TYPE_LOCAL,
|
.online = {
|
||||||
.local = {
|
.port = SERVER_PORT_DEFAULT
|
||||||
.port = SERVER_LOCAL_PORT_DEFAULT
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -32,7 +31,6 @@ void gameInit() {
|
|||||||
void gameUpdate(const float delta) {
|
void gameUpdate(const float delta) {
|
||||||
gameTimeUpdate(delta);
|
gameTimeUpdate(delta);
|
||||||
inputUpdate();
|
inputUpdate();
|
||||||
serverUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void gameDispose() {
|
void gameDispose() {
|
||||||
|
12
src/dusk/net/CMakeLists.txt
Normal file
12
src/dusk/net/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Copyright (c) 2023 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https:#opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
# Sources
|
||||||
|
target_sources(${DUSK_TARGET_NAME}
|
||||||
|
PRIVATE
|
||||||
|
)
|
||||||
|
|
||||||
|
# Subdirs
|
||||||
|
add_subdirectory(server)
|
11
src/dusk/net/server/CMakeLists.txt
Normal file
11
src/dusk/net/server/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Copyright (c) 2023 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https:#opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
# Sources
|
||||||
|
target_sources(${DUSK_TARGET_NAME}
|
||||||
|
PRIVATE
|
||||||
|
server.c
|
||||||
|
serverclient.c
|
||||||
|
)
|
145
src/dusk/net/server/server.c
Normal file
145
src/dusk/net/server/server.c
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "server.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "console/console.h"
|
||||||
|
|
||||||
|
server_t SERVER;
|
||||||
|
|
||||||
|
void serverInit(const serverinit_t init) {
|
||||||
|
memset(&SERVER, 0, sizeof(server_t));
|
||||||
|
SERVER.state = SERVER_STATE_STARTING;
|
||||||
|
SERVER.type = init.type;
|
||||||
|
|
||||||
|
// Create the server
|
||||||
|
switch(init.type) {
|
||||||
|
case SERVER_TYPE_ONLINE:
|
||||||
|
SERVER.online.socket = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
SERVER.online.address.sin_family = AF_INET;
|
||||||
|
SERVER.online.address.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
SERVER.online.address.sin_port = htons(init.online.port);
|
||||||
|
|
||||||
|
// Bind
|
||||||
|
if(bind(
|
||||||
|
SERVER.online.socket,
|
||||||
|
(struct sockaddr *)&SERVER.online.address,
|
||||||
|
sizeof(SERVER.online.address)
|
||||||
|
) < 0) {
|
||||||
|
assertUnreachable("Failed to bind server.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin listening
|
||||||
|
if(listen(SERVER.online.socket, SERVER_CLIENT_COUNT_MAX) < 0) {
|
||||||
|
assertUnreachable("Failed to listen on server.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the socket to non-blocking mode
|
||||||
|
int flags = fcntl(SERVER.online.socket, F_GETFL, 0);
|
||||||
|
fcntl(SERVER.online.socket, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assertUnreachable("Invalid server type.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start thread
|
||||||
|
pthread_create(&SERVER.thread, NULL, serverThread, NULL);
|
||||||
|
|
||||||
|
// Wait for server to start
|
||||||
|
while(SERVER.state == SERVER_STATE_STARTING) usleep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void * serverThread(void *arg) {
|
||||||
|
assertNull(arg, "Server thread does not accept arguments.");
|
||||||
|
|
||||||
|
// Server now running
|
||||||
|
SERVER.state = SERVER_STATE_RUNNING;
|
||||||
|
|
||||||
|
fd_set readfds;
|
||||||
|
int32_t activity;
|
||||||
|
int32_t error;
|
||||||
|
struct timeval timeout;
|
||||||
|
timeout.tv_sec = 0;
|
||||||
|
timeout.tv_usec = 1000;
|
||||||
|
|
||||||
|
// Start server
|
||||||
|
while(SERVER.state == SERVER_STATE_RUNNING) {
|
||||||
|
FD_ZERO(&readfds);
|
||||||
|
FD_SET(SERVER.online.socket, &readfds);
|
||||||
|
|
||||||
|
activity = select(
|
||||||
|
SERVER.online.socket + 1,
|
||||||
|
&readfds,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&timeout
|
||||||
|
);
|
||||||
|
|
||||||
|
if(activity < 0) assertUnreachable("Select error");
|
||||||
|
|
||||||
|
if(!FD_ISSET(SERVER.online.socket, &readfds)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept incoming connections
|
||||||
|
int32_t clientSockDesc = accept(SERVER.online.socket, NULL, NULL);
|
||||||
|
if(clientSockDesc < 0) {
|
||||||
|
printf("Failed to accept connection\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, is the server full?
|
||||||
|
if(SERVER.clientCount >= SERVER_CLIENT_COUNT_MAX) {
|
||||||
|
consolePrint("Server is full, rejecting connection.");
|
||||||
|
// TODO: Write a reason to client.
|
||||||
|
close(clientSockDesc);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create server client for handling
|
||||||
|
serverclient_t *client = &SERVER.clients[SERVER.clientCount];
|
||||||
|
serverClientInit(client, (serverclientinit_t){
|
||||||
|
.socket = clientSockDesc
|
||||||
|
});
|
||||||
|
SERVER.clientCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
SERVER.state = SERVER_STATE_STOPPED;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void serverDispose() {
|
||||||
|
// Disconnect clients
|
||||||
|
for(uint8_t i = 0; i < SERVER.clientCount; i++) {
|
||||||
|
serverClientDispose(&SERVER.clients[i]);
|
||||||
|
}
|
||||||
|
SERVER.clientCount = 0;
|
||||||
|
|
||||||
|
// Stop server thread.
|
||||||
|
if(SERVER.state == SERVER_STATE_RUNNING) {
|
||||||
|
// Request thread to stop
|
||||||
|
SERVER.state = SERVER_STATE_STOPPING;
|
||||||
|
|
||||||
|
// Wait for server to stop
|
||||||
|
while(SERVER.state == SERVER_STATE_STOPPING) usleep(1000);
|
||||||
|
pthread_join(SERVER.thread, NULL);
|
||||||
|
|
||||||
|
// Close the socket
|
||||||
|
switch(SERVER.type) {
|
||||||
|
case SERVER_TYPE_ONLINE:
|
||||||
|
close(SERVER.online.socket);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assertUnreachable("Invalid server type.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
src/dusk/net/server/server.h
Normal file
55
src/dusk/net/server/server.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "serverclient.h"
|
||||||
|
|
||||||
|
#define SERVER_CLIENT_COUNT_MAX 8
|
||||||
|
#define SERVER_PORT_DEFAULT 24555
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SERVER_STATE_STOPPED,
|
||||||
|
SERVER_STATE_STARTING,
|
||||||
|
SERVER_STATE_RUNNING,
|
||||||
|
SERVER_STATE_STOPPING
|
||||||
|
} serverstate_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SERVER_TYPE_OFFLINE,
|
||||||
|
SERVER_TYPE_ONLINE
|
||||||
|
} servertype_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
serverstate_t state;
|
||||||
|
servertype_t type;
|
||||||
|
serverclient_t clients[SERVER_CLIENT_COUNT_MAX];
|
||||||
|
uint8_t clientCount;
|
||||||
|
pthread_t thread;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
int32_t socket;
|
||||||
|
struct sockaddr_in address;
|
||||||
|
} online;
|
||||||
|
};
|
||||||
|
} server_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
servertype_t type;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
int32_t port;
|
||||||
|
} online;
|
||||||
|
};
|
||||||
|
} serverinit_t;
|
||||||
|
|
||||||
|
extern server_t SERVER;
|
||||||
|
|
||||||
|
void serverInit(const serverinit_t init);
|
||||||
|
void * serverThread(void* arg);
|
||||||
|
void serverDispose();
|
101
src/dusk/net/server/serverclient.c
Normal file
101
src/dusk/net/server/serverclient.c
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "serverclient.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "console/console.h"
|
||||||
|
|
||||||
|
void serverClientInit(serverclient_t *client, const serverclientinit_t init) {
|
||||||
|
assertNotNull(client, "Client cannot be NULL.");
|
||||||
|
client->socket = init.socket;
|
||||||
|
consolePrint("Accepted client %i", client->socket);
|
||||||
|
|
||||||
|
client->state = SERVER_CLIENT_STATE_CONNECTING;
|
||||||
|
|
||||||
|
// Set socket to non-blocking
|
||||||
|
int flags = fcntl(client->socket, F_GETFL, 0);
|
||||||
|
fcntl(client->socket, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
|
||||||
|
// Start thread
|
||||||
|
pthread_create(&client->thread, NULL, serverClientThread, client);
|
||||||
|
|
||||||
|
// Wait for client to connect
|
||||||
|
while(client->state == SERVER_CLIENT_STATE_CONNECTING) usleep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void * serverClientThread(void *arg) {
|
||||||
|
serverclient_t *client = (serverclient_t*)arg;
|
||||||
|
assertNotNull(client, "Client cannot be NULL.");
|
||||||
|
|
||||||
|
// Set the client state to connected
|
||||||
|
client->state = SERVER_CLIENT_STATE_CONNECTED;
|
||||||
|
|
||||||
|
// Send hello to client, as well as the version number.
|
||||||
|
{
|
||||||
|
uint8_t hello[16];
|
||||||
|
memset(hello, 0, sizeof(hello));
|
||||||
|
memcpy(hello, "Dusk", 4);
|
||||||
|
strcpy((char*)&hello[4], DUSK_VERSION);
|
||||||
|
|
||||||
|
_serverClientSend(client, hello, sizeof(hello));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client should respond "OK"
|
||||||
|
{
|
||||||
|
uint8_t response[2];
|
||||||
|
_serverClientReceive(client, response, sizeof(response));
|
||||||
|
assertTrue(
|
||||||
|
response[0] == 'O' &&
|
||||||
|
response[1] == 'K',
|
||||||
|
"Client did not respond with OK."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
while(client->state == SERVER_CLIENT_STATE_CONNECTED) {
|
||||||
|
// Do nothing for now.
|
||||||
|
usleep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disconnect
|
||||||
|
client->state = SERVER_CLIENT_STATE_DISCONNECTED;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _serverClientSend(
|
||||||
|
serverclient_t *client,
|
||||||
|
const uint8_t *data,
|
||||||
|
const size_t size
|
||||||
|
) {
|
||||||
|
assertNotNull(client, "Client cannot be NULL.");
|
||||||
|
assertNotNull(data, "Data cannot be NULL.");
|
||||||
|
assertTrue(size > 0, "Size cannot be zero.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void _serverClientReceive(
|
||||||
|
serverclient_t *client,
|
||||||
|
uint8_t *data,
|
||||||
|
const size_t size
|
||||||
|
) {
|
||||||
|
assertNotNull(client, "Client cannot be NULL.");
|
||||||
|
assertNotNull(data, "Data cannot be NULL.");
|
||||||
|
assertTrue(size > 0, "Size cannot be zero.");
|
||||||
|
|
||||||
|
recv(client->socket, data, size, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void serverClientDispose(serverclient_t *client) {
|
||||||
|
assertNotNull(client, "Client cannot be NULL.");
|
||||||
|
|
||||||
|
if(client->state == SERVER_CLIENT_STATE_CONNECTED) {
|
||||||
|
// Let the thread know we are disconnecting
|
||||||
|
client->state = SERVER_CLIENT_STATE_DISCONNECTING;
|
||||||
|
|
||||||
|
// Wait for the thread to finish
|
||||||
|
while(client->state == SERVER_CLIENT_STATE_DISCONNECTING) usleep(1000);
|
||||||
|
pthread_join(client->thread, NULL);
|
||||||
|
}
|
||||||
|
}
|
45
src/dusk/net/server/serverclient.h
Normal file
45
src/dusk/net/server/serverclient.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SERVER_CLIENT_STATE_DISCONNECTED,
|
||||||
|
SERVER_CLIENT_STATE_CONNECTING,
|
||||||
|
SERVER_CLIENT_STATE_CONNECTED,
|
||||||
|
SERVER_CLIENT_STATE_DISCONNECTING
|
||||||
|
} serverclientstate_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
serverclientstate_t state;
|
||||||
|
pthread_t thread;
|
||||||
|
int32_t socket;
|
||||||
|
} serverclient_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int32_t socket;
|
||||||
|
} serverclientinit_t;
|
||||||
|
|
||||||
|
void serverClientInit(serverclient_t *client, const serverclientinit_t init);
|
||||||
|
void * serverClientThread(void *arg);
|
||||||
|
|
||||||
|
void _serverClientSend(
|
||||||
|
serverclient_t *client,
|
||||||
|
const uint8_t *data,
|
||||||
|
const size_t size
|
||||||
|
);
|
||||||
|
|
||||||
|
void _serverClientReceive(
|
||||||
|
serverclient_t *client,
|
||||||
|
uint8_t *buffer,
|
||||||
|
const size_t size
|
||||||
|
);
|
||||||
|
|
||||||
|
void serverClientDispose(serverclient_t *client);
|
Reference in New Issue
Block a user