Compare commits
10 Commits
c32af03d20
...
b3261454d9
Author | SHA1 | Date | |
---|---|---|---|
b3261454d9 | |||
4f93992fba | |||
f702065a7c | |||
b03e194a34 | |||
c892acab80 | |||
92fed87080 | |||
307323e561 | |||
693c238664 | |||
bb71248414 | |||
a87b6c1152 |
@ -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=c17 -Wpedantic -pedantic-errors -Wformat=2 -Wshadow -Wwrite-strings -Wstrict-prototypes -g")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -std=gnu17 -Wpedantic -pedantic-errors -Wformat=2 -Wshadow -Wwrite-strings -Wstrict-prototypes -g")
|
||||||
|
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
@ -10,3 +10,5 @@ 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,9 +4,11 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
||||||
#include <world.h>
|
|
||||||
#include <error.h>
|
#include <error.h>
|
||||||
|
#include <request.h>
|
||||||
|
#include <world.h>
|
||||||
|
|
||||||
|
#include "render/render.h"
|
||||||
#include "sock.h"
|
#include "sock.h"
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
@ -16,21 +18,33 @@ int main(void) {
|
|||||||
err = sock_init(&sock);
|
err = sock_init(&sock);
|
||||||
if (err) goto error;
|
if (err) goto error;
|
||||||
|
|
||||||
char ibuf[8192] = { 0 };
|
char input_buffer[8192] = { 0 };
|
||||||
read(sock, ibuf, 8192);
|
char output_buffer[8192] = { 0 };
|
||||||
|
|
||||||
struct world_t world = { 0 };
|
// Send request for world data to server
|
||||||
err = world_deserialise_str(&world, ibuf);
|
struct request_body_get_world_data_t request_body = { 420 };
|
||||||
|
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;
|
||||||
|
|
||||||
printf("CLIENT: (%zu %zu) ->", world.height++, world.width ++);
|
write(sock, output_buffer, 8192);
|
||||||
printf(" (%zu %zu)\n", world.height, world.width);
|
|
||||||
|
|
||||||
char obuf[8192] = { 0 };
|
// Get response from server
|
||||||
err = world_serialise_buf(&world, obuf, 8192);
|
read(sock, input_buffer, 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;
|
||||||
|
|
||||||
write(sock, obuf, 8192);
|
// Render data
|
||||||
|
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,11 +1,16 @@
|
|||||||
// 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,8 +11,10 @@ target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/common/inc
|
|||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
|
|
||||||
pkg_check_modules(LUA REQUIRED lua)
|
pkg_check_modules(LUA REQUIRED lua5.4)
|
||||||
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,6 +1,10 @@
|
|||||||
#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,
|
||||||
@ -11,6 +15,10 @@ 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,11 +2,10 @@
|
|||||||
#define COMMON_REQUEST_H
|
#define COMMON_REQUEST_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "world.h"
|
#include "request/get_world_data.h"
|
||||||
|
|
||||||
// ### REQUEST TYPES ### //
|
|
||||||
|
|
||||||
enum request_type_t {
|
enum request_type_t {
|
||||||
REQUEST_NONE,
|
REQUEST_NONE,
|
||||||
@ -14,11 +13,6 @@ 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;
|
||||||
@ -32,13 +26,6 @@ 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;
|
||||||
|
41
common/include/request/get_world_data.h
Normal file
41
common/include/request/get_world_data.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#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,6 +15,8 @@ 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,6 +12,10 @@ char const *const ERROR_STRS[] = {
|
|||||||
"SOCKET",
|
"SOCKET",
|
||||||
"JSON SERIALISATION",
|
"JSON SERIALISATION",
|
||||||
"JSON DESERIALISATION",
|
"JSON DESERIALISATION",
|
||||||
|
"INVALID REQUEST",
|
||||||
|
"REQUEST FAILED",
|
||||||
|
"THREAD INITIALISATION",
|
||||||
|
"MUTEX ERROR",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,34 +5,9 @@
|
|||||||
|
|
||||||
#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,
|
||||||
@ -43,7 +18,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_serialise_body_get_world_data(self->body, jsonptr);
|
return request_body_get_world_data_serialise(self->body, jsonptr);
|
||||||
|
|
||||||
default: return ERR_JSON_SERIALISE;
|
default: return ERR_JSON_SERIALISE;
|
||||||
}
|
}
|
||||||
@ -162,22 +137,6 @@ 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
|
||||||
@ -190,7 +149,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_deserialise_body_get_world_data(self->body, json);
|
return request_body_get_world_data_deserialise(self->body, json);
|
||||||
|
|
||||||
default: return ERR_JSON_SERIALISE;
|
default: return ERR_JSON_SERIALISE;
|
||||||
}
|
}
|
||||||
@ -241,22 +200,6 @@ 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
|
||||||
@ -266,7 +209,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_serialise_body_get_world_data(self->body, jsonptr);
|
return response_body_get_world_data_serialise(self->body, jsonptr);
|
||||||
|
|
||||||
default: return ERR_JSON_SERIALISE;
|
default: return ERR_JSON_SERIALISE;
|
||||||
}
|
}
|
||||||
@ -393,20 +336,6 @@ 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
|
||||||
@ -419,7 +348,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_deserialise_body_get_world_data(self->body, json);
|
return response_body_get_world_data_deserialise(self->body, json);
|
||||||
|
|
||||||
default: return ERR_JSON_SERIALISE;
|
default: return ERR_JSON_SERIALISE;
|
||||||
}
|
}
|
||||||
|
71
common/src/request/get_world_data.c
Normal file
71
common/src/request/get_world_data.c
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
// 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,12 +5,14 @@
|
|||||||
|
|
||||||
#include "world.h"
|
#include "world.h"
|
||||||
|
|
||||||
static char const *const WORLD_JSON_FMT = "{so, so, sI, sI}";
|
static char const *const WORLD_JSON_FMT = "{so, so, sI, 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;
|
||||||
|
|
||||||
@ -84,7 +86,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 height, size_t width,
|
size_t tick, size_t height, size_t width,
|
||||||
struct json_t **json
|
struct json_t **json
|
||||||
) {
|
) {
|
||||||
assert(entities_json != NULL);
|
assert(entities_json != NULL);
|
||||||
@ -94,7 +96,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,
|
||||||
"height", height, "width", width
|
"tick", tick, "height", height, "width", width
|
||||||
);
|
);
|
||||||
|
|
||||||
if (world_json == NULL) return ERR_JSON_SERIALISE;
|
if (world_json == NULL) return ERR_JSON_SERIALISE;
|
||||||
@ -131,7 +133,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->height, self->width,
|
self->tick, self->height, self->width,
|
||||||
&world_json
|
&world_json
|
||||||
);
|
);
|
||||||
if (err != ERR_OK) goto error_world;
|
if (err != ERR_OK) goto error_world;
|
||||||
@ -150,7 +152,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 *height, size_t *width,
|
size_t *tick, size_t *height, size_t *width,
|
||||||
struct json_t *json
|
struct json_t *json
|
||||||
) {
|
) {
|
||||||
assert(entities_json != NULL);
|
assert(entities_json != NULL);
|
||||||
@ -162,7 +164,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,
|
||||||
"height", height, "width", width
|
"tick", tick, "height", height, "width", width
|
||||||
);
|
);
|
||||||
|
|
||||||
if (err < 0) return ERR_JSON_DESERIALISE;
|
if (err < 0) return ERR_JSON_DESERIALISE;
|
||||||
@ -230,7 +232,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.height, &temp_world.width,
|
&temp_world.tick, &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,12 +2,18 @@
|
|||||||
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
|
||||||
@ -19,14 +25,13 @@ A list of things I'd like to accomplish
|
|||||||
* Write Makefile to automate compilation
|
* Write Makefile to automate compilation
|
||||||
|
|
||||||
## Planned
|
## Planned
|
||||||
* Split game ticking and socket connecting logic (can only tick on connection atm)
|
* Requests/responses for more than just the complete set of world data all at once
|
||||||
* 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
|
||||||
|
3
mods/core/init.lua
Normal file
3
mods/core/init.lua
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
-- init.lua
|
||||||
|
|
||||||
|
print("Hello, world!")
|
6
mods/core/mod.json
Normal file
6
mods/core/mod.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "core",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"depends": [],
|
||||||
|
"optdepends": []
|
||||||
|
}
|
@ -7,16 +7,40 @@ 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 &
|
./build/server/simworld-daemon ${server_args} &
|
||||||
sleep 1
|
sleep 1
|
||||||
|
|
||||||
echo "### CLIENT 1 ###"
|
echo "### CLIENT 1 ###"
|
||||||
./build/client/simworld-client
|
./build/client/simworld-client ${client1_args}
|
||||||
|
|
||||||
echo "### CLIENT 2 ###"
|
echo "### CLIENT 2 ###"
|
||||||
./build/client/simworld-client
|
./build/client/simworld-client ${client2_args}
|
||||||
|
|
||||||
|
echo "### CLIENT 3 ###"
|
||||||
|
sleep 1
|
||||||
|
./build/client/simworld-client ${client3_args}
|
||||||
|
|
||||||
killall simworld-daemon
|
killall simworld-daemon
|
||||||
|
@ -9,3 +9,5 @@ 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})
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
#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
|
|
67
server/src/game.c
Normal file
67
server/src/game.c
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// 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;
|
||||||
|
}
|
32
server/src/game.h
Normal file
32
server/src/game.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#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,5 +1,6 @@
|
|||||||
// 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>
|
||||||
@ -12,61 +13,48 @@
|
|||||||
#include <request.h>
|
#include <request.h>
|
||||||
|
|
||||||
#include "opts.h"
|
#include "opts.h"
|
||||||
#include "sock.h"
|
#include "socket.h"
|
||||||
#include "data.h"
|
#include "game.h"
|
||||||
|
|
||||||
static void handle_signal(int signal_no) {
|
static void handle_signal(int signal_no) {
|
||||||
(void)signal_no;
|
(void)signal_no;
|
||||||
|
|
||||||
remove(SOCK_PATH);
|
remove(SOCKET_PATH);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
enum error_t game_loop(struct data_t *data) {
|
static enum error_t simulation_thread_body(struct game_t *game) {
|
||||||
enum error_t err = ERR_OK;
|
assert(game != NULL);
|
||||||
|
|
||||||
// TEMP
|
// TODO: An actual game loop lol
|
||||||
struct response_body_get_world_data_t response_body = { data->world };
|
while (true) {
|
||||||
struct response_t response = { true, REQUEST_GET_WORLD_DATA, &response_body };
|
if (pthread_mutex_lock(&game->world_lock) != 0) goto error;
|
||||||
|
game->world.tick += 1;
|
||||||
|
if (pthread_mutex_unlock(&game->world_lock) != 0) goto error;
|
||||||
|
|
||||||
char const *response_json_str = NULL;
|
sleep(1);
|
||||||
err = response_serialise_str(&response, &response_json_str);
|
}
|
||||||
if (err) return err;
|
|
||||||
|
|
||||||
printf("serialised response: %s\n", response_json_str);
|
return ERR_OK;
|
||||||
|
|
||||||
struct response_t response2 = { 0 };
|
error:
|
||||||
err = response_deserialise_str(&response2, response_json_str);
|
return ERR_MUTEX;
|
||||||
if (err) return err;
|
}
|
||||||
|
|
||||||
struct response_body_get_world_data_t *response2_body = response2.body;
|
|
||||||
|
|
||||||
char const *world_json_str = NULL;
|
static enum error_t simulation_thread(
|
||||||
err = world_serialise_str(&response2_body->world, &world_json_str);
|
pthread_t *pthread,
|
||||||
if (err) return err;
|
struct game_t *data
|
||||||
printf("deserialised response: (%d, %d, %s\n",
|
) {
|
||||||
response2.success,
|
assert(pthread != NULL);
|
||||||
response2.type,
|
assert(data != NULL);
|
||||||
world_json_str
|
|
||||||
|
int err = pthread_create(
|
||||||
|
pthread, NULL,
|
||||||
|
(void *(*)(void *))simulation_thread_body, data
|
||||||
);
|
);
|
||||||
free((void *)response_json_str);
|
if (err != 0) return ERR_THREAD;
|
||||||
|
|
||||||
// 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;
|
||||||
}
|
}
|
||||||
@ -75,7 +63,8 @@ enum error_t game_loop(struct data_t *data) {
|
|||||||
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 data_t data = { 0 };
|
struct game_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);
|
||||||
@ -94,6 +83,13 @@ 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;
|
||||||
|
|
||||||
@ -106,16 +102,34 @@ 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 handling and run gameloop
|
// Socket handler thread
|
||||||
err = sock_loop(&data, game_loop);
|
pthread_t pthread_socket;
|
||||||
if (err) goto handle_error;
|
err = socket_thread(&pthread_socket, &data);
|
||||||
|
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:
|
||||||
@ -123,6 +137,6 @@ handle_error:
|
|||||||
opts_free(&data.options);
|
opts_free(&data.options);
|
||||||
|
|
||||||
handle_error_pre_opts:
|
handle_error_pre_opts:
|
||||||
printf("SERVER ERROR: %s\n", ERROR_STRS[err]);
|
write_log(data.options.daemonise, "SERVER ERROR: %s\n", ERROR_STRS[err]);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#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>
|
||||||
@ -11,22 +12,29 @@
|
|||||||
|
|
||||||
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, "d", long_options, &option_index);
|
int c = getopt_long(argc, argv, "dm:", 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,6 +6,7 @@
|
|||||||
#include <error.h>
|
#include <error.h>
|
||||||
|
|
||||||
struct options_t {
|
struct options_t {
|
||||||
|
char* mods_directory;
|
||||||
bool daemonise;
|
bool daemonise;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,3 +1,63 @@
|
|||||||
// 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,8 +1,13 @@
|
|||||||
#ifndef REQUEST_H
|
#ifndef REQUEST_H
|
||||||
#define REQUEST_H
|
#define REQUEST_H
|
||||||
|
|
||||||
#include <error.h>
|
#include <request.h>
|
||||||
|
|
||||||
enum error_t handle_request(void);
|
#include "game.h"
|
||||||
|
|
||||||
|
struct response_t handle_request(
|
||||||
|
struct request_t const *,
|
||||||
|
struct game_t *
|
||||||
|
);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
// 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;
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
#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
|
|
125
server/src/socket.c
Normal file
125
server/src/socket.c
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
// 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;
|
||||||
|
}
|
17
server/src/socket.h
Normal file
17
server/src/socket.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#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