serialise and deserialise very basic json data, and send it between server and client

This commit is contained in:
snit 2024-10-10 00:58:13 -05:00
parent f1b30b697e
commit 8d5fc464cb
11 changed files with 123 additions and 45 deletions

View File

@ -5,8 +5,12 @@ Note: I'm not galaxy-brained enough to do the full idea at the moment, so this i
## Build from Source
### Dependencies
#### All
* CMake
#### Common Library
* Lua 5.4
* Jansson
### Building
```

View File

@ -1,10 +1,10 @@
// main.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include "world.h"
#include "error.h"
#include "sock.h"
@ -15,15 +15,25 @@ int main(void) {
err = sock_init(&sock);
if (err) goto handle_error;
char ibuf[1028] = { 0 };
char obuf[1028] = { 0 };
strcpy(obuf, "Hello, world!\n");
char ibuf[1024] = { 0 };
char obuf[1024] = { 0 };
// Just echoing everything back for now
write(sock, obuf, 1028);
int rc = read(sock, ibuf, 1028);
// TODO: I skipped out on most error handling here
read(sock, ibuf, 1024);
if (rc > 0) printf("%s", ibuf);
struct json_error_t json_err;
struct json_t *json = json_loads(ibuf, 0, &json_err);
struct world_t world = { 0 };
world_deserialise(&world, json);
printf("CLIENT: (%zu %zu)\n", world.height, world.width);
struct json_t *new_json;
world_serialise(&world, &new_json);
json_dumpb(new_json, obuf, 1024, 0);
write(sock, obuf, 1024);
close(sock);
return ERR_OK;

View File

@ -16,8 +16,7 @@ enum error_t sock_init(int *sockptr) {
struct sockaddr_un sa = { 0 };
sa.sun_family = AF_UNIX;
strcpy(sa.sun_path, "/tmp/swd.sock");
// Socket path should be a shared setting between server and client
strcpy(sa.sun_path, SOCK_PATH);
if (connect(sock, (struct sockaddr *)&sa, sizeof(sa))) goto sock_error;

View File

@ -3,6 +3,8 @@
#include "error.h"
#define SOCK_PATH "/tmp/swd.sock"
enum error_t sock_init(int *);
#endif

View File

@ -10,7 +10,9 @@ add_library(${PROJECT_NAME} SHARED ${SOURCES})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/common/include)
find_package(PkgConfig REQUIRED)
pkg_check_modules(LUA REQUIRED lua)
target_link_libraries(${PROJECT_NAME} ${LUA_LIBRARIES})
target_include_directories(${PROJECT_NAME} PRIVATE ${LUA_INCLUDE_DIRS})
pkg_check_modules(LUA REQUIRED lua)
pkg_check_modules(JANSSON REQUIRED jansson)
target_link_libraries(${PROJECT_NAME} ${LUA_LIBRARIES} ${JANSSON_LIBRARIES})
target_include_directories(${PROJECT_NAME} PRIVATE ${LUA_INCLUDE_DIRS} ${JANSSON_INCLUDE_DIRS})

View File

@ -9,6 +9,8 @@ enum error_t {
ERR_FORK,
ERR_SETSID,
ERR_SOCKET,
ERR_JSON_SERIALISE,
ERR_JSON_DESERIALISE,
__ERR_COUNT,
};

View File

@ -3,6 +3,8 @@
#include <stdlib.h>
#include <jansson.h>
#include "error.h"
#include "entity/registry.h"
#include "entity/entity.h"
@ -20,6 +22,20 @@ struct world_t {
enum error_t world_init(struct world_t *, size_t, size_t);
void world_free(struct world_t *);
// json_t doesn't store the json data itself; the value of the pointer is
// apparently needed for jansson to use the data, so I need a double pointer
//
// TODO
// Right now, to serialise and deserialise again you have to call:
// 1. world_serialise() | world_t -> json_t
// 2. json_dumpX() | json_t -> char *
// 3. json_loadX() | char * -> json_t
// 4. world_deserialise() | json_t -> world_t
//
// Perhaps these two functions should go directly between world_t and char *
enum error_t world_serialise(struct world_t *, struct json_t **);
enum error_t world_deserialise(struct world_t *, struct json_t *);
enum error_t world_register_entity(struct world_t *, char const *, char);
enum error_t world_place_entity(struct world_t *, size_t, size_t, size_t);

View File

@ -10,6 +10,8 @@ char const *const ERROR_STRS[] = {
"FORK",
"SETSID",
"SOCKET",
"JSON SERIALISATION",
"JSON DESERIALISATION",
};

View File

@ -34,14 +34,50 @@ enum error_t world_init(struct world_t *self, size_t height, size_t width) {
void world_free(struct world_t *self) {
assert(self != NULL);
entity_registry_free(&self->registered_entities);
}
enum error_t world_serialise(struct world_t *self, struct json_t **json) {
assert(self != NULL);
// TODO: (de)serialise the following:
// struct entity_t entities[MAX_ENTITIES];
// struct entity_registry_t registered_entities;
struct json_t *maybe_json = json_pack("{s:I,s:I}",
"height", self->height,
"width", self->width
);
if (maybe_json == NULL) return ERR_JSON_SERIALISE;
*json = maybe_json;
return ERR_OK;
}
enum error_t world_deserialise(struct world_t *self, struct json_t *json) {
assert(self != NULL);
assert(json != NULL);
int err = json_unpack(json, "{s:I,s:I}",
"height", &self->height,
"width", &self->width
);
if (err < 0) return ERR_JSON_DESERIALISE;
return ERR_OK;
}
enum error_t world_register_entity(
struct world_t *self,
char const *name, char tile
) {
assert(self != NULL);
assert(name != NULL);
enum error_t err;
struct entity_registry_t *registry = &self->registered_entities;
@ -57,6 +93,8 @@ enum error_t world_register_entity(
static size_t world_find_empty_entity(struct world_t const *self) {
assert(self != NULL);
for (size_t i = 0; i < MAX_ENTITIES; i++)
if (self->entities[i].id == 0) return i;

View File

@ -2,8 +2,10 @@
#define DAEMON_DATA_H
#include "world.h"
#include "opts.h"
struct data_t {
struct options_t options;
struct world_t world;
int socket;

View File

@ -22,11 +22,34 @@ static void handle_signal(int signal_no) {
}
enum error_t game_loop(struct data_t *data) {
uint8_t buffer[1024] = { 0 };
#define LOGMSG(options, fmt, ...) \
((options)->daemonise ? \
syslog(LOG_NOTICE, (fmt), __VA_ARGS__) : \
(void)printf((fmt), __VA_ARGS__))
read(data->socket_accept, buffer, 1024);
write(data->socket_accept, buffer, 1024);
enum error_t game_loop(struct data_t *data) {
enum error_t err = ERR_OK;
char obuf[1024] = { 0 };
char ibuf[1024] = { 0 };
// TODO: I skipped out on most error handling here
struct json_t *json;
err = world_serialise(&data->world, &json);
if (err != ERR_OK) return err;
json_dumpb(json, obuf, 1024, 0);
write(data->socket_accept, obuf, 1024);
read(data->socket_accept, ibuf, 1024);
json = json_loads(ibuf, 0, NULL);
struct world_t returned_world;
world_deserialise(&returned_world, json);
LOGMSG(&data->options, "SERVER: (%zu %zu) -> (%zu %zu)\n",
data->world.height, data->world.width,
returned_world.height, returned_world.width
);
return ERR_OK;
}
@ -42,13 +65,12 @@ int main(int argc, char **argv) {
signal(SIGTERM, handle_signal);
// Handle commandline options
struct options_t options = { 0 };
opts_default(&options);
opts_default(&data.options);
err = opts_parse(&options, argc, argv);
err = opts_parse(&data.options, argc, argv);
if (err) goto handle_error_pre_opts;
err = opts_init(&options);
err = opts_init(&data.options);
if (err) goto handle_error;
// World initialisation
@ -61,42 +83,21 @@ int main(int argc, char **argv) {
err = world_place_entity(&data.world, 1, 3, 7);
if (err) goto handle_error_world;
// Temporary code because I just realised I need to properly serialise
// data because pointers don't work cross-socket
// TODO: Serialise to JSON
const char *fmt = "%zu (%s) (%zu, %zu) '%c'\n";
struct entity_t const *const entity = &data.world.entities[0];
struct entity_registrant_t const *const registrant =
&data.world.registered_entities.entities[entity->id];
if (options.daemonise)
syslog(LOG_NOTICE, fmt,
entity->id, registrant->name,
entity->x, entity->y,
registrant->tile
);
else
printf(fmt,
entity->id, registrant->name,
entity->x, entity->y,
registrant->tile
);
// Socket handling and run gameloop
err = sock_loop(&data, game_loop);
if (err) goto handle_error;
// Deinitialisation
world_free(&data.world);
opts_free(&options);
opts_free(&data.options);
return ERR_OK;
handle_error_world:
world_free(&data.world);
handle_error:
if (options.daemonise) syslog(LOG_ERR, "%s", ERROR_STRS[err]);
opts_free(&options);
if (data.options.daemonise) syslog(LOG_ERR, "%s", ERROR_STRS[err]);
opts_free(&data.options);
handle_error_pre_opts:
printf("ERROR: %s\n", ERROR_STRS[err]);