Compare commits
No commits in common. "b3261454d954920a1499cb9dcddb370fe056c244" and "c32af03d209dc5b9956c753d6ab9a8e96fa21d6a" have entirely different histories.
b3261454d9
...
c32af03d20
@ -3,7 +3,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(simworld VERSION 0.0.1 LANGUAGES C)
|
project(simworld VERSION 0.0.1 LANGUAGES C)
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -std=gnu17 -Wpedantic -pedantic-errors -Wformat=2 -Wshadow -Wwrite-strings -Wstrict-prototypes -g")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -std=c17 -Wpedantic -pedantic-errors -Wformat=2 -Wshadow -Wwrite-strings -Wstrict-prototypes -g")
|
||||||
|
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
@ -10,5 +10,3 @@ add_executable(${PROJECT_NAME} ${SOURCES})
|
|||||||
target_link_libraries(${PROJECT_NAME} PRIVATE simworld)
|
target_link_libraries(${PROJECT_NAME} PRIVATE simworld)
|
||||||
|
|
||||||
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/common/include)
|
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/common/include)
|
||||||
|
|
||||||
install(TARGETS ${PROJECT_NAME})
|
|
||||||
|
@ -4,11 +4,9 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
||||||
#include <error.h>
|
|
||||||
#include <request.h>
|
|
||||||
#include <world.h>
|
#include <world.h>
|
||||||
|
#include <error.h>
|
||||||
|
|
||||||
#include "render/render.h"
|
|
||||||
#include "sock.h"
|
#include "sock.h"
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
@ -18,33 +16,21 @@ int main(void) {
|
|||||||
err = sock_init(&sock);
|
err = sock_init(&sock);
|
||||||
if (err) goto error;
|
if (err) goto error;
|
||||||
|
|
||||||
char input_buffer[8192] = { 0 };
|
char ibuf[8192] = { 0 };
|
||||||
char output_buffer[8192] = { 0 };
|
read(sock, ibuf, 8192);
|
||||||
|
|
||||||
// Send request for world data to server
|
struct world_t world = { 0 };
|
||||||
struct request_body_get_world_data_t request_body = { 420 };
|
err = world_deserialise_str(&world, ibuf);
|
||||||
struct request_t request = { REQUEST_GET_WORLD_DATA, &request_body };
|
|
||||||
|
|
||||||
err = request_serialise_buf(&request, output_buffer, 8192);
|
|
||||||
if (err != ERR_OK) goto error_socket;
|
if (err != ERR_OK) goto error_socket;
|
||||||
|
|
||||||
write(sock, output_buffer, 8192);
|
printf("CLIENT: (%zu %zu) ->", world.height++, world.width ++);
|
||||||
|
printf(" (%zu %zu)\n", world.height, world.width);
|
||||||
|
|
||||||
// Get response from server
|
char obuf[8192] = { 0 };
|
||||||
read(sock, input_buffer, 8192);
|
err = world_serialise_buf(&world, obuf, 8192);
|
||||||
|
|
||||||
struct response_t response = { 0 };
|
|
||||||
err = response_deserialise_str(&response, input_buffer);
|
|
||||||
if (err != ERR_OK) goto error_socket;
|
if (err != ERR_OK) goto error_socket;
|
||||||
|
|
||||||
// Render data
|
write(sock, obuf, 8192);
|
||||||
if (response.success != true) {
|
|
||||||
err = ERR_REQUEST_FAILED;
|
|
||||||
goto error_socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct response_body_get_world_data_t *response_body = response.body;
|
|
||||||
render_world(&response_body->world);
|
|
||||||
|
|
||||||
close(sock);
|
close(sock);
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
// render/render.c
|
// render/render.c
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
|
|
||||||
|
|
||||||
void render_world(struct world_t const *world) {
|
void render_world(struct world_t const *world) {
|
||||||
assert(world != NULL);
|
|
||||||
|
|
||||||
printf("TICK: %zu\n", world->tick);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < MAX_ENTITIES; i++) {
|
for (size_t i = 0; i < MAX_ENTITIES; i++) {
|
||||||
struct entity_t const *const entity = &world->entities[i];
|
struct entity_t const *const entity = &world->entities[i];
|
||||||
struct entity_registrant_t const *const registrant =
|
struct entity_registrant_t const *const registrant =
|
||||||
|
@ -11,10 +11,8 @@ target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/common/inc
|
|||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
|
|
||||||
pkg_check_modules(LUA REQUIRED lua5.4)
|
pkg_check_modules(LUA REQUIRED lua)
|
||||||
pkg_check_modules(JANSSON REQUIRED jansson)
|
pkg_check_modules(JANSSON REQUIRED jansson)
|
||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME} ${LUA_LIBRARIES} ${JANSSON_LIBRARIES})
|
target_link_libraries(${PROJECT_NAME} ${LUA_LIBRARIES} ${JANSSON_LIBRARIES})
|
||||||
target_include_directories(${PROJECT_NAME} PRIVATE ${LUA_INCLUDE_DIRS} ${JANSSON_INCLUDE_DIRS})
|
target_include_directories(${PROJECT_NAME} PRIVATE ${LUA_INCLUDE_DIRS} ${JANSSON_INCLUDE_DIRS})
|
||||||
|
|
||||||
install(TARGETS ${PROJECT_NAME} DESTINATION lib)
|
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
#ifndef ERROR_H
|
#ifndef ERROR_H
|
||||||
#define ERROR_H
|
#define ERROR_H
|
||||||
|
|
||||||
// TODO: Most of these aren't even errors used in this library.
|
|
||||||
// I should trim this down to only the ones belonging to the library, and make
|
|
||||||
// new enums for the client and server. Perhaps I can leverage __ERR_COUNT to
|
|
||||||
// have the client/server-specific enums start where this one ends
|
|
||||||
enum error_t {
|
enum error_t {
|
||||||
ERR_OK,
|
ERR_OK,
|
||||||
ERR_INPUT,
|
ERR_INPUT,
|
||||||
@ -15,10 +11,6 @@ enum error_t {
|
|||||||
ERR_SOCKET,
|
ERR_SOCKET,
|
||||||
ERR_JSON_SERIALISE,
|
ERR_JSON_SERIALISE,
|
||||||
ERR_JSON_DESERIALISE,
|
ERR_JSON_DESERIALISE,
|
||||||
ERR_INVALID_REQUEST,
|
|
||||||
ERR_REQUEST_FAILED,
|
|
||||||
ERR_THREAD,
|
|
||||||
ERR_MUTEX,
|
|
||||||
__ERR_COUNT,
|
__ERR_COUNT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,10 +2,11 @@
|
|||||||
#define COMMON_REQUEST_H
|
#define COMMON_REQUEST_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "request/get_world_data.h"
|
#include "world.h"
|
||||||
|
|
||||||
|
// ### REQUEST TYPES ### //
|
||||||
|
|
||||||
enum request_type_t {
|
enum request_type_t {
|
||||||
REQUEST_NONE,
|
REQUEST_NONE,
|
||||||
@ -13,6 +14,11 @@ enum request_type_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct request_body_get_world_data_t {
|
||||||
|
size_t world_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct request_t {
|
struct request_t {
|
||||||
enum request_type_t type;
|
enum request_type_t type;
|
||||||
void *body;
|
void *body;
|
||||||
@ -26,6 +32,13 @@ enum error_t request_deserialise_str(struct request_t *, char const *);
|
|||||||
enum error_t request_serialise_buf(struct request_t const *, char *, size_t);
|
enum error_t request_serialise_buf(struct request_t const *, char *, size_t);
|
||||||
|
|
||||||
|
|
||||||
|
// ### RESPONSE TYPES ### //
|
||||||
|
|
||||||
|
struct response_body_get_world_data_t {
|
||||||
|
struct world_t world;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct response_t {
|
struct response_t {
|
||||||
enum request_type_t type;
|
enum request_type_t type;
|
||||||
bool success;
|
bool success;
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
#ifndef COMMON_REQUEST_GET_WORLD_DATA_H
|
|
||||||
#define COMMON_REQUEST_GET_WORLD_DATA_H
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "../world.h"
|
|
||||||
|
|
||||||
struct request_body_get_world_data_t {
|
|
||||||
size_t world_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
enum error_t request_body_get_world_data_serialise(
|
|
||||||
struct request_body_get_world_data_t const *,
|
|
||||||
struct json_t **
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
enum error_t request_body_get_world_data_deserialise(
|
|
||||||
struct request_body_get_world_data_t *,
|
|
||||||
struct json_t *
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
struct response_body_get_world_data_t {
|
|
||||||
struct world_t world;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
enum error_t response_body_get_world_data_serialise(
|
|
||||||
struct response_body_get_world_data_t const *,
|
|
||||||
struct json_t **
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
enum error_t response_body_get_world_data_deserialise(
|
|
||||||
struct response_body_get_world_data_t *,
|
|
||||||
struct json_t *
|
|
||||||
);
|
|
||||||
|
|
||||||
#endif
|
|
@ -15,8 +15,6 @@ struct world_t {
|
|||||||
struct entity_t entities[MAX_ENTITIES];
|
struct entity_t entities[MAX_ENTITIES];
|
||||||
struct entity_registry_t registered_entities;
|
struct entity_registry_t registered_entities;
|
||||||
|
|
||||||
size_t tick;
|
|
||||||
|
|
||||||
size_t height;
|
size_t height;
|
||||||
size_t width;
|
size_t width;
|
||||||
};
|
};
|
||||||
|
@ -12,10 +12,6 @@ char const *const ERROR_STRS[] = {
|
|||||||
"SOCKET",
|
"SOCKET",
|
||||||
"JSON SERIALISATION",
|
"JSON SERIALISATION",
|
||||||
"JSON DESERIALISATION",
|
"JSON DESERIALISATION",
|
||||||
"INVALID REQUEST",
|
|
||||||
"REQUEST FAILED",
|
|
||||||
"THREAD INITIALISATION",
|
|
||||||
"MUTEX ERROR",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,9 +5,34 @@
|
|||||||
|
|
||||||
#include "request.h"
|
#include "request.h"
|
||||||
|
|
||||||
|
// Request Format Strings
|
||||||
static char const *REQUEST_JSON_FMT = "{si, so}";
|
static char const *REQUEST_JSON_FMT = "{si, so}";
|
||||||
|
|
||||||
|
static char const *REQUEST_BODY_GET_WORLD_DATA_JSON_FMT = "{sI}";
|
||||||
|
|
||||||
|
// Response Format Strings
|
||||||
static char const *RESPONSE_JSON_FMT = "{sb, si, so}";
|
static char const *RESPONSE_JSON_FMT = "{sb, si, so}";
|
||||||
|
|
||||||
|
static char const *RESPONSE_BODY_GET_WORLD_DATA_JSON_FMT = "{so}";
|
||||||
|
|
||||||
|
|
||||||
|
static enum error_t request_serialise_body_get_world_data(
|
||||||
|
struct request_body_get_world_data_t const *self,
|
||||||
|
struct json_t **jsonptr
|
||||||
|
) {
|
||||||
|
assert(self != NULL);
|
||||||
|
assert(jsonptr != NULL);
|
||||||
|
|
||||||
|
struct json_t *json = json_pack(REQUEST_BODY_GET_WORLD_DATA_JSON_FMT,
|
||||||
|
"world-id", self->world_id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (json == NULL) return ERR_JSON_SERIALISE;
|
||||||
|
|
||||||
|
*jsonptr = json;
|
||||||
|
return ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static enum error_t request_serialise_body(
|
static enum error_t request_serialise_body(
|
||||||
struct request_t const *self,
|
struct request_t const *self,
|
||||||
@ -18,7 +43,7 @@ static enum error_t request_serialise_body(
|
|||||||
|
|
||||||
switch (self->type) {
|
switch (self->type) {
|
||||||
case REQUEST_GET_WORLD_DATA:
|
case REQUEST_GET_WORLD_DATA:
|
||||||
return request_body_get_world_data_serialise(self->body, jsonptr);
|
return request_serialise_body_get_world_data(self->body, jsonptr);
|
||||||
|
|
||||||
default: return ERR_JSON_SERIALISE;
|
default: return ERR_JSON_SERIALISE;
|
||||||
}
|
}
|
||||||
@ -137,6 +162,22 @@ static enum error_t request_deserialise_parts(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static enum error_t request_deserialise_body_get_world_data(
|
||||||
|
struct request_body_get_world_data_t *self,
|
||||||
|
struct json_t *json
|
||||||
|
) {
|
||||||
|
assert(self != NULL);
|
||||||
|
assert(json != NULL);
|
||||||
|
|
||||||
|
int err = json_unpack(json, REQUEST_BODY_GET_WORLD_DATA_JSON_FMT,
|
||||||
|
"world-id", &self->world_id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (err < 0) return ERR_JSON_DESERIALISE;
|
||||||
|
return ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static enum error_t request_deserialise_body(
|
static enum error_t request_deserialise_body(
|
||||||
struct request_t *self,
|
struct request_t *self,
|
||||||
struct json_t *json
|
struct json_t *json
|
||||||
@ -149,7 +190,7 @@ static enum error_t request_deserialise_body(
|
|||||||
self->body = malloc(sizeof(struct request_body_get_world_data_t));
|
self->body = malloc(sizeof(struct request_body_get_world_data_t));
|
||||||
if (self->body == NULL) return ERR_ALLOC;
|
if (self->body == NULL) return ERR_ALLOC;
|
||||||
|
|
||||||
return request_body_get_world_data_deserialise(self->body, json);
|
return request_deserialise_body_get_world_data(self->body, json);
|
||||||
|
|
||||||
default: return ERR_JSON_SERIALISE;
|
default: return ERR_JSON_SERIALISE;
|
||||||
}
|
}
|
||||||
@ -200,6 +241,22 @@ enum error_t request_deserialise_str(struct request_t *self, char const *str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static enum error_t response_serialise_body_get_world_data(
|
||||||
|
struct response_body_get_world_data_t const *self,
|
||||||
|
struct json_t **jsonptr
|
||||||
|
) {
|
||||||
|
assert(self != NULL);
|
||||||
|
assert(jsonptr != NULL);
|
||||||
|
|
||||||
|
struct json_t *json = NULL;
|
||||||
|
enum error_t err = world_serialise(&self->world, &json);
|
||||||
|
if (err != ERR_OK) return err;
|
||||||
|
|
||||||
|
*jsonptr = json;
|
||||||
|
return ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static enum error_t response_serialise_body(
|
static enum error_t response_serialise_body(
|
||||||
struct response_t const *self,
|
struct response_t const *self,
|
||||||
struct json_t **jsonptr
|
struct json_t **jsonptr
|
||||||
@ -209,7 +266,7 @@ static enum error_t response_serialise_body(
|
|||||||
|
|
||||||
switch (self->type) {
|
switch (self->type) {
|
||||||
case REQUEST_GET_WORLD_DATA:
|
case REQUEST_GET_WORLD_DATA:
|
||||||
return response_body_get_world_data_serialise(self->body, jsonptr);
|
return response_serialise_body_get_world_data(self->body, jsonptr);
|
||||||
|
|
||||||
default: return ERR_JSON_SERIALISE;
|
default: return ERR_JSON_SERIALISE;
|
||||||
}
|
}
|
||||||
@ -336,6 +393,20 @@ static enum error_t response_deserialise_parts(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static enum error_t response_deserialise_body_get_world_data(
|
||||||
|
struct response_body_get_world_data_t *self,
|
||||||
|
struct json_t *json
|
||||||
|
) {
|
||||||
|
assert(self != NULL);
|
||||||
|
assert(json != NULL);
|
||||||
|
|
||||||
|
enum error_t err = world_deserialise(&self->world, json);
|
||||||
|
if (err != ERR_OK) return err;
|
||||||
|
|
||||||
|
return ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static enum error_t response_deserialise_body(
|
static enum error_t response_deserialise_body(
|
||||||
struct response_t *self,
|
struct response_t *self,
|
||||||
struct json_t *json
|
struct json_t *json
|
||||||
@ -348,7 +419,7 @@ static enum error_t response_deserialise_body(
|
|||||||
self->body = malloc(sizeof(struct response_body_get_world_data_t));
|
self->body = malloc(sizeof(struct response_body_get_world_data_t));
|
||||||
if (self->body == NULL) return ERR_ALLOC;
|
if (self->body == NULL) return ERR_ALLOC;
|
||||||
|
|
||||||
return response_body_get_world_data_deserialise(self->body, json);
|
return response_deserialise_body_get_world_data(self->body, json);
|
||||||
|
|
||||||
default: return ERR_JSON_SERIALISE;
|
default: return ERR_JSON_SERIALISE;
|
||||||
}
|
}
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
// request/get_world_data.c
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include "request/get_world_data.h"
|
|
||||||
|
|
||||||
static char const *REQUEST_BODY_GET_WORLD_DATA_JSON_FMT = "{sI}";
|
|
||||||
|
|
||||||
|
|
||||||
enum error_t request_body_get_world_data_serialise(
|
|
||||||
struct request_body_get_world_data_t const *self,
|
|
||||||
struct json_t **jsonptr
|
|
||||||
) {
|
|
||||||
assert(self != NULL);
|
|
||||||
assert(jsonptr != NULL);
|
|
||||||
|
|
||||||
struct json_t *json = json_pack(REQUEST_BODY_GET_WORLD_DATA_JSON_FMT,
|
|
||||||
"world-id", self->world_id
|
|
||||||
);
|
|
||||||
|
|
||||||
if (json == NULL) return ERR_JSON_SERIALISE;
|
|
||||||
|
|
||||||
*jsonptr = json;
|
|
||||||
return ERR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum error_t request_body_get_world_data_deserialise(
|
|
||||||
struct request_body_get_world_data_t *self,
|
|
||||||
struct json_t *json
|
|
||||||
) {
|
|
||||||
assert(self != NULL);
|
|
||||||
assert(json != NULL);
|
|
||||||
|
|
||||||
int err = json_unpack(json, REQUEST_BODY_GET_WORLD_DATA_JSON_FMT,
|
|
||||||
"world-id", &self->world_id
|
|
||||||
);
|
|
||||||
|
|
||||||
if (err < 0) return ERR_JSON_DESERIALISE;
|
|
||||||
return ERR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum error_t response_body_get_world_data_serialise(
|
|
||||||
struct response_body_get_world_data_t const *self,
|
|
||||||
struct json_t **jsonptr
|
|
||||||
) {
|
|
||||||
assert(self != NULL);
|
|
||||||
assert(jsonptr != NULL);
|
|
||||||
|
|
||||||
struct json_t *json = NULL;
|
|
||||||
enum error_t err = world_serialise(&self->world, &json);
|
|
||||||
if (err != ERR_OK) return err;
|
|
||||||
|
|
||||||
*jsonptr = json;
|
|
||||||
return ERR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum error_t response_body_get_world_data_deserialise(
|
|
||||||
struct response_body_get_world_data_t *self,
|
|
||||||
struct json_t *json
|
|
||||||
) {
|
|
||||||
assert(self != NULL);
|
|
||||||
assert(json != NULL);
|
|
||||||
|
|
||||||
enum error_t err = world_deserialise(&self->world, json);
|
|
||||||
if (err != ERR_OK) return err;
|
|
||||||
|
|
||||||
return ERR_OK;
|
|
||||||
}
|
|
@ -5,14 +5,12 @@
|
|||||||
|
|
||||||
#include "world.h"
|
#include "world.h"
|
||||||
|
|
||||||
static char const *const WORLD_JSON_FMT = "{so, so, sI, sI, sI}";
|
static char const *const WORLD_JSON_FMT = "{so, so, sI, sI}";
|
||||||
|
|
||||||
|
|
||||||
enum error_t world_init(struct world_t *self, size_t height, size_t width) {
|
enum error_t world_init(struct world_t *self, size_t height, size_t width) {
|
||||||
assert(self != NULL);
|
assert(self != NULL);
|
||||||
|
|
||||||
self->tick = 0;
|
|
||||||
|
|
||||||
self->height = height;
|
self->height = height;
|
||||||
self->width = width;
|
self->width = width;
|
||||||
|
|
||||||
@ -86,7 +84,7 @@ static enum error_t world_serialise_entities(
|
|||||||
static enum error_t world_serialise_parts(
|
static enum error_t world_serialise_parts(
|
||||||
struct json_t *entities_json,
|
struct json_t *entities_json,
|
||||||
struct json_t *registered_entities_json,
|
struct json_t *registered_entities_json,
|
||||||
size_t tick, size_t height, size_t width,
|
size_t height, size_t width,
|
||||||
struct json_t **json
|
struct json_t **json
|
||||||
) {
|
) {
|
||||||
assert(entities_json != NULL);
|
assert(entities_json != NULL);
|
||||||
@ -96,7 +94,7 @@ static enum error_t world_serialise_parts(
|
|||||||
struct json_t *world_json = json_pack(WORLD_JSON_FMT,
|
struct json_t *world_json = json_pack(WORLD_JSON_FMT,
|
||||||
"entities", entities_json,
|
"entities", entities_json,
|
||||||
"registered-entities", registered_entities_json,
|
"registered-entities", registered_entities_json,
|
||||||
"tick", tick, "height", height, "width", width
|
"height", height, "width", width
|
||||||
);
|
);
|
||||||
|
|
||||||
if (world_json == NULL) return ERR_JSON_SERIALISE;
|
if (world_json == NULL) return ERR_JSON_SERIALISE;
|
||||||
@ -133,7 +131,7 @@ enum error_t world_serialise(
|
|||||||
err = world_serialise_parts(
|
err = world_serialise_parts(
|
||||||
entities_json,
|
entities_json,
|
||||||
registered_entities_json,
|
registered_entities_json,
|
||||||
self->tick, self->height, self->width,
|
self->height, self->width,
|
||||||
&world_json
|
&world_json
|
||||||
);
|
);
|
||||||
if (err != ERR_OK) goto error_world;
|
if (err != ERR_OK) goto error_world;
|
||||||
@ -152,7 +150,7 @@ error:
|
|||||||
static enum error_t world_deserialise_parts(
|
static enum error_t world_deserialise_parts(
|
||||||
struct json_t **entities_json,
|
struct json_t **entities_json,
|
||||||
struct json_t **registered_entities_json,
|
struct json_t **registered_entities_json,
|
||||||
size_t *tick, size_t *height, size_t *width,
|
size_t *height, size_t *width,
|
||||||
struct json_t *json
|
struct json_t *json
|
||||||
) {
|
) {
|
||||||
assert(entities_json != NULL);
|
assert(entities_json != NULL);
|
||||||
@ -164,7 +162,7 @@ static enum error_t world_deserialise_parts(
|
|||||||
int err = json_unpack(json, WORLD_JSON_FMT,
|
int err = json_unpack(json, WORLD_JSON_FMT,
|
||||||
"entities", entities_json,
|
"entities", entities_json,
|
||||||
"registered-entities", registered_entities_json,
|
"registered-entities", registered_entities_json,
|
||||||
"tick", tick, "height", height, "width", width
|
"height", height, "width", width
|
||||||
);
|
);
|
||||||
|
|
||||||
if (err < 0) return ERR_JSON_DESERIALISE;
|
if (err < 0) return ERR_JSON_DESERIALISE;
|
||||||
@ -232,7 +230,7 @@ enum error_t world_deserialise(struct world_t *self, struct json_t *json) {
|
|||||||
enum error_t err = world_deserialise_parts(
|
enum error_t err = world_deserialise_parts(
|
||||||
&entities_json,
|
&entities_json,
|
||||||
®istered_entities_json,
|
®istered_entities_json,
|
||||||
&temp_world.tick, &temp_world.height, &temp_world.width,
|
&temp_world.height, &temp_world.width,
|
||||||
json
|
json
|
||||||
);
|
);
|
||||||
if (err != ERR_OK) return err;
|
if (err != ERR_OK) return err;
|
||||||
|
17
doc/TODO.gmi
17
doc/TODO.gmi
@ -2,18 +2,12 @@
|
|||||||
A list of things I'd like to accomplish
|
A list of things I'd like to accomplish
|
||||||
|
|
||||||
## In Progress
|
## In Progress
|
||||||
* Add a lua modding API
|
|
||||||
* Load mods
|
|
||||||
* Handle mod dependencies
|
|
||||||
|
|
||||||
## Completed
|
|
||||||
* Specify mods directory
|
|
||||||
* Tick-based game loop
|
|
||||||
* Split game ticking and socket connecting logic (can only tick on connection atm)
|
|
||||||
* Client send request
|
* Client send request
|
||||||
* Server handle request and send response
|
* Server handle request and send response
|
||||||
* Client handle response
|
* Client handle response
|
||||||
* Have client render world data (return to pre-daemonised equivalent state)
|
* Have client render world data (return to pre-daemonised equivalent state)
|
||||||
|
|
||||||
|
## Completed
|
||||||
* Serialise data to JSON for socket data transmission
|
* Serialise data to JSON for socket data transmission
|
||||||
* Send world data to client
|
* Send world data to client
|
||||||
* Initialise a world again
|
* Initialise a world again
|
||||||
@ -25,13 +19,14 @@ A list of things I'd like to accomplish
|
|||||||
* Write Makefile to automate compilation
|
* Write Makefile to automate compilation
|
||||||
|
|
||||||
## Planned
|
## Planned
|
||||||
* Requests/responses for more than just the complete set of world data all at once
|
* Split game ticking and socket connecting logic (can only tick on connection atm)
|
||||||
* Create and load worlds
|
* Create and load worlds
|
||||||
* Display environment and pan camera
|
* Display environment and pan camera
|
||||||
* Time controls (play/pause/speed up)
|
* Time controls (play/pause/speed up)
|
||||||
* See creature stats
|
* See creature stats
|
||||||
|
* Add a lua modding API
|
||||||
|
* Load mods
|
||||||
|
* Handle mod dependencies
|
||||||
* Remove rendering from serverside
|
* Remove rendering from serverside
|
||||||
* Client-side resource files for each mod
|
* Client-side resource files for each mod
|
||||||
* Real error handling (right now I just pass up to main and immediately exit)
|
* 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)
|
|
||||||
* Reduce repetitiveness of JSON (de)serialisation code
|
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
-- init.lua
|
|
||||||
|
|
||||||
print("Hello, world!")
|
|
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "core",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"depends": [],
|
|
||||||
"optdepends": []
|
|
||||||
}
|
|
@ -7,40 +7,16 @@ if ! [ -d ".git" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
OPTIND=1
|
|
||||||
|
|
||||||
server_args=""
|
|
||||||
client1_args=""
|
|
||||||
client2_args=""
|
|
||||||
client3_args=""
|
|
||||||
|
|
||||||
while getopts "d:1:2:3:" opt; do
|
|
||||||
case "${opt}" in
|
|
||||||
d) server_args="${OPTARG}" ;;
|
|
||||||
1) client1_args="${OPTARG}" ;;
|
|
||||||
2) client2_args="${OPTARG}" ;;
|
|
||||||
3) client3_args="${OPTARG}" ;;
|
|
||||||
*) echo "Invalid flag"; exit 1
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
shift $((OPTIND - 1))
|
|
||||||
[ "${1:-}" = "--" ] && shift
|
|
||||||
|
|
||||||
./scripts/build.sh
|
./scripts/build.sh
|
||||||
|
|
||||||
echo "### DAEMON ###"
|
echo "### DAEMON ###"
|
||||||
./build/server/simworld-daemon ${server_args} &
|
./build/server/simworld-daemon &
|
||||||
sleep 1
|
sleep 1
|
||||||
|
|
||||||
echo "### CLIENT 1 ###"
|
echo "### CLIENT 1 ###"
|
||||||
./build/client/simworld-client ${client1_args}
|
./build/client/simworld-client
|
||||||
|
|
||||||
echo "### CLIENT 2 ###"
|
echo "### CLIENT 2 ###"
|
||||||
./build/client/simworld-client ${client2_args}
|
./build/client/simworld-client
|
||||||
|
|
||||||
echo "### CLIENT 3 ###"
|
|
||||||
sleep 1
|
|
||||||
./build/client/simworld-client ${client3_args}
|
|
||||||
|
|
||||||
killall simworld-daemon
|
killall simworld-daemon
|
||||||
|
@ -9,5 +9,3 @@ file(GLOB_RECURSE SOURCES ${SOURCE_DIR}/*.c)
|
|||||||
add_executable(${PROJECT_NAME} ${SOURCES})
|
add_executable(${PROJECT_NAME} ${SOURCES})
|
||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE simworld)
|
target_link_libraries(${PROJECT_NAME} PRIVATE simworld)
|
||||||
|
|
||||||
install(TARGETS ${PROJECT_NAME})
|
|
||||||
|
16
server/src/data.h
Normal file
16
server/src/data.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef DAEMON_DATA_H
|
||||||
|
#define DAEMON_DATA_H
|
||||||
|
|
||||||
|
#include <world.h>
|
||||||
|
|
||||||
|
#include "opts.h"
|
||||||
|
|
||||||
|
struct data_t {
|
||||||
|
struct options_t options;
|
||||||
|
struct world_t world;
|
||||||
|
|
||||||
|
int socket;
|
||||||
|
int socket_accept;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -1,67 +0,0 @@
|
|||||||
// game.c
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/syslog.h>
|
|
||||||
|
|
||||||
#include "game.h"
|
|
||||||
|
|
||||||
|
|
||||||
void write_log(bool is_daemon, char const *fmt, ...) {
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
|
|
||||||
if (is_daemon) vsyslog(LOG_INFO, fmt, args);
|
|
||||||
else vprintf(fmt, args);
|
|
||||||
|
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum error_t game_load_mods(struct game_t *game) {
|
|
||||||
assert(game != NULL);
|
|
||||||
|
|
||||||
if (game->options.mods_directory == NULL) return ERR_NOTFOUND;
|
|
||||||
|
|
||||||
DIR *mods_directory = opendir(game->options.mods_directory);
|
|
||||||
if (mods_directory == NULL) return ERR_NOTFOUND;
|
|
||||||
// Imagine handling errors properly
|
|
||||||
|
|
||||||
struct dirent *ent = NULL;
|
|
||||||
while ((ent = readdir(mods_directory)) != NULL) {
|
|
||||||
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
size_t abs_path_len = strlen(
|
|
||||||
game->options.mods_directory) + strlen(ent->d_name
|
|
||||||
);
|
|
||||||
|
|
||||||
char* abs_path = calloc(abs_path_len, sizeof(char));
|
|
||||||
|
|
||||||
strcat(abs_path, game->options.mods_directory);
|
|
||||||
strcat(abs_path, "/");
|
|
||||||
strcat(abs_path, ent->d_name);
|
|
||||||
|
|
||||||
struct stat file_info = { 0 };
|
|
||||||
|
|
||||||
if (stat(abs_path, &file_info) != 0) {
|
|
||||||
free(abs_path);
|
|
||||||
return ERR_NOTFOUND;
|
|
||||||
}
|
|
||||||
free(abs_path);
|
|
||||||
|
|
||||||
if (S_ISREG(file_info.st_mode))
|
|
||||||
game_log(game, "FOUND REGULAR FILE: ");
|
|
||||||
else if (S_ISDIR(file_info.st_mode))
|
|
||||||
game_log(game, "FOUND DIRECTORY: ");
|
|
||||||
|
|
||||||
game_log(game, "%s\n", ent->d_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
closedir(mods_directory);
|
|
||||||
return ERR_OK;
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
#ifndef GAME_H
|
|
||||||
#define GAME_H
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include <world.h>
|
|
||||||
|
|
||||||
#include "opts.h"
|
|
||||||
|
|
||||||
struct game_t {
|
|
||||||
struct options_t options;
|
|
||||||
|
|
||||||
struct world_t world;
|
|
||||||
pthread_mutex_t world_lock;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct mod_t {
|
|
||||||
char const *path;
|
|
||||||
struct mod_t **depends;
|
|
||||||
struct mod_t **opt_depends;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void write_log(bool, char const *, ...);
|
|
||||||
|
|
||||||
#define game_log(game, fmt, ...) \
|
|
||||||
write_log((game)->options.daemonise, fmt, ##__VA_ARGS__)
|
|
||||||
|
|
||||||
enum error_t game_load_mods(struct game_t *);
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,6 +1,5 @@
|
|||||||
// main.c
|
// main.c
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@ -13,48 +12,61 @@
|
|||||||
#include <request.h>
|
#include <request.h>
|
||||||
|
|
||||||
#include "opts.h"
|
#include "opts.h"
|
||||||
#include "socket.h"
|
#include "sock.h"
|
||||||
#include "game.h"
|
#include "data.h"
|
||||||
|
|
||||||
static void handle_signal(int signal_no) {
|
static void handle_signal(int signal_no) {
|
||||||
(void)signal_no;
|
(void)signal_no;
|
||||||
|
|
||||||
remove(SOCKET_PATH);
|
remove(SOCK_PATH);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static enum error_t simulation_thread_body(struct game_t *game) {
|
enum error_t game_loop(struct data_t *data) {
|
||||||
assert(game != NULL);
|
enum error_t err = ERR_OK;
|
||||||
|
|
||||||
// TODO: An actual game loop lol
|
// TEMP
|
||||||
while (true) {
|
struct response_body_get_world_data_t response_body = { data->world };
|
||||||
if (pthread_mutex_lock(&game->world_lock) != 0) goto error;
|
struct response_t response = { true, REQUEST_GET_WORLD_DATA, &response_body };
|
||||||
game->world.tick += 1;
|
|
||||||
if (pthread_mutex_unlock(&game->world_lock) != 0) goto error;
|
|
||||||
|
|
||||||
sleep(1);
|
char const *response_json_str = NULL;
|
||||||
}
|
err = response_serialise_str(&response, &response_json_str);
|
||||||
|
if (err) return err;
|
||||||
|
|
||||||
return ERR_OK;
|
printf("serialised response: %s\n", response_json_str);
|
||||||
|
|
||||||
error:
|
struct response_t response2 = { 0 };
|
||||||
return ERR_MUTEX;
|
err = response_deserialise_str(&response2, response_json_str);
|
||||||
}
|
if (err) return err;
|
||||||
|
|
||||||
|
struct response_body_get_world_data_t *response2_body = response2.body;
|
||||||
|
|
||||||
static enum error_t simulation_thread(
|
char const *world_json_str = NULL;
|
||||||
pthread_t *pthread,
|
err = world_serialise_str(&response2_body->world, &world_json_str);
|
||||||
struct game_t *data
|
if (err) return err;
|
||||||
) {
|
printf("deserialised response: (%d, %d, %s\n",
|
||||||
assert(pthread != NULL);
|
response2.success,
|
||||||
assert(data != NULL);
|
response2.type,
|
||||||
|
world_json_str
|
||||||
int err = pthread_create(
|
|
||||||
pthread, NULL,
|
|
||||||
(void *(*)(void *))simulation_thread_body, data
|
|
||||||
);
|
);
|
||||||
if (err != 0) return ERR_THREAD;
|
free((void *)response_json_str);
|
||||||
|
|
||||||
|
// TEMP
|
||||||
|
|
||||||
|
char obuf[8192] = { 0 };
|
||||||
|
err = world_serialise_buf(&data->world, obuf, 8192);
|
||||||
|
if (err != ERR_OK) return err;
|
||||||
|
|
||||||
|
write(data->socket_accept, obuf, 8192);
|
||||||
|
|
||||||
|
char ibuf[8192] = { 0 };
|
||||||
|
read(data->socket_accept, ibuf, 8192);
|
||||||
|
|
||||||
|
err = world_deserialise_str(&data->world, ibuf);
|
||||||
|
if (err != ERR_OK) return err;
|
||||||
|
|
||||||
|
printf("SERVER: \"%s\" -> \"%s\"\n", obuf, ibuf);
|
||||||
|
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
@ -63,8 +75,7 @@ static enum error_t simulation_thread(
|
|||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
// Set up variables
|
// Set up variables
|
||||||
enum error_t err = ERR_OK;
|
enum error_t err = ERR_OK;
|
||||||
struct game_t data = { 0 };
|
struct data_t data = { 0 };
|
||||||
|
|
||||||
|
|
||||||
// Signal handling; TODO: should probably improve this
|
// Signal handling; TODO: should probably improve this
|
||||||
signal(SIGINT, handle_signal);
|
signal(SIGINT, handle_signal);
|
||||||
@ -83,13 +94,6 @@ int main(int argc, char **argv) {
|
|||||||
err = world_init(&data.world, 10, 10);
|
err = world_init(&data.world, 10, 10);
|
||||||
if (err) goto handle_error;
|
if (err) goto handle_error;
|
||||||
|
|
||||||
// Load mods (yes, after world_init() though that sounds really wrong
|
|
||||||
// and most definitely should be changed)
|
|
||||||
err = game_load_mods(&data);
|
|
||||||
if (err) goto handle_error;
|
|
||||||
|
|
||||||
pthread_mutex_init(&data.world_lock, NULL);
|
|
||||||
|
|
||||||
err = world_register_entity(&data.world, "john", 'j');
|
err = world_register_entity(&data.world, "john", 'j');
|
||||||
if (err) goto handle_error_world;
|
if (err) goto handle_error_world;
|
||||||
|
|
||||||
@ -102,34 +106,16 @@ int main(int argc, char **argv) {
|
|||||||
entity_init(&data.world.entities[26], 2, 2, 6);
|
entity_init(&data.world.entities[26], 2, 2, 6);
|
||||||
// Make a gap in the entity array to see if "ghost" entities remain
|
// Make a gap in the entity array to see if "ghost" entities remain
|
||||||
|
|
||||||
// Socket handler thread
|
// Socket handling and run gameloop
|
||||||
pthread_t pthread_socket;
|
err = sock_loop(&data, game_loop);
|
||||||
err = socket_thread(&pthread_socket, &data);
|
if (err) goto handle_error;
|
||||||
if (err) goto handle_error_world;
|
|
||||||
|
|
||||||
// Simulation thread
|
|
||||||
pthread_t pthread_simulation;
|
|
||||||
err = simulation_thread(&pthread_simulation, &data);
|
|
||||||
if (err) goto handle_error_world;
|
|
||||||
|
|
||||||
// Join threads
|
|
||||||
// TODO: A way to shut down the program properly
|
|
||||||
pthread_join(pthread_socket, (void **)&err); // No way this is correct lol
|
|
||||||
if (err) goto handle_error_world;
|
|
||||||
|
|
||||||
pthread_join(pthread_simulation, (void **)&err);
|
|
||||||
if (err) goto handle_error_world;
|
|
||||||
|
|
||||||
// Deinitialisation
|
// Deinitialisation
|
||||||
pthread_mutex_destroy(&data.world_lock);
|
|
||||||
|
|
||||||
world_free(&data.world);
|
world_free(&data.world);
|
||||||
opts_free(&data.options);
|
opts_free(&data.options);
|
||||||
|
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
|
|
||||||
handle_error_world:
|
handle_error_world:
|
||||||
pthread_mutex_destroy(&data.world_lock);
|
|
||||||
world_free(&data.world);
|
world_free(&data.world);
|
||||||
|
|
||||||
handle_error:
|
handle_error:
|
||||||
@ -137,6 +123,6 @@ handle_error:
|
|||||||
opts_free(&data.options);
|
opts_free(&data.options);
|
||||||
|
|
||||||
handle_error_pre_opts:
|
handle_error_pre_opts:
|
||||||
write_log(data.options.daemonise, "SERVER ERROR: %s\n", ERROR_STRS[err]);
|
printf("SERVER ERROR: %s\n", ERROR_STRS[err]);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/syslog.h>
|
#include <sys/syslog.h>
|
||||||
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
@ -12,29 +11,22 @@
|
|||||||
|
|
||||||
void opts_default(struct options_t *options) {
|
void opts_default(struct options_t *options) {
|
||||||
options->daemonise = false;
|
options->daemonise = false;
|
||||||
|
|
||||||
// TODO: Decide on a real default mods directory
|
|
||||||
options->mods_directory = calloc(4096, sizeof(char));
|
|
||||||
getcwd(options->mods_directory, 4096);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
enum error_t opts_parse(struct options_t *options, int argc, char **argv) {
|
enum error_t opts_parse(struct options_t *options, int argc, char **argv) {
|
||||||
struct option const long_options[] = {
|
struct option const long_options[] = {
|
||||||
{ "daemon", no_argument, 0, 'd' },
|
{ "daemon", no_argument, 0, 'd' },
|
||||||
{ "mods-directory", required_argument, 0, 'm' },
|
|
||||||
{ NULL, 0, 0, 0 }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
|
|
||||||
int c = getopt_long(argc, argv, "dm:", long_options, &option_index);
|
int c = getopt_long(argc, argv, "d", long_options, &option_index);
|
||||||
if (c == -1) break;
|
if (c == -1) break;
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'd': options->daemonise = true; break;
|
case 'd': options->daemonise = true; break;
|
||||||
case 'm': options->mods_directory = optarg; break;
|
|
||||||
default: return ERR_INPUT;
|
default: return ERR_INPUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
#include <error.h>
|
#include <error.h>
|
||||||
|
|
||||||
struct options_t {
|
struct options_t {
|
||||||
char* mods_directory;
|
|
||||||
bool daemonise;
|
bool daemonise;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,63 +1,3 @@
|
|||||||
// request.c
|
// request.c
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include "request.h"
|
#include "request.h"
|
||||||
|
|
||||||
static struct response_t response_failure(
|
|
||||||
enum request_type_t request
|
|
||||||
) {
|
|
||||||
return (struct response_t){ request, false, NULL };
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_request_get_world_data(
|
|
||||||
struct response_t *response,
|
|
||||||
struct request_t const *request,
|
|
||||||
struct game_t *ctx
|
|
||||||
) {
|
|
||||||
assert(response != NULL);
|
|
||||||
assert(request != NULL);
|
|
||||||
assert(ctx != NULL);
|
|
||||||
|
|
||||||
// TODO: World creation isn't set up yet, so the world-id parameter isn't
|
|
||||||
// useful right now, but make sure to use it later when it is set up
|
|
||||||
|
|
||||||
struct response_body_get_world_data_t *body =
|
|
||||||
malloc(sizeof(struct response_body_get_world_data_t));
|
|
||||||
if (body == NULL) goto error;
|
|
||||||
|
|
||||||
response->body = body;
|
|
||||||
|
|
||||||
if (pthread_mutex_lock(&ctx->world_lock) != 0) goto error;
|
|
||||||
body->world = ctx->world;
|
|
||||||
if (pthread_mutex_unlock(&ctx->world_lock) != 0) goto error;
|
|
||||||
|
|
||||||
response->type = request->type;
|
|
||||||
response->success = true;
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
error:
|
|
||||||
free(body);
|
|
||||||
*response = response_failure(request->type);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct response_t handle_request(
|
|
||||||
struct request_t const *request,
|
|
||||||
struct game_t *ctx
|
|
||||||
) {
|
|
||||||
assert(request != NULL);
|
|
||||||
assert(ctx != NULL);
|
|
||||||
|
|
||||||
struct response_t response = { 0 };
|
|
||||||
|
|
||||||
switch (request->type) {
|
|
||||||
case REQUEST_GET_WORLD_DATA:
|
|
||||||
handle_request_get_world_data(&response, request, ctx);
|
|
||||||
return response;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return response_failure(request->type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,13 +1,8 @@
|
|||||||
#ifndef REQUEST_H
|
#ifndef REQUEST_H
|
||||||
#define REQUEST_H
|
#define REQUEST_H
|
||||||
|
|
||||||
#include <request.h>
|
#include <error.h>
|
||||||
|
|
||||||
#include "game.h"
|
enum error_t handle_request(void);
|
||||||
|
|
||||||
struct response_t handle_request(
|
|
||||||
struct request_t const *,
|
|
||||||
struct game_t *
|
|
||||||
);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
74
server/src/sock.c
Normal file
74
server/src/sock.c
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// sock.c
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
17
server/src/sock.h
Normal file
17
server/src/sock.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef SOCK_H
|
||||||
|
#define SOCK_H
|
||||||
|
|
||||||
|
#include <error.h>
|
||||||
|
|
||||||
|
#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
|
@ -1,125 +0,0 @@
|
|||||||
// socket.c
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/un.h>
|
|
||||||
|
|
||||||
#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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: Out of curiosity, what's the consensus on struct declarations outside
|
|
||||||
// of header files? Its a private struct that the end-user should never need to
|
|
||||||
// use, so I don't want to include it in socket.h, but it feels off putting it
|
|
||||||
// in socket.c as well just because I'm used to them all being in .h
|
|
||||||
struct socket_data_t {
|
|
||||||
struct game_t *game_data;
|
|
||||||
int socket;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static enum error_t socket_handle(struct socket_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, 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->game_data);
|
|
||||||
err = response_serialise_buf(&response, output_buffer, 8192);
|
|
||||||
if (err != ERR_OK) return err;
|
|
||||||
|
|
||||||
write(data->socket, output_buffer, 8192);
|
|
||||||
|
|
||||||
return ERR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static enum error_t socket_thread_body(struct game_t *data) {
|
|
||||||
assert(data != NULL);
|
|
||||||
|
|
||||||
enum error_t err = ERR_OK;
|
|
||||||
|
|
||||||
struct socket_data_t socket_data = { data, 0 };
|
|
||||||
|
|
||||||
int socket = 0;
|
|
||||||
if ((err = socket_init(&socket))) return err;
|
|
||||||
|
|
||||||
for (int sock_accept;;) {
|
|
||||||
if ((sock_accept = accept(socket, NULL, NULL)) < 0) {
|
|
||||||
err = ERR_SOCKET;
|
|
||||||
goto error_pre;
|
|
||||||
};
|
|
||||||
|
|
||||||
socket_data.socket = sock_accept;
|
|
||||||
if ((err = socket_handle(&socket_data))) goto error_pre;
|
|
||||||
|
|
||||||
close(socket_data.socket);
|
|
||||||
continue;
|
|
||||||
|
|
||||||
error_pre:
|
|
||||||
close(socket_data.socket);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
error:
|
|
||||||
socket_free(&socket);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum error_t socket_thread(pthread_t *pthread, struct game_t *data) {
|
|
||||||
assert(pthread != NULL);
|
|
||||||
assert(data != NULL);
|
|
||||||
|
|
||||||
int err = pthread_create(
|
|
||||||
pthread, NULL,
|
|
||||||
(void *(*)(void *))socket_thread_body, // I want to cry tf is this
|
|
||||||
data
|
|
||||||
);
|
|
||||||
if (err != 0) return ERR_THREAD;
|
|
||||||
|
|
||||||
return ERR_OK;
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
#ifndef SOCKET_H
|
|
||||||
#define SOCKET_H
|
|
||||||
|
|
||||||
#include <error.h>
|
|
||||||
|
|
||||||
#include "game.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_t *);
|
|
||||||
|
|
||||||
#endif
|
|
Loading…
Reference in New Issue
Block a user