diff --git a/common/include/error.h b/common/include/error.h index 2a1a66f..7a231a1 100644 --- a/common/include/error.h +++ b/common/include/error.h @@ -13,6 +13,7 @@ enum error_t { ERR_JSON_DESERIALISE, ERR_INVALID_REQUEST, ERR_REQUEST_FAILED, + ERR_THREAD, __ERR_COUNT, }; diff --git a/common/src/error.c b/common/src/error.c index 3e3b028..988768d 100644 --- a/common/src/error.c +++ b/common/src/error.c @@ -13,7 +13,8 @@ char const *const ERROR_STRS[] = { "JSON SERIALISATION", "JSON DESERIALISATION", "INVALID REQUEST", - "REQUEST FAILED" + "REQUEST FAILED", + "THREAD INITIALISATION" }; diff --git a/doc/TODO.gmi b/doc/TODO.gmi index 4d7c48b..c727903 100644 --- a/doc/TODO.gmi +++ b/doc/TODO.gmi @@ -30,3 +30,4 @@ A list of things I'd like to accomplish * Remove rendering from serverside * Client-side resource files for each mod * Real error handling (right now I just pass up to main and immediately exit) +* Portability to other UNIX-like operating systems (if changes are needed; I don't care about DOS-likes) diff --git a/server/src/data.h b/server/src/game_data.h similarity index 55% rename from server/src/data.h rename to server/src/game_data.h index cc0a570..46a3e6c 100644 --- a/server/src/data.h +++ b/server/src/game_data.h @@ -1,13 +1,17 @@ -#ifndef DAEMON_DATA_H -#define DAEMON_DATA_H +#ifndef GAME_DATA_H +#define GAME_DATA_H + +#include #include #include "opts.h" -struct data_t { +struct game_data_t { struct options_t options; + struct world_t world; + pthread_mutex_t world_lock; int socket; int socket_accept; diff --git a/server/src/main.c b/server/src/main.c index 7360d44..dad77ff 100644 --- a/server/src/main.c +++ b/server/src/main.c @@ -12,46 +12,22 @@ #include #include "opts.h" -#include "sock.h" -#include "data.h" -#include "request.h" +#include "socket.h" +#include "game_data.h" static void handle_signal(int signal_no) { (void)signal_no; - remove(SOCK_PATH); + remove(SOCKET_PATH); exit(0); } -static enum error_t game_loop(struct data_t *data) { - enum error_t err = ERR_OK; - - char output_buffer[8192] = { 0 }; - char input_buffer[8192] = { 0 }; - - // Receive request from client - read(data->socket_accept, input_buffer, 8192); - - struct request_t request = { 0 }; - err = request_deserialise_str(&request, input_buffer); - if (err != ERR_OK) return err; - - // Send response to client - struct response_t response = handle_request(&request, data); - err = response_serialise_buf(&response, output_buffer, 8192); - if (err != ERR_OK) return err; - - write(data->socket_accept, output_buffer, 8192); - - return ERR_OK; -} - - int main(int argc, char **argv) { // Set up variables enum error_t err = ERR_OK; - struct data_t data = { 0 }; + struct game_data_t data = { 0 }; + // Signal handling; TODO: should probably improve this signal(SIGINT, handle_signal); @@ -70,6 +46,8 @@ int main(int argc, char **argv) { err = world_init(&data.world, 10, 10); if (err) goto handle_error; + pthread_mutex_init(&data.world_lock, NULL); + err = world_register_entity(&data.world, "john", 'j'); if (err) goto handle_error_world; @@ -82,16 +60,23 @@ int main(int argc, char **argv) { entity_init(&data.world.entities[26], 2, 2, 6); // Make a gap in the entity array to see if "ghost" entities remain - // Socket handling and run gameloop - err = sock_loop(&data, game_loop); + // Socket handling + pthread_t pthread_socket; + err = socket_thread(&pthread_socket, &data); if (err) goto handle_error; + pthread_join(pthread_socket, NULL); + // Deinitialisation + pthread_mutex_destroy(&data.world_lock); + world_free(&data.world); opts_free(&data.options); + return ERR_OK; handle_error_world: + pthread_mutex_destroy(&data.world_lock); world_free(&data.world); handle_error: diff --git a/server/src/request.c b/server/src/request.c index 9b8dda2..d0f1c08 100644 --- a/server/src/request.c +++ b/server/src/request.c @@ -7,7 +7,7 @@ static void handle_request_get_world_data( struct response_t *response, struct request_t const *request, - struct data_t const *ctx + struct game_data_t const *ctx ) { assert(response != NULL); assert(request != NULL); @@ -26,7 +26,7 @@ static void handle_request_get_world_data( struct response_t handle_request( struct request_t const *request, - struct data_t *ctx + struct game_data_t *ctx ) { assert(request != NULL); assert(ctx != NULL); diff --git a/server/src/request.h b/server/src/request.h index e55c1c6..f9ef8c4 100644 --- a/server/src/request.h +++ b/server/src/request.h @@ -3,11 +3,11 @@ #include -#include "data.h" +#include "game_data.h" struct response_t handle_request( struct request_t const *, - struct data_t * + struct game_data_t * ); #endif diff --git a/server/src/sock.c b/server/src/sock.c deleted file mode 100644 index 60049d9..0000000 --- a/server/src/sock.c +++ /dev/null @@ -1,74 +0,0 @@ -// sock.c - -#include -#include -#include -#include -#include -#include - -#include "sock.h" - -enum error_t sock_init(int *sockptr) { - assert(sockptr != NULL); - - int const sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock < 0) goto sock_init_error; - - struct sockaddr_un sa = { 0 }; - sa.sun_family = AF_UNIX; - - strcpy(sa.sun_path, SOCK_PATH); - remove(SOCK_PATH); - // Should be redundant but it doesn't hurt to be sure - - if (bind(sock, (struct sockaddr *)&sa, sizeof(sa))) goto sock_error; - if (listen(sock, 4096) < 0) goto sock_error; - - *sockptr = sock; - return ERR_OK; - -sock_error: - sock_free(&sock); - -sock_init_error: - return ERR_SOCKET; -} - - -void sock_free(int const *sockptr) { - assert(sockptr != NULL); - - close(*sockptr); - remove(SOCK_PATH); -} - - -enum error_t sock_loop(struct data_t *data, gameloop_fn fn) { - assert(data != NULL); - - enum error_t err = ERR_OK; - - if ((err = sock_init(&data->socket))) return err; - - for (int sock_accept;;) { - if ((sock_accept = accept(data->socket, NULL, NULL)) < 0) { - err = ERR_SOCKET; - goto sock_loop_end_pre; - }; - - data->socket_accept = sock_accept; - if ((err = fn(data))) goto sock_loop_end_pre; - - close(data->socket_accept); - continue; - -sock_loop_end_pre: - close(data->socket_accept); - goto sock_loop_end; - } - -sock_loop_end: - sock_free(&data->socket); - return err; -} diff --git a/server/src/sock.h b/server/src/sock.h deleted file mode 100644 index d7a5c5d..0000000 --- a/server/src/sock.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef SOCK_H -#define SOCK_H - -#include - -#include "data.h" - -#define SOCK_PATH "/tmp/swd.sock" - // Socket path should be a shared setting between server and client - -enum error_t sock_init(int *); -void sock_free(int const *); - -typedef enum error_t (* gameloop_fn)(struct data_t *); -enum error_t sock_loop(struct data_t *, gameloop_fn); - -#endif diff --git a/server/src/socket.c b/server/src/socket.c new file mode 100644 index 0000000..e88bf9b --- /dev/null +++ b/server/src/socket.c @@ -0,0 +1,107 @@ +// sock.c + +#include +#include +#include +#include +#include +#include + +#include "socket.h" +#include "request.h" + +enum error_t socket_init(int *sockptr) { + assert(sockptr != NULL); + + int const sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) goto error; + + struct sockaddr_un sa = { 0 }; + sa.sun_family = AF_UNIX; + + strcpy(sa.sun_path, SOCKET_PATH); + remove(SOCKET_PATH); // Should be redundant but it doesn't hurt to be sure + + if (bind(sock, (struct sockaddr *)&sa, sizeof(sa))) goto error_free_sock; + if (listen(sock, 4096) < 0) goto error_free_sock; + + *sockptr = sock; + return ERR_OK; + +error_free_sock: + socket_free(&sock); +error: + return ERR_SOCKET; +} + + +void socket_free(int const *sockptr) { + assert(sockptr != NULL); + + close(*sockptr); + remove(SOCKET_PATH); +} + + +static enum error_t socket_handle(struct game_data_t *data) { + enum error_t err = ERR_OK; + + char output_buffer[8192] = { 0 }; + char input_buffer[8192] = { 0 }; + + // Receive request from client + read(data->socket_accept, input_buffer, 8192); + + struct request_t request = { 0 }; + err = request_deserialise_str(&request, input_buffer); + if (err != ERR_OK) return err; + + // Send response to client + struct response_t response = handle_request(&request, data); + err = response_serialise_buf(&response, output_buffer, 8192); + if (err != ERR_OK) return err; + + write(data->socket_accept, output_buffer, 8192); + + return ERR_OK; +} + + +static void *socket_thread_fn(void *data_void) { + assert(data_void != NULL); + + struct game_data_t *data = (struct game_data_t *)data_void; + + enum error_t err = ERR_OK; + + if ((err = socket_init(&data->socket))) return (void *)err; + + for (int sock_accept;;) { + if ((sock_accept = accept(data->socket, NULL, NULL)) < 0) { + err = ERR_SOCKET; + goto error_pre; + }; + + data->socket_accept = sock_accept; + if ((err = socket_handle(data))) goto error_pre; + + close(data->socket_accept); + continue; + +error_pre: + close(data->socket_accept); + goto error; + } + +error: + socket_free(&data->socket); + return (void *)err; +} + + +enum error_t socket_thread(pthread_t *pthread, struct game_data_t *data) { + int err = pthread_create(pthread, NULL, socket_thread_fn, data); + if (err != 0) return ERR_THREAD; + + return ERR_OK; +} diff --git a/server/src/socket.h b/server/src/socket.h new file mode 100644 index 0000000..7fef344 --- /dev/null +++ b/server/src/socket.h @@ -0,0 +1,16 @@ +#ifndef SOCKET_H +#define SOCKET_H + +#include + +#include "game_data.h" + +#define SOCKET_PATH "/tmp/swd.sock" + // Socket path should be a shared setting between server and client + +enum error_t socket_init(int *); +void socket_free(int const *); + +enum error_t socket_thread(pthread_t *, struct game_data_t *); + +#endif