serialise and deserialise very basic json data, and send it between server and client
This commit is contained in:
parent
f1b30b697e
commit
8d5fc464cb
@ -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
|
## Build from Source
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
#### All
|
||||||
* CMake
|
* CMake
|
||||||
|
|
||||||
|
#### Common Library
|
||||||
* Lua 5.4
|
* Lua 5.4
|
||||||
|
* Jansson
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
```
|
```
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
// main.c
|
// main.c
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include "world.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "sock.h"
|
#include "sock.h"
|
||||||
|
|
||||||
@ -15,15 +15,25 @@ int main(void) {
|
|||||||
err = sock_init(&sock);
|
err = sock_init(&sock);
|
||||||
if (err) goto handle_error;
|
if (err) goto handle_error;
|
||||||
|
|
||||||
char ibuf[1028] = { 0 };
|
char ibuf[1024] = { 0 };
|
||||||
char obuf[1028] = { 0 };
|
char obuf[1024] = { 0 };
|
||||||
strcpy(obuf, "Hello, world!\n");
|
|
||||||
|
|
||||||
// Just echoing everything back for now
|
// TODO: I skipped out on most error handling here
|
||||||
write(sock, obuf, 1028);
|
read(sock, ibuf, 1024);
|
||||||
int rc = read(sock, ibuf, 1028);
|
|
||||||
|
|
||||||
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);
|
close(sock);
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
|
@ -16,8 +16,7 @@ enum error_t sock_init(int *sockptr) {
|
|||||||
|
|
||||||
struct sockaddr_un sa = { 0 };
|
struct sockaddr_un sa = { 0 };
|
||||||
sa.sun_family = AF_UNIX;
|
sa.sun_family = AF_UNIX;
|
||||||
strcpy(sa.sun_path, "/tmp/swd.sock");
|
strcpy(sa.sun_path, SOCK_PATH);
|
||||||
// Socket path should be a shared setting between server and client
|
|
||||||
|
|
||||||
if (connect(sock, (struct sockaddr *)&sa, sizeof(sa))) goto sock_error;
|
if (connect(sock, (struct sockaddr *)&sa, sizeof(sa))) goto sock_error;
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
|
||||||
|
#define SOCK_PATH "/tmp/swd.sock"
|
||||||
|
|
||||||
enum error_t sock_init(int *);
|
enum error_t sock_init(int *);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -10,7 +10,9 @@ add_library(${PROJECT_NAME} SHARED ${SOURCES})
|
|||||||
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/common/include)
|
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/common/include)
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_check_modules(LUA REQUIRED lua)
|
|
||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME} ${LUA_LIBRARIES})
|
pkg_check_modules(LUA REQUIRED lua)
|
||||||
target_include_directories(${PROJECT_NAME} PRIVATE ${LUA_INCLUDE_DIRS})
|
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})
|
||||||
|
@ -9,6 +9,8 @@ enum error_t {
|
|||||||
ERR_FORK,
|
ERR_FORK,
|
||||||
ERR_SETSID,
|
ERR_SETSID,
|
||||||
ERR_SOCKET,
|
ERR_SOCKET,
|
||||||
|
ERR_JSON_SERIALISE,
|
||||||
|
ERR_JSON_DESERIALISE,
|
||||||
__ERR_COUNT,
|
__ERR_COUNT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <jansson.h>
|
||||||
|
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "entity/registry.h"
|
#include "entity/registry.h"
|
||||||
#include "entity/entity.h"
|
#include "entity/entity.h"
|
||||||
@ -20,6 +22,20 @@ struct world_t {
|
|||||||
enum error_t world_init(struct world_t *, size_t, size_t);
|
enum error_t world_init(struct world_t *, size_t, size_t);
|
||||||
void world_free(struct world_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_register_entity(struct world_t *, char const *, char);
|
||||||
enum error_t world_place_entity(struct world_t *, size_t, size_t, size_t);
|
enum error_t world_place_entity(struct world_t *, size_t, size_t, size_t);
|
||||||
|
|
||||||
|
@ -10,6 +10,8 @@ char const *const ERROR_STRS[] = {
|
|||||||
"FORK",
|
"FORK",
|
||||||
"SETSID",
|
"SETSID",
|
||||||
"SOCKET",
|
"SOCKET",
|
||||||
|
"JSON SERIALISATION",
|
||||||
|
"JSON DESERIALISATION",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -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) {
|
void world_free(struct world_t *self) {
|
||||||
|
assert(self != NULL);
|
||||||
|
|
||||||
entity_registry_free(&self->registered_entities);
|
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(
|
enum error_t world_register_entity(
|
||||||
struct world_t *self,
|
struct world_t *self,
|
||||||
char const *name, char tile
|
char const *name, char tile
|
||||||
) {
|
) {
|
||||||
|
assert(self != NULL);
|
||||||
|
assert(name != NULL);
|
||||||
|
|
||||||
enum error_t err;
|
enum error_t err;
|
||||||
struct entity_registry_t *registry = &self->registered_entities;
|
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) {
|
static size_t world_find_empty_entity(struct world_t const *self) {
|
||||||
|
assert(self != NULL);
|
||||||
|
|
||||||
for (size_t i = 0; i < MAX_ENTITIES; i++)
|
for (size_t i = 0; i < MAX_ENTITIES; i++)
|
||||||
if (self->entities[i].id == 0) return i;
|
if (self->entities[i].id == 0) return i;
|
||||||
|
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
#define DAEMON_DATA_H
|
#define DAEMON_DATA_H
|
||||||
|
|
||||||
#include "world.h"
|
#include "world.h"
|
||||||
|
#include "opts.h"
|
||||||
|
|
||||||
struct data_t {
|
struct data_t {
|
||||||
|
struct options_t options;
|
||||||
struct world_t world;
|
struct world_t world;
|
||||||
|
|
||||||
int socket;
|
int socket;
|
||||||
|
@ -22,11 +22,34 @@ static void handle_signal(int signal_no) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
enum error_t game_loop(struct data_t *data) {
|
#define LOGMSG(options, fmt, ...) \
|
||||||
uint8_t buffer[1024] = { 0 };
|
((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;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
@ -42,13 +65,12 @@ int main(int argc, char **argv) {
|
|||||||
signal(SIGTERM, handle_signal);
|
signal(SIGTERM, handle_signal);
|
||||||
|
|
||||||
// Handle commandline options
|
// Handle commandline options
|
||||||
struct options_t options = { 0 };
|
opts_default(&data.options);
|
||||||
opts_default(&options);
|
|
||||||
|
|
||||||
err = opts_parse(&options, argc, argv);
|
err = opts_parse(&data.options, argc, argv);
|
||||||
if (err) goto handle_error_pre_opts;
|
if (err) goto handle_error_pre_opts;
|
||||||
|
|
||||||
err = opts_init(&options);
|
err = opts_init(&data.options);
|
||||||
if (err) goto handle_error;
|
if (err) goto handle_error;
|
||||||
|
|
||||||
// World initialisation
|
// World initialisation
|
||||||
@ -61,42 +83,21 @@ int main(int argc, char **argv) {
|
|||||||
err = world_place_entity(&data.world, 1, 3, 7);
|
err = world_place_entity(&data.world, 1, 3, 7);
|
||||||
if (err) goto handle_error_world;
|
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
|
// Socket handling and run gameloop
|
||||||
err = sock_loop(&data, game_loop);
|
err = sock_loop(&data, game_loop);
|
||||||
if (err) goto handle_error;
|
if (err) goto handle_error;
|
||||||
|
|
||||||
// Deinitialisation
|
// Deinitialisation
|
||||||
world_free(&data.world);
|
world_free(&data.world);
|
||||||
opts_free(&options);
|
opts_free(&data.options);
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
|
|
||||||
handle_error_world:
|
handle_error_world:
|
||||||
world_free(&data.world);
|
world_free(&data.world);
|
||||||
|
|
||||||
handle_error:
|
handle_error:
|
||||||
if (options.daemonise) syslog(LOG_ERR, "%s", ERROR_STRS[err]);
|
if (data.options.daemonise) syslog(LOG_ERR, "%s", ERROR_STRS[err]);
|
||||||
opts_free(&options);
|
opts_free(&data.options);
|
||||||
|
|
||||||
handle_error_pre_opts:
|
handle_error_pre_opts:
|
||||||
printf("ERROR: %s\n", ERROR_STRS[err]);
|
printf("ERROR: %s\n", ERROR_STRS[err]);
|
||||||
|
Loading…
Reference in New Issue
Block a user