ref
This commit is contained in:
@ -1,13 +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
|
|
||||||
client.c
|
|
||||||
clientremote.c
|
|
||||||
)
|
|
||||||
|
|
||||||
# Subdirs
|
|
@ -1,71 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "client.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
void clientInit(
|
|
||||||
client_t *client,
|
|
||||||
const clientinit_t init
|
|
||||||
) {
|
|
||||||
assertNotNull(client, "Client cannot be NULL.");
|
|
||||||
memset(client, 0, sizeof(client_t));
|
|
||||||
|
|
||||||
client->type = init.type;
|
|
||||||
client->state = CLIENT_STATE_CONNECTING;
|
|
||||||
|
|
||||||
// Perform connection
|
|
||||||
switch(init.type) {
|
|
||||||
case CLIENT_TYPE_REMOTE:
|
|
||||||
clientRemoteInit(client, init.remote);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assertUnreachable("Invalid client type.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Begin exchanging.
|
|
||||||
// clientSend(client, (packet_t){
|
|
||||||
// .type = PACKET_TYPE_HELLO
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
|
|
||||||
void clientSend(
|
|
||||||
client_t *client,
|
|
||||||
const packet_t packet
|
|
||||||
) {
|
|
||||||
assertNotNull(client, "Client cannot be NULL.");
|
|
||||||
assertTrue(
|
|
||||||
client->state == CLIENT_STATE_CONNECTED,
|
|
||||||
"Client must be connected to send packets."
|
|
||||||
);
|
|
||||||
|
|
||||||
switch(client->type) {
|
|
||||||
case CLIENT_TYPE_REMOTE:
|
|
||||||
clientRemoteSend(client, packet);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assertUnreachable("Invalid client type.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void clientDispose(client_t *client) {
|
|
||||||
assertNotNull(client, "Client cannot be NULL.");
|
|
||||||
client->state = CLIENT_STATE_DISCONNECTING;
|
|
||||||
|
|
||||||
switch(client->type) {
|
|
||||||
case CLIENT_TYPE_REMOTE:
|
|
||||||
clientRemoteDispose(client);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assertUnreachable("Invalid client type.");
|
|
||||||
}
|
|
||||||
|
|
||||||
client->state = CLIENT_STATE_DISCONNECTED;
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "clientremote.h"
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
CLIENT_TYPE_SINGLE_PLAYER,
|
|
||||||
CLIENT_TYPE_LOCAL,
|
|
||||||
CLIENT_TYPE_REMOTE
|
|
||||||
} clienttype_t;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
CLIENT_STATE_DISCONNECTED,
|
|
||||||
CLIENT_STATE_DISCONNECTING,
|
|
||||||
CLIENT_STATE_CONNECTING,
|
|
||||||
CLIENT_STATE_CONNECTED
|
|
||||||
} clientstate_t;
|
|
||||||
|
|
||||||
typedef struct _client_t {
|
|
||||||
clienttype_t type;
|
|
||||||
clientstate_t state;
|
|
||||||
|
|
||||||
union {
|
|
||||||
clientremote_t remote;
|
|
||||||
};
|
|
||||||
} client_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
clienttype_t type;
|
|
||||||
|
|
||||||
union {
|
|
||||||
clientremoteinit_t remote;
|
|
||||||
};
|
|
||||||
} clientinit_t;
|
|
||||||
|
|
||||||
void clientInit(
|
|
||||||
client_t *client,
|
|
||||||
const clientinit_t init
|
|
||||||
);
|
|
||||||
|
|
||||||
void clientSend(
|
|
||||||
client_t *client,
|
|
||||||
const packet_t packet
|
|
||||||
);
|
|
||||||
|
|
||||||
void clientDispose(client_t *client);
|
|
@ -1,61 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "client.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "console/console.h"
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
void* clientRemoteThreadFunc(void* arg) {
|
|
||||||
client_t *client = (client_t*)arg;
|
|
||||||
assertNotNull(client, "Client is NULL");
|
|
||||||
|
|
||||||
client->state = CLIENT_STATE_CONNECTED;
|
|
||||||
|
|
||||||
// Set socket to non-blocking
|
|
||||||
int flags = fcntl(client->remote.clientSockDesc, F_GETFL, 0);
|
|
||||||
fcntl(client->remote.clientSockDesc, F_SETFL, flags | O_NONBLOCK);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void clientRemoteInit(
|
|
||||||
client_t *client,
|
|
||||||
const clientremoteinit_t init
|
|
||||||
) {
|
|
||||||
client->remote.clientSockDesc = init.clientSockDesc;
|
|
||||||
consolePrint("Accepted a new connection.");
|
|
||||||
|
|
||||||
pthread_create(&client->remote.thread, NULL, clientRemoteThreadFunc, client);
|
|
||||||
}
|
|
||||||
|
|
||||||
void clientRemoteSend(client_t *client, const packet_t packet) {
|
|
||||||
printf("send pack\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void clientRemoteDispose(client_t *client) {
|
|
||||||
pthread_join(client->remote.thread, NULL);
|
|
||||||
|
|
||||||
close(client->remote.clientSockDesc);
|
|
||||||
consolePrint("Client disconnected.");
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
#include "net/packet.h"
|
|
||||||
|
|
||||||
typedef struct _client_t client_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int32_t clientSockDesc;
|
|
||||||
} clientremoteinit_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int32_t clientSockDesc;
|
|
||||||
pthread_t thread;
|
|
||||||
} clientremote_t;
|
|
||||||
|
|
||||||
void clientRemoteInit(client_t *client, const clientremoteinit_t init);
|
|
||||||
void clientRemoteSend(client_t *client, const packet_t packet);
|
|
||||||
void * clientRemoteThread(void *arg);
|
|
||||||
void clientRemoteDispose(client_t *client);
|
|
@ -1,14 +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
|
|
||||||
server.c
|
|
||||||
serverlocal.c
|
|
||||||
serversingleplayer.c
|
|
||||||
)
|
|
||||||
|
|
||||||
# Subdirs
|
|
@ -1,77 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "server.h"
|
|
||||||
#include "console/console.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
server_t SERVER;
|
|
||||||
|
|
||||||
void serverInit() {
|
|
||||||
memset(&SERVER, 0, sizeof(server_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
void serverStart(const serverstart_t start) {
|
|
||||||
assertTrue(SERVER.state==SERVER_STATE_STOPPED, "Server is already running.");
|
|
||||||
|
|
||||||
SERVER.type = start.type;
|
|
||||||
SERVER.clientCount = 0;
|
|
||||||
|
|
||||||
switch(start.type) {
|
|
||||||
case SERVER_TYPE_SINGLE_PLAYER:
|
|
||||||
serverSinglePlayerStart(start.singlePlayer);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SERVER_TYPE_LOCAL:
|
|
||||||
serverLocalStart(start.local);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SERVER_TYPE_REMOTE:
|
|
||||||
assertUnreachable("Remote server not implemented.");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assertUnreachable("Unknown server type.");
|
|
||||||
}
|
|
||||||
|
|
||||||
SERVER.state = SERVER_STATE_RUNNING;
|
|
||||||
}
|
|
||||||
|
|
||||||
void serverStop() {
|
|
||||||
assertTrue(SERVER.state == SERVER_STATE_RUNNING,"Server is already stopped.");
|
|
||||||
|
|
||||||
for(uint8_t i = 0; i < SERVER.clientCount; i++) {
|
|
||||||
clientDispose(&SERVER.clients[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(SERVER.type) {
|
|
||||||
case SERVER_TYPE_SINGLE_PLAYER:
|
|
||||||
assertUnreachable("Single player server not implemented.");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SERVER_TYPE_LOCAL:
|
|
||||||
serverLocalStop();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SERVER_TYPE_REMOTE:
|
|
||||||
assertUnreachable("Remote server not implemented.");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assertUnreachable("Unknown server type.");
|
|
||||||
}
|
|
||||||
|
|
||||||
SERVER.state = SERVER_STATE_STOPPED;
|
|
||||||
}
|
|
||||||
|
|
||||||
void serverUpdate() {
|
|
||||||
if(SERVER.state == SERVER_STATE_STOPPED) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void serverDispose() {
|
|
||||||
if(SERVER.state == SERVER_STATE_RUNNING) serverStop();
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "client/client.h"
|
|
||||||
#include "serversingleplayer.h"
|
|
||||||
#include "serverlocal.h"
|
|
||||||
|
|
||||||
#define SERVER_CLIENT_COUNT_MAX 4
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
SERVER_TYPE_SINGLE_PLAYER = 0,
|
|
||||||
SERVER_TYPE_LOCAL = 1,
|
|
||||||
SERVER_TYPE_REMOTE = 2
|
|
||||||
} servertype_t;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
SERVER_STATE_STOPPED = 0,
|
|
||||||
SERVER_STATE_RUNNING = 1
|
|
||||||
} serverstate_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
servertype_t type;
|
|
||||||
|
|
||||||
union {
|
|
||||||
serversingleplayerstart_t singlePlayer;
|
|
||||||
serverlocalstart_t local;
|
|
||||||
};
|
|
||||||
} serverstart_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
serverstate_t state;
|
|
||||||
servertype_t type;
|
|
||||||
|
|
||||||
client_t clients[SERVER_CLIENT_COUNT_MAX];
|
|
||||||
uint8_t clientCount;
|
|
||||||
|
|
||||||
union {
|
|
||||||
serverlocal_t local;
|
|
||||||
};
|
|
||||||
} server_t;
|
|
||||||
|
|
||||||
extern server_t SERVER;
|
|
||||||
|
|
||||||
void serverInit();
|
|
||||||
void serverStart(const serverstart_t start);
|
|
||||||
void serverStop();
|
|
||||||
void serverUpdate();
|
|
||||||
void serverDispose();
|
|
@ -1,127 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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"
|
|
||||||
#include <fcntl.h>
|
|
||||||
|
|
||||||
void serverLocalStart(const serverlocalstart_t start) {
|
|
||||||
//Create the socket
|
|
||||||
SERVER.local.sockDesc = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if(SERVER.local.sockDesc < 0) {
|
|
||||||
assertUnreachable("Failed to create socket");
|
|
||||||
}
|
|
||||||
|
|
||||||
//Bind the socket
|
|
||||||
SERVER.local.sockAddr.sin_family = AF_INET;
|
|
||||||
SERVER.local.sockAddr.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
SERVER.local.sockAddr.sin_port = htons(start.port);
|
|
||||||
|
|
||||||
if(bind(
|
|
||||||
SERVER.local.sockDesc,
|
|
||||||
(struct sockaddr*)&SERVER.local.sockAddr,
|
|
||||||
sizeof(SERVER.local.sockAddr)
|
|
||||||
) < 0) {
|
|
||||||
assertUnreachable("Failed to bind socket");
|
|
||||||
}
|
|
||||||
|
|
||||||
//Listen on the socket
|
|
||||||
if(listen(SERVER.local.sockDesc, 5) < 0) {
|
|
||||||
assertUnreachable("Failed to listen on socket");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Begin the server thread.
|
|
||||||
consolePrint("Server started.");
|
|
||||||
|
|
||||||
// Start the server thread
|
|
||||||
SERVER.local.threadState = SERVER_LOCAL_THREAD_STATE_STARTING;
|
|
||||||
pthread_create(&SERVER.local.thread, NULL, serverLocalThread, NULL);
|
|
||||||
|
|
||||||
while(SERVER.local.threadState == SERVER_LOCAL_THREAD_STATE_STARTING) {
|
|
||||||
usleep(SERVER_LOCAL_THREAD_SLEEP_TIME);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void serverLocalStop() {
|
|
||||||
SERVER.local.threadState = SERVER_LOCAL_THREAD_STATE_STOPPING;
|
|
||||||
while(SERVER.local.threadState == SERVER_LOCAL_THREAD_STATE_STOPPING) {
|
|
||||||
usleep(SERVER_LOCAL_THREAD_SLEEP_TIME);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop the server thread.
|
|
||||||
pthread_cancel(SERVER.local.thread);
|
|
||||||
|
|
||||||
//Close the socket
|
|
||||||
close(SERVER.local.sockDesc);
|
|
||||||
|
|
||||||
// End the server thread.
|
|
||||||
consolePrint("Server stopped.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void * serverLocalThread(void *arg) {
|
|
||||||
SERVER.local.threadState = SERVER_LOCAL_THREAD_STATE_RUNNING;
|
|
||||||
|
|
||||||
fd_set readfds;
|
|
||||||
int32_t activity;
|
|
||||||
int32_t error;
|
|
||||||
|
|
||||||
struct timeval timeout;
|
|
||||||
timeout.tv_sec = 0;
|
|
||||||
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) {
|
|
||||||
FD_ZERO(&readfds);
|
|
||||||
FD_SET(SERVER.local.sockDesc, &readfds);
|
|
||||||
|
|
||||||
activity = select(
|
|
||||||
SERVER.local.sockDesc + 1,
|
|
||||||
&readfds,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
&timeout
|
|
||||||
);
|
|
||||||
|
|
||||||
if(activity < 0) assertUnreachable("Select error");
|
|
||||||
|
|
||||||
if(!FD_ISSET(SERVER.local.sockDesc, &readfds)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept incoming connections
|
|
||||||
int32_t clientSockDesc = accept(SERVER.local.sockDesc, 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 client.
|
|
||||||
client_t *client = &SERVER.clients[SERVER.clientCount];
|
|
||||||
clientInit(client, (clientinit_t){
|
|
||||||
.type = CLIENT_TYPE_REMOTE,
|
|
||||||
.remote = {
|
|
||||||
.clientSockDesc = clientSockDesc
|
|
||||||
}
|
|
||||||
});
|
|
||||||
SERVER.clientCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
SERVER.local.threadState = SERVER_LOCAL_THREAD_STATE_STOPPED;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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>
|
|
||||||
|
|
||||||
#define SERVER_LOCAL_PORT_DEFAULT 8080
|
|
||||||
#define SERVER_LOCAL_THREAD_SLEEP_TIME 1000
|
|
||||||
#define SERVER_LOCAL_THREAD_ACCEPT_SLEEP_TIME 60000
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
SERVER_LOCAL_THREAD_STATE_STOPPED = 0,
|
|
||||||
SERVER_LOCAL_THREAD_STATE_STARTING = 1,
|
|
||||||
SERVER_LOCAL_THREAD_STATE_RUNNING = 2,
|
|
||||||
SERVER_LOCAL_THREAD_STATE_STOPPING = 3
|
|
||||||
} serverlocalthreadstate_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint32_t port;
|
|
||||||
} serverlocalstart_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int32_t sockDesc;
|
|
||||||
struct sockaddr_in sockAddr;
|
|
||||||
pthread_t thread;
|
|
||||||
uint8_t threadState;
|
|
||||||
} serverlocal_t;
|
|
||||||
|
|
||||||
void serverLocalStart(const serverlocalstart_t start);
|
|
||||||
void serverLocalStop();
|
|
||||||
void * serverLocalThread(void *arg);
|
|
@ -1,12 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "server.h"
|
|
||||||
|
|
||||||
void serverSinglePlayerStart(const serversingleplayerstart_t start) {
|
|
||||||
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 {
|
|
||||||
int32_t nothing;
|
|
||||||
} serversingleplayerstart_t;
|
|
||||||
|
|
||||||
void serverSinglePlayerStart(const serversingleplayerstart_t start);
|
|
@ -19,13 +19,10 @@ target_include_directories(${DUSK_TARGET_NAME}
|
|||||||
target_sources(${DUSK_TARGET_NAME}
|
target_sources(${DUSK_TARGET_NAME}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
game.c
|
game.c
|
||||||
input.c
|
|
||||||
gametime.c
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Subdirs
|
# Subdirs
|
||||||
add_subdirectory(assert)
|
add_subdirectory(assert)
|
||||||
add_subdirectory(console)
|
add_subdirectory(console)
|
||||||
add_subdirectory(net)
|
|
||||||
add_subdirectory(entity)
|
add_subdirectory(entity)
|
||||||
add_subdirectory(display)
|
add_subdirectory(display)
|
@ -1,12 +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
|
|
||||||
console.c
|
|
||||||
)
|
|
||||||
|
|
||||||
# Subdirs
|
|
@ -1,59 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "console.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
console_t CONSOLE;
|
|
||||||
|
|
||||||
void consoleInit() {
|
|
||||||
memset(&CONSOLE, 0, sizeof(console_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
const consolecmd_t * consoleCommand(const char_t *cmd) {
|
|
||||||
assertTrue(
|
|
||||||
CONSOLE.commandCount < CONSOLE_COMMAND_COUNT_MAX,
|
|
||||||
"Command buffer full."
|
|
||||||
);
|
|
||||||
assertStrLen(cmd, CONSOLE_CMD_LENGTH_MAX, "Command too long.");
|
|
||||||
|
|
||||||
consolecmd_t *command = &CONSOLE.commands[CONSOLE.commandCount++];
|
|
||||||
strcpy(command->cmd, cmd);
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
|
|
||||||
void consolePrint(const char_t *fmt, ...) {
|
|
||||||
// Do we need to shift the log buffer?
|
|
||||||
if (CONSOLE.logCount == CONSOLE_LOG_COUNT_MAX) {
|
|
||||||
memcpy(
|
|
||||||
CONSOLE.log[0],
|
|
||||||
CONSOLE.log[1],
|
|
||||||
sizeof(char_t) *
|
|
||||||
CONSOLE_LOG_MESSAGE_LENGTH_MAX *
|
|
||||||
(CONSOLE_LOG_COUNT_MAX - 1)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print the message
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
|
|
||||||
// buffer but don't let it overflow the string length.
|
|
||||||
int ret = vsnprintf(
|
|
||||||
CONSOLE.log[CONSOLE.logCount++],
|
|
||||||
CONSOLE_LOG_MESSAGE_LENGTH_MAX,
|
|
||||||
fmt,
|
|
||||||
args
|
|
||||||
);
|
|
||||||
|
|
||||||
assertTrue(ret >= 0, "Log message error.");
|
|
||||||
assertTrue(ret < CONSOLE_LOG_MESSAGE_LENGTH_MAX, "Log message overflow.");
|
|
||||||
|
|
||||||
printf("%s\n", CONSOLE.log[CONSOLE.logCount - 1]);
|
|
||||||
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "consolecmd.h"
|
|
||||||
|
|
||||||
#define CONSOLE_COMMAND_COUNT_MAX 64
|
|
||||||
#define CONSOLE_LOG_MESSAGE_LENGTH_MAX 1024
|
|
||||||
#define CONSOLE_LOG_COUNT_MAX 256
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
consolecmd_t commands[CONSOLE_COMMAND_COUNT_MAX];
|
|
||||||
uint8_t commandCount;
|
|
||||||
|
|
||||||
char_t log[CONSOLE_LOG_COUNT_MAX][CONSOLE_LOG_MESSAGE_LENGTH_MAX + 1];
|
|
||||||
uint8_t logCount;
|
|
||||||
} console_t;
|
|
||||||
|
|
||||||
extern console_t CONSOLE;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the console.
|
|
||||||
*/
|
|
||||||
void consoleInit();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run a command against the console.
|
|
||||||
*
|
|
||||||
* @param cmd The command string to run.
|
|
||||||
*/
|
|
||||||
const consolecmd_t * consoleCommand(const char_t *cmd);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints a message to the console.
|
|
||||||
*
|
|
||||||
* @param fmt The format string.
|
|
||||||
* @param ... The format arguments.
|
|
||||||
*/
|
|
||||||
void consolePrint(const char_t *fmt, ...);
|
|
@ -1,15 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
#define CONSOLE_CMD_LENGTH_MAX 256
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char_t cmd[CONSOLE_CMD_LENGTH_MAX + 1];
|
|
||||||
} consolecmd_t;
|
|
@ -6,58 +6,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "game.h"
|
#include "game.h"
|
||||||
#include "gametime.h"
|
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "console/console.h"
|
|
||||||
#include "net/server/server.h"
|
|
||||||
#include "net/client/client.h"
|
|
||||||
|
|
||||||
int32_t status = 0;
|
|
||||||
|
|
||||||
void gameInit() {
|
void gameInit() {
|
||||||
consoleInit();
|
|
||||||
gameTimeInit();
|
|
||||||
inputInit();
|
inputInit();
|
||||||
|
|
||||||
consolePrint("Press LEFT to start server, RIGHT to join server.");
|
|
||||||
|
|
||||||
memset(&SERVER, 0, sizeof(server_t));
|
|
||||||
memset(&CLIENT, 0, sizeof(client_t));
|
|
||||||
|
|
||||||
// serverInit((serverinit_t){
|
|
||||||
// .type = SERVER_TYPE_ONLINE,
|
|
||||||
// .online = {
|
|
||||||
// .port = SERVER_PORT_DEFAULT
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void gameUpdate(const float delta) {
|
void gameUpdate() {
|
||||||
gameTimeUpdate(delta);
|
|
||||||
inputUpdate();
|
inputUpdate();
|
||||||
|
|
||||||
switch(status) {
|
|
||||||
case 0:
|
|
||||||
if(inputWasPressed(INPUT_LEFT)) {
|
|
||||||
|
|
||||||
serverInit((serverinit_t){
|
|
||||||
.type = SERVER_TYPE_ONLINE,
|
|
||||||
.online = {
|
|
||||||
.port = SERVER_PORT_DEFAULT
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if(inputWasPressed(INPUT_RIGHT)) {
|
|
||||||
|
|
||||||
clientInit((clientinit_t){
|
|
||||||
.host = "127.0.0.1",
|
|
||||||
.port = SERVER_PORT_DEFAULT
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void gameDispose() {
|
void gameDispose() {
|
||||||
clientDispose();
|
|
||||||
serverDispose();
|
|
||||||
}
|
}
|
@ -14,11 +14,12 @@
|
|||||||
void gameInit();
|
void gameInit();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the game.
|
* Updates the game. The game is setup to handle "frames", aka 1/60th of a
|
||||||
*
|
* second. Typically more modern engines would use floats to tick game engine
|
||||||
* @param delta Game time update delta since last tick.
|
* by a semi random delta time, but this engine is intended to work on really
|
||||||
|
* weak machines so we take the simple approach.
|
||||||
*/
|
*/
|
||||||
void gameUpdate(const float_t delta);
|
void gameUpdate();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disposes the game.
|
* Disposes the game.
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "gametime.h"
|
|
||||||
|
|
||||||
gametime_t GAME_TIME;
|
|
||||||
|
|
||||||
void gameTimeInit() {
|
|
||||||
memset(&GAME_TIME, 0, sizeof(gametime_t));
|
|
||||||
GAME_TIME.delta = GAME_TIME_STEP_DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
void gameTimeUpdate(const float_t delta) {
|
|
||||||
GAME_TIME.delta = delta;
|
|
||||||
GAME_TIME.time += GAME_TIME.delta;
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 {
|
|
||||||
float_t delta;
|
|
||||||
float_t time;
|
|
||||||
} gametime_t;
|
|
||||||
|
|
||||||
extern gametime_t GAME_TIME;
|
|
||||||
|
|
||||||
#define GAME_TIME_STEP_DEFAULT 0.016f
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the game time.
|
|
||||||
*/
|
|
||||||
void gameTimeInit();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the game time.
|
|
||||||
*
|
|
||||||
* @param delta The time since the last frame.
|
|
||||||
*/
|
|
||||||
void gameTimeUpdate(const float_t delta);
|
|
@ -1,46 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "input.h"
|
|
||||||
|
|
||||||
inputstate_t INPUT;
|
|
||||||
|
|
||||||
void inputInit() {
|
|
||||||
memset(&INPUT, 0, sizeof(inputstate_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
void inputUpdate() {
|
|
||||||
memcpy(INPUT.previous, INPUT.current, sizeof(inputvalue_t) * INPUT_BIND_COUNT);
|
|
||||||
|
|
||||||
for(int i = 0; i < INPUT_BIND_COUNT; i++) {
|
|
||||||
INPUT.current[i] = inputStateGet(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inputvalue_t inputGet(inputbind_t bind) {
|
|
||||||
return INPUT.current[bind];
|
|
||||||
}
|
|
||||||
|
|
||||||
inputvalue_t inputGetPrevious(inputbind_t bind) {
|
|
||||||
return INPUT.previous[bind];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool_t inputWasPressed(inputbind_t bind) {
|
|
||||||
return inputGet(bind) != 0.0f && inputGetPrevious(bind) == 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool_t inputWasReleased(inputbind_t bind) {
|
|
||||||
return inputGet(bind) == 0.0f && inputGetPrevious(bind) != 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool_t inputIsDown(inputbind_t bind) {
|
|
||||||
return inputGet(bind) != 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool_t inputIsUp(inputbind_t bind) {
|
|
||||||
return inputGet(bind) == 0.0f;
|
|
||||||
}
|
|
@ -6,91 +6,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
typedef float_t inputvalue_t;
|
typedef uint16_t inputstate_t;
|
||||||
|
|
||||||
typedef enum {
|
extern inputstate_t INPUT_CURRENT;
|
||||||
INPUT_UP,
|
extern inputstate_t INPUT_LAST_FRAME;
|
||||||
INPUT_DOWN,
|
|
||||||
INPUT_LEFT,
|
|
||||||
INPUT_RIGHT,
|
|
||||||
INPUT_ACCEPT,
|
|
||||||
INPUT_BACK,
|
|
||||||
INPUT_QUIT,
|
|
||||||
} inputbind_t;
|
|
||||||
|
|
||||||
#define INPUT_BIND_COUNT INPUT_QUIT + 1
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
inputvalue_t current[INPUT_BIND_COUNT];
|
|
||||||
inputvalue_t previous[INPUT_BIND_COUNT];
|
|
||||||
} inputstate_t;
|
|
||||||
|
|
||||||
extern inputstate_t INPUT;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the input system.
|
* Initializes the input system
|
||||||
*/
|
*/
|
||||||
void inputInit();
|
void inputInit();
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the input system.
|
|
||||||
*/
|
|
||||||
void inputUpdate();
|
void inputUpdate();
|
||||||
|
|
||||||
/**
|
|
||||||
* Calls on the platform to get the current state of a given input bind.
|
|
||||||
*
|
|
||||||
* @param input Input to check.
|
|
||||||
* @return Current state of the input.
|
|
||||||
*/
|
|
||||||
inputvalue_t inputStateGet(const inputbind_t input);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current value of the input bind.
|
|
||||||
*
|
|
||||||
* @param input Input to get.
|
|
||||||
* @return Current value of the input.
|
|
||||||
*/
|
|
||||||
inputvalue_t inputGet(const inputbind_t input);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the previous value of the input bind.
|
|
||||||
*
|
|
||||||
* @param input Input to get.
|
|
||||||
* @return Previous value of the input.
|
|
||||||
*/
|
|
||||||
inputvalue_t inputGetPrevious(const inputbind_t input);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the input was pressed this frame.
|
|
||||||
*
|
|
||||||
* @param input Input to check.
|
|
||||||
* @return Whether the input was pressed this frame.
|
|
||||||
*/
|
|
||||||
bool_t inputWasPressed(inputbind_t input);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the input was released this frame.
|
|
||||||
*
|
|
||||||
* @param input Input to check.
|
|
||||||
* @return Whether the input was released this frame.
|
|
||||||
*/
|
|
||||||
bool_t inputWasReleased(inputbind_t input);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the input is currently down.
|
|
||||||
*
|
|
||||||
* @param input Input to check.
|
|
||||||
* @return Whether the input is currently down.
|
|
||||||
*/
|
|
||||||
bool_t inputIsDown(inputbind_t input);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the input is currently up.
|
|
||||||
*
|
|
||||||
* @param input Input to check.
|
|
||||||
* @return Whether the input is currently up.
|
|
||||||
*/
|
|
||||||
bool_t inputIsUp(inputbind_t input);
|
|
@ -1,13 +0,0 @@
|
|||||||
# 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(client)
|
|
||||||
add_subdirectory(server)
|
|
@ -1,10 +0,0 @@
|
|||||||
# 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
|
|
||||||
client.c
|
|
||||||
)
|
|
@ -1,119 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "client.h"
|
|
||||||
#include "console/console.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
client_t CLIENT;
|
|
||||||
|
|
||||||
void clientInit(const clientinit_t init) {
|
|
||||||
memset(&CLIENT, 0, sizeof(client_t));
|
|
||||||
CLIENT.state = CLIENT_STATE_CONNECTING;
|
|
||||||
|
|
||||||
CLIENT.socket = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
CLIENT.serverAddress.sin_family = AF_INET;
|
|
||||||
CLIENT.serverAddress.sin_port = htons(init.port);
|
|
||||||
|
|
||||||
if(inet_pton(AF_INET, init.host, &CLIENT.serverAddress.sin_addr) <= 0) {
|
|
||||||
assertUnreachable("Invalid address or address not supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(connect(CLIENT.socket, (struct sockaddr *)&CLIENT.serverAddress, sizeof(CLIENT.serverAddress)) < 0) {
|
|
||||||
assertUnreachable("Connection failed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_create(&CLIENT.thread, NULL, clientThread, NULL);
|
|
||||||
|
|
||||||
// Wait for client to connect
|
|
||||||
while(CLIENT.state == CLIENT_STATE_CONNECTING) usleep(1000);
|
|
||||||
|
|
||||||
consolePrint("Connected to server.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void * clientThread(void* arg) {
|
|
||||||
assertNull(arg, "Client thread does not accept arguments.");
|
|
||||||
|
|
||||||
|
|
||||||
// Server sends 16 bytes.
|
|
||||||
{
|
|
||||||
char_t buffer[16];
|
|
||||||
consolePrint("Reading first packet.");
|
|
||||||
if(_clientReceive((uint8_t*)buffer, sizeof(buffer)) != sizeof(buffer)) {
|
|
||||||
assertUnreachable("Failed to read initial server data.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// First 4 bytes must be "Dusk"
|
|
||||||
if(memcmp(buffer, "Dusk", 4) != 0) {
|
|
||||||
assertUnreachable("Invalid server data.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next, server will send its version number, we compare to DUSK_VERSION
|
|
||||||
char_t cmp[] = DUSK_VERSION;
|
|
||||||
if(memcmp(buffer+4, cmp, 4) != 0) {
|
|
||||||
assertUnreachable("Server version mismatch.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send "OK" and nothing else (don't send NULL term)
|
|
||||||
if(_clientSend((uint8_t*)"OK", 2) != 2) {
|
|
||||||
assertUnreachable("Failed to send OK to server.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server will send the first properly formed packet now.
|
|
||||||
|
|
||||||
CLIENT.state = CLIENT_STATE_CONNECTED;
|
|
||||||
while(CLIENT.state == CLIENT_STATE_CONNECTED) {
|
|
||||||
usleep(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
CLIENT.state = CLIENT_STATE_DISCONNECTED;
|
|
||||||
close(CLIENT.socket);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t _clientSend(const uint8_t *data, const size_t size) {
|
|
||||||
assertNotNull(data, "Data cannot be NULL.");
|
|
||||||
assertTrue(size > 0, "Size cannot be zero.");
|
|
||||||
|
|
||||||
return send(CLIENT.socket, data, size, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t _clientReceive(uint8_t *data, const size_t size) {
|
|
||||||
assertNotNull(data, "Data cannot be NULL.");
|
|
||||||
assertTrue(size > 0, "Size cannot be zero.");
|
|
||||||
|
|
||||||
fd_set readfds;
|
|
||||||
struct timeval timeout;
|
|
||||||
int32_t activity;
|
|
||||||
|
|
||||||
FD_ZERO(&readfds);
|
|
||||||
FD_SET(CLIENT.socket, &readfds);
|
|
||||||
timeout.tv_sec = 5; // 5 seconds timeout
|
|
||||||
timeout.tv_usec = 0;
|
|
||||||
|
|
||||||
activity = select(CLIENT.socket + 1, &readfds, NULL, NULL, &timeout);
|
|
||||||
if(activity <= 0) {
|
|
||||||
assertUnreachable("Timeout or error while waiting for server data.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return recv(CLIENT.socket, data, size, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void clientDispose() {
|
|
||||||
if(CLIENT.state == CLIENT_STATE_CONNECTED) {
|
|
||||||
CLIENT.state = CLIENT_STATE_DISCONNECTING;
|
|
||||||
|
|
||||||
// Wait for client to disconnect
|
|
||||||
while(CLIENT.state == CLIENT_STATE_DISCONNECTING) usleep(1000);
|
|
||||||
pthread_join(CLIENT.thread, NULL);
|
|
||||||
|
|
||||||
consolePrint("Disconnected from server.");
|
|
||||||
}
|
|
||||||
|
|
||||||
close(CLIENT.socket);
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
|
|
||||||
#define CLIENT_HOST_STR_LEN 256
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
CLIENT_STATE_DISCONNECTED,
|
|
||||||
CLIENT_STATE_CONNECTING,
|
|
||||||
CLIENT_STATE_CONNECTED,
|
|
||||||
CLIENT_STATE_DISCONNECTING
|
|
||||||
} clientstate_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
clientstate_t state;
|
|
||||||
int32_t socket;
|
|
||||||
pthread_t thread;
|
|
||||||
struct sockaddr_in serverAddress;
|
|
||||||
} client_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char_t host[CLIENT_HOST_STR_LEN+1];
|
|
||||||
uint32_t port;
|
|
||||||
} clientinit_t;
|
|
||||||
|
|
||||||
extern client_t CLIENT;
|
|
||||||
|
|
||||||
void clientInit(const clientinit_t init);
|
|
||||||
void clientDispose();
|
|
||||||
void * clientThread(void* arg);
|
|
||||||
|
|
||||||
ssize_t _clientSend(const uint8_t *data, const size_t size);
|
|
||||||
ssize_t _clientReceive(uint8_t *data, const size_t size);
|
|
@ -1,17 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
PACKET_TYPE_CONNECTED
|
|
||||||
} packettype_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
packettype_t type;
|
|
||||||
} packet_t;
|
|
@ -1,11 +0,0 @@
|
|||||||
# 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
|
|
||||||
)
|
|
@ -1,149 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
consolePrint("Server started");
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
consolePrint("Server stopped");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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();
|
|
@ -1,139 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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.");
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
ssize_t bytesSent = serverClientSend(client, hello, sizeof(hello));
|
|
||||||
assertTrue(
|
|
||||||
bytesSent == sizeof(hello),
|
|
||||||
"Failed to send hello to client."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Client should respond "OK"
|
|
||||||
{
|
|
||||||
uint8_t response[2];
|
|
||||||
ssize_t bytesReceived = serverClientReceive(client, response, sizeof(response));
|
|
||||||
assertTrue(
|
|
||||||
bytesReceived == sizeof(response) &&
|
|
||||||
response[0] == 'O' &&
|
|
||||||
response[1] == 'K',
|
|
||||||
"Client did not respond with OK."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point we can start communicating "properly", so let's send the
|
|
||||||
// first real packet.
|
|
||||||
packet_t packet = { .type = PACKET_TYPE_CONNECTED };
|
|
||||||
if(!serverClientSendPacket(client, &packet)) {
|
|
||||||
assertUnreachable("Failed to send connected packet to client.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the client state to connected
|
|
||||||
client->state = SERVER_CLIENT_STATE_CONNECTED;
|
|
||||||
|
|
||||||
while(client->state == SERVER_CLIENT_STATE_CONNECTED) {
|
|
||||||
// Do nothing for now.
|
|
||||||
usleep(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disconnect
|
|
||||||
client->state = SERVER_CLIENT_STATE_DISCONNECTED;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t 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.");
|
|
||||||
|
|
||||||
return send(client->socket, data, size, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t 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.");
|
|
||||||
|
|
||||||
fd_set readfds;
|
|
||||||
struct timeval timeout;
|
|
||||||
int32_t activity;
|
|
||||||
|
|
||||||
FD_ZERO(&readfds);
|
|
||||||
FD_SET(client->socket, &readfds);
|
|
||||||
timeout.tv_sec = 5; // 5 seconds timeout
|
|
||||||
timeout.tv_usec = 0;
|
|
||||||
|
|
||||||
activity = select(client->socket + 1, &readfds, NULL, NULL, &timeout);
|
|
||||||
if(activity <= 0) {
|
|
||||||
assertUnreachable("Timeout or error while waiting for client data.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return recv(client->socket, data, size, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool_t serverClientSendPacket(
|
|
||||||
serverclient_t *client,
|
|
||||||
const packet_t *packet
|
|
||||||
) {
|
|
||||||
assertNotNull(client, "Client cannot be NULL.");
|
|
||||||
assertNotNull(packet, "Packet cannot be NULL.");
|
|
||||||
|
|
||||||
ssize_t sent = serverClientSend(client, (const uint8_t*)packet, sizeof(packet_t));
|
|
||||||
return sent == sizeof(packet_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
#include "net/packet.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);
|
|
||||||
|
|
||||||
ssize_t serverClientSend(
|
|
||||||
serverclient_t *client,
|
|
||||||
const uint8_t *data,
|
|
||||||
const size_t size
|
|
||||||
);
|
|
||||||
|
|
||||||
ssize_t serverClientReceive(
|
|
||||||
serverclient_t *client,
|
|
||||||
uint8_t *buffer,
|
|
||||||
const size_t size
|
|
||||||
);
|
|
||||||
|
|
||||||
bool_t serverClientSendPacket(
|
|
||||||
serverclient_t *client,
|
|
||||||
const packet_t *packet
|
|
||||||
);
|
|
||||||
|
|
||||||
void serverClientDispose(serverclient_t *client);
|
|
Reference in New Issue
Block a user