refactor a lot of the code because it was getting messy

This commit is contained in:
snit 2024-10-13 15:04:35 -05:00
parent aab6b41d18
commit c31078ebd5
7 changed files with 452 additions and 152 deletions

View File

@ -2,11 +2,11 @@
A list of things I'd like to accomplish
## In Progress
* Serialise data to JSON for socket data transmission
* Send world data to client
* Have client render world data (return to pre-daemonised equivalent state)
## Completed
* Serialise data to JSON for socket data transmission
* Send world data to client
* Initialise a world again
* Expose a socket or pipe on the daemon for the client
* Create client to connect to daemon

View File

@ -19,7 +19,7 @@ int main(void) {
read(sock, ibuf, 8192);
struct world_t world = { 0 };
err = world_deserialise(&world, ibuf);
err = world_deserialise_str(&world, ibuf);
if (err != ERR_OK) goto handle_error_sock;
printf("CLIENT: (%zu %zu) ->", world.height++, world.width ++);

View File

@ -12,6 +12,7 @@ struct entity_registrant_t {
char tile;
};
enum error_t entity_registrant_init(
struct entity_registrant_t *,
char const *, char
@ -26,7 +27,7 @@ struct entity_registry_t {
};
enum error_t entity_registry_init(struct entity_registry_t *);
void entity_registry_init(struct entity_registry_t *);
void entity_registry_free(struct entity_registry_t *);
enum error_t entity_registry_serialise(
@ -39,6 +40,20 @@ enum error_t entity_registry_deserialise(
struct json_t *
);
enum error_t entity_registry_resize(struct entity_registry_t *, size_t);
void entity_registry_set(
struct entity_registry_t *,
struct entity_registrant_t const *,
size_t
);
enum error_t entity_registry_try_set(
struct entity_registry_t *,
struct entity_registrant_t const *,
size_t
);
enum error_t entity_registry_append(
struct entity_registry_t *,
struct entity_registrant_t const *

View File

@ -22,11 +22,12 @@ struct world_t {
enum error_t world_init(struct world_t *, size_t, size_t);
void world_free(struct world_t *);
// world_*_str() allocates a string that the caller must free()
// world_*_buf() uses a pre-existing buffer; results are not zero-terminated
enum error_t world_serialise_str(struct world_t *, char const **);
enum error_t world_deserialise_str(struct world_t *, char const *);
// Operates on strings; serialise allocates so make sure to free
enum error_t world_serialise_buf(struct world_t *, char *, size_t);
enum error_t world_deserialise(struct world_t *, char const *);
// Operates on buffers; need not be zero-terminated
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

@ -47,13 +47,13 @@ static enum error_t registrant_serialise(
assert(self != NULL);
assert(json != NULL);
struct json_t *maybe_json = json_pack(ENTITY_REGISTRANT_JSON_FMT,
struct json_t *registrant_json = json_pack(ENTITY_REGISTRANT_JSON_FMT,
"name", self->name,
"tile", self->tile
);
if (maybe_json == NULL) return ERR_JSON_SERIALISE;
if (registrant_json == NULL) return ERR_JSON_SERIALISE;
*json = maybe_json;
*json = registrant_json;
return ERR_OK;
}
@ -64,29 +64,29 @@ static enum error_t registrant_deserialise(
assert(self != NULL);
assert(json != NULL);
char const *name_borrowed = NULL;
int err = json_unpack(json, ENTITY_REGISTRANT_JSON_FMT,
"name", &self->name,
"name", &name_borrowed,
"tile", &self->tile
);
if (err < 0) return ERR_JSON_DESERIALISE;
char *name_owned = calloc(strlen(name_borrowed), sizeof(char));
if (name_owned == NULL) return ERR_ALLOC;
strcpy(name_owned, name_borrowed);
self->name = name_owned;
return ERR_OK;
}
enum error_t entity_registry_init(struct entity_registry_t *self) {
void entity_registry_init(struct entity_registry_t *self) {
assert(self != NULL);
self->size = 1;
struct entity_registrant_t *entities = calloc(
self->size, sizeof(struct entity_registrant_t)
);
if (entities == NULL) return ERR_ALLOC;
self->entities = entities;
return ERR_OK;
self->size = 0;
self->entities = NULL;
}
@ -100,6 +100,76 @@ void entity_registry_free(struct entity_registry_t *self) {
}
static enum error_t entity_registry_serialise_entity(
struct entity_registry_t const *self,
struct json_t *json,
size_t index
) {
assert(self != NULL);
assert(json != NULL);
struct json_t *entity_json = NULL;
enum error_t err = registrant_serialise(
&self->entities[index], &entity_json
);
if (err != ERR_OK) return err;
int json_err = json_array_append_new(json, entity_json);
if (json_err < 0) {
json_decref(entity_json);
return ERR_JSON_SERIALISE;
}
return ERR_OK;
}
static enum error_t entity_registry_serialise_entities(
struct entity_registry_t const *self,
struct json_t **json
) {
assert(self != NULL);
assert(json != NULL);
struct json_t *entities_json = json_array();
if (entities_json == NULL) return ERR_ALLOC;
for (size_t i = 0; i < self->size; i++) {
enum error_t err = entity_registry_serialise_entity(
self, entities_json, i
);
if (err != ERR_OK) {
json_decref(entities_json);
return err;
}
}
*json = entities_json;
return ERR_OK;
}
static enum error_t entity_registry_serialise_parts(
struct json_t *entities_json,
size_t size,
struct json_t **json
) {
assert(entities_json != NULL);
assert(json != NULL);
struct json_t *registry_json = json_pack(ENTITY_REGISTRY_JSON_FMT,
"entities", entities_json,
"size", size
);
if (registry_json == NULL) return ERR_JSON_SERIALISE;
*json = registry_json;
return ERR_OK;
}
enum error_t entity_registry_serialise(
struct entity_registry_t const *self,
struct json_t **json
@ -107,34 +177,86 @@ enum error_t entity_registry_serialise(
assert(self != NULL);
assert(json != NULL);
enum error_t err = ERR_OK;
struct json_t *entities_json = NULL;
enum error_t err = entity_registry_serialise_entities(self, &entities_json);
if (err != ERR_OK) return err;
struct json_t *entities_json = json_array();
for (size_t i = 0; i < self->size; i++) {
struct json_t *maybe_entity_json;
err = registrant_serialise(&self->entities[i], &maybe_entity_json);
if (err != ERR_OK) goto error_cleanup_array;
struct json_t *registry_json = NULL;
err = entity_registry_serialise_parts(
entities_json, self->size,
&registry_json
);
int json_err = json_array_append_new(entities_json, maybe_entity_json);
if (json_err < 0) {
err = ERR_JSON_SERIALISE;
goto error_cleanup_array;
if (err != ERR_OK) {
json_decref(entities_json);
return err;
}
*json = registry_json;
return ERR_OK;
}
static enum error_t entity_registry_deserialise_parts(
struct json_t **entities_json,
size_t *size,
struct json_t *json
) {
assert(entities_json != NULL);
assert(size != NULL);
assert(json != NULL);
int err = json_unpack(json, ENTITY_REGISTRY_JSON_FMT,
"entities", entities_json,
"size", size
);
if (err < 0) return ERR_JSON_DESERIALISE;
return ERR_OK;
}
static enum error_t entity_registry_deserialise_entity(
struct entity_registry_t *self,
struct json_t *json,
size_t index
) {
assert(self != NULL);
assert(json != NULL);
struct json_t *entity_json = json_array_get(json, index);
if (entity_json == NULL) return ERR_JSON_DESERIALISE;
enum error_t err = registrant_deserialise(
&self->entities[index], entity_json
);
if (err != ERR_OK) return err;
return ERR_OK;
}
static enum error_t entity_registry_deserialise_entities(
struct entity_registry_t *self,
struct json_t *json
) {
assert(self != NULL);
assert(json != NULL);
self->entities = calloc(
json_array_size(json), sizeof(struct entity_registrant_t)
);
if (self->entities == NULL) return ERR_ALLOC;
for (size_t i = 0; i < json_array_size(json); i++) {
enum error_t err = entity_registry_deserialise_entity(self, json, i);
if (err != ERR_OK) {
free(self->entities);
return err;
}
}
assert(json_array_size(entities_json) == self->size);
struct json_t *maybe_json = json_pack(ENTITY_REGISTRY_JSON_FMT,
"entities", entities_json,
"size", self->size
);
if (maybe_json == NULL) goto error_cleanup_array;
*json = maybe_json;
return ERR_OK;
error_cleanup_array:
json_array_clear(entities_json);
return err;
}
@ -145,34 +267,69 @@ enum error_t entity_registry_deserialise(
assert(self != NULL);
assert(json != NULL);
enum error_t err = ERR_OK;
struct entity_registry_t temp_registry = { 0 };
struct json_t *entities_json = NULL;
int json_err = json_unpack(json, ENTITY_REGISTRY_JSON_FMT,
"entities", &entities_json,
"size", &self->size
enum error_t err = entity_registry_deserialise_parts(
&entities_json, &temp_registry.size, json
);
if (json_err < 0) return ERR_JSON_DESERIALISE;
if (err != ERR_OK) return err;
struct entity_registrant_t *entities = calloc(
self->size, sizeof(struct entity_registrant_t)
);
if (entities == NULL) goto error_cleanup_entities;
for (size_t i = 0; i < json_array_size(entities_json); i++) {
struct json_t *entity_json = json_array_get(entities_json, i);
if (entity_json == NULL) goto error_cleanup_entities;
err = registrant_deserialise(&entities[i], entity_json);
if (err != ERR_OK) goto error_cleanup_entities;
err = entity_registry_deserialise_entities(&temp_registry, entities_json);
if (err != ERR_OK) {
json_decref(entities_json);
return err;
}
self->entities = entities;
return ERR_OK;
json_decref(entities_json);
error_cleanup_entities:
free(entities);
return ERR_JSON_DESERIALISE;
*self = temp_registry;
return ERR_OK;
}
enum error_t entity_registry_resize(
struct entity_registry_t *self,
size_t size
) {
assert(self != NULL);
struct entity_registrant_t *entities = realloc(
self->entities,
size * sizeof(struct entity_registrant_t)
);
if (entities == NULL) return ERR_ALLOC;
self->size = size;
self->entities = entities;
return ERR_OK;
}
void entity_registry_set(
struct entity_registry_t *self,
struct entity_registrant_t const *registrant,
size_t index
) {
assert(self != NULL);
assert(registrant != NULL);
assert(index < self->size);
self->entities[index] = *registrant;
}
enum error_t entity_registry_try_set(
struct entity_registry_t *self,
struct entity_registrant_t const *registrant,
size_t index
) {
if (self == NULL || registrant == NULL) return ERR_INPUT;
if (index >= self->size) return ERR_INPUT;
entity_registry_set(self, registrant, index);
return ERR_OK;
}
@ -180,16 +337,12 @@ enum error_t entity_registry_append(
struct entity_registry_t *self,
struct entity_registrant_t const *registrant
) {
assert(self != NULL && registrant != NULL);
assert(self != NULL);
assert(registrant != NULL);
self->size += 1;
self->entities = realloc(
self->entities,
self->size * sizeof(struct entity_registrant_t)
);
enum error_t err = entity_registry_resize(self, self->size + 1);
if (err != ERR_OK) return err;
if (self->entities == NULL) return ERR_ALLOC;
self->entities[self->size - 1] = *registrant;
entity_registry_set(self, registrant, self->size - 1);
return ERR_OK;
}

View File

@ -7,29 +7,21 @@
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) {
assert(self != NULL);
self->height = height;
self->width = width;
for (size_t i = 0; i < MAX_ENTITIES; i++) {
self->entities[i].id = 0;
self->entities[i].x = 0;
self->entities[i].y = 0;
}
memset(self->entities, 0, MAX_ENTITIES * sizeof(struct entity_t));
enum error_t err = entity_registry_init(&self->registered_entities);
entity_registry_init(&self->registered_entities);
enum error_t err = world_register_entity(self, "NULL", '?');
if (err != ERR_OK) return err;
struct entity_registrant_t null_entity;
err = entity_registrant_init(&null_entity, "NULL", '?');
if (err != ERR_OK) {
entity_registry_free(&self->registered_entities);
return err;
}
self->registered_entities.entities[0] = null_entity;
// If this fails, nothing's been allocated to the registry,
// so there's no need to free it
return ERR_OK;
}
@ -42,97 +34,234 @@ void world_free(struct world_t *self) {
}
static enum error_t serialise(struct world_t *self, struct json_t **json) {
static enum error_t world_serialise_entity(
struct world_t const *self,
struct json_t *json,
size_t index
) {
assert(self != NULL);
assert(json != NULL);
if (self->entities[index].id == 0) return ERR_OK;
struct json_t *entity_json = NULL;
enum error_t err = entity_serialise(&self->entities[index], &entity_json);
if (err != ERR_OK) return err;
int json_err = json_array_append_new(json, entity_json);
if (json_err < 0) {
json_decref(entity_json);
return ERR_JSON_SERIALISE;
}
return ERR_OK;
}
static enum error_t world_serialise_entities(
struct world_t const *self,
struct json_t **json
) {
assert(self != NULL);
assert(json != NULL);
struct json_t *entities_json = json_array();
if (entities_json == NULL) return ERR_ALLOC;
for (size_t i = 0; i < MAX_ENTITIES; i++) {
enum error_t err = world_serialise_entity(self, entities_json, i);
if (err != ERR_OK) {
json_decref(entities_json);
return err;
}
}
*json = entities_json;
return ERR_OK;
}
static enum error_t world_serialise_parts(
struct json_t *entities_json,
struct json_t *registered_entities_json,
size_t height, size_t width,
struct json_t **json
) {
assert(entities_json != NULL);
assert(registered_entities_json != NULL);
assert(json != NULL);
struct json_t *world_json = json_pack(WORLD_JSON_FMT,
"entities", entities_json,
"registered_entities", registered_entities_json,
"height", height, "width", width
);
if (world_json == NULL) return ERR_JSON_SERIALISE;
*json = world_json;
return ERR_OK;
}
static enum error_t world_serialise(struct world_t *self, struct json_t **json) {
assert(self != NULL);
assert(json != NULL);
enum error_t err = ERR_OK;
struct json_t *entities_json = json_array();
for (size_t i = 0; i < MAX_ENTITIES; i++) {
if (self->entities[i].id == 0) continue;
// Serialise existing entities
struct json_t *entities_json = NULL;
err = world_serialise_entities(self, &entities_json);
if (err != ERR_OK) return err;
struct json_t *maybe_entity_json;
err = entity_serialise(&self->entities[i], &maybe_entity_json);
if (err != ERR_OK) goto error_cleanup_array;
int json_err = json_array_append_new(entities_json, maybe_entity_json);
if (json_err < 0) {
err = ERR_JSON_SERIALISE;
goto error_cleanup_array;
}
}
struct json_t *registered_entities_json;
// Serialise the entity registry
struct json_t *registered_entities_json = NULL;
err = entity_registry_serialise(
&self->registered_entities,
&registered_entities_json
);
if (err != ERR_OK) return err;
if (err != ERR_OK) goto error;
struct json_t *maybe_json = json_pack(WORLD_JSON_FMT,
"entities", entities_json,
"registered_entities", registered_entities_json,
"height", self->height,
"width", self->width
// Serialise the full structure
struct json_t *world_json = NULL;
err = world_serialise_parts(
entities_json,
registered_entities_json,
self->height, self->width,
&world_json
);
if (maybe_json == NULL) return ERR_JSON_SERIALISE;
if (err != ERR_OK) goto error_world;
*json = maybe_json;
*json = world_json;
return ERR_OK;
error_cleanup_array:
json_array_clear(entities_json);
error_world:
json_decref(registered_entities_json);
error:
json_decref(entities_json);
return err;
}
static enum error_t deserialise(struct world_t *self, struct json_t *json) {
static enum error_t world_deserialise_parts(
struct json_t **entities_json,
struct json_t **registered_entities_json,
size_t *height, size_t *width,
struct json_t *json
) {
assert(entities_json != NULL);
assert(registered_entities_json != NULL);
assert(height != NULL);
assert(width != NULL);
assert(json != NULL);
int err = json_unpack(json, WORLD_JSON_FMT,
"entities", entities_json,
"registered_entities", registered_entities_json,
"height", height, "width", width
);
if (err < 0) return ERR_JSON_DESERIALISE;
return ERR_OK;
}
static enum error_t world_deserialise_entity(
struct world_t *self,
struct json_t *json,
size_t index
) {
assert(self != NULL);
assert(json != NULL);
struct json_t *entities_json;
struct json_t *registered_entities_json;
int json_err = json_unpack(json, WORLD_JSON_FMT,
"entities", &entities_json,
"registered_entities", &registered_entities_json,
"height", &self->height,
"width", &self->width
);
if (json_err < 0) return ERR_JSON_DESERIALISE;
for (size_t i = 0; i < MAX_ENTITIES; i++) {
// The entity array can have gaps, but the serialisation process shifts
// all entities forwards to fill them; this zeroes out the empty space
// at the end, to ensure no "ghost" entities remain
if (i >= json_array_size(entities_json)) {
self->entities[i].id = 0;
continue;
}
struct json_t *entity_json = json_array_get(entities_json, i);
if (entity_json == NULL) return ERR_JSON_DESERIALISE;
enum error_t err = entity_deserialise(&self->entities[i], entity_json);
if (err != ERR_OK) return err;
// The entity array may have gaps, such as [ 1, 0, 0, 2 ]
// Serialisation omits entities of ID 0, and doesn't store the original
// index of each entity, so the deserialiser only sees [ 1, 2 ]
// If you've just serialised and deserialised to the same world_t, this
// could cause "ghost" entities to arise, looking like [1, 2, 0, 2 ]
// Here, we clear out those ghost entities
if (index >= json_array_size(json)) {
self->entities[index].id = 0;
return ERR_OK;
}
enum error_t err = entity_registry_deserialise(
&self->registered_entities,
registered_entities_json
);
struct json_t *entity_json = json_array_get(json, index);
if (entity_json == NULL) return ERR_JSON_DESERIALISE;
enum error_t err = entity_deserialise(&self->entities[index], entity_json);
if (err != ERR_OK) return err;
return ERR_OK;
}
static enum error_t world_deserialise_entities(
struct world_t *self,
struct json_t *json
) {
assert(self != NULL);
assert(json != NULL);
for (size_t i = 0; i < MAX_ENTITIES; i++) {
enum error_t err = world_deserialise_entity(self, json, i);
if (err != ERR_OK) return err;
}
return ERR_OK;
}
static enum error_t world_deserialise(struct world_t *self, struct json_t *json) {
assert(self != NULL);
assert(json != NULL);
struct world_t temp_world = { 0 };
// I don't want to write to self until the very end, in case an error
// occurs when we've written some fields but not all of them
struct json_t *entities_json = NULL;
struct json_t *registered_entities_json = NULL;
// Deserialise full struct into parts
enum error_t err = world_deserialise_parts(
&entities_json,
&registered_entities_json,
&temp_world.height, &temp_world.width,
json
);
if (err != ERR_OK) return err;
// Deserialise the existing entities
err = world_deserialise_entities(&temp_world, entities_json);
if (err != ERR_OK) goto error;
// Deserialise the entity registry
err = entity_registry_deserialise(
&temp_world.registered_entities,
registered_entities_json
);
if (err != ERR_OK) goto error;
json_decref(entities_json);
json_decref(registered_entities_json);
*self = temp_world;
return ERR_OK;
error:
json_decref(entities_json);
json_decref(registered_entities_json);
return err;
}
enum error_t world_serialise_str(struct world_t *self, char const **strptr) {
assert(self != NULL);
assert(strptr != NULL);
struct json_t *json;
enum error_t err = serialise(self, &json);
enum error_t err = world_serialise(self, &json);
if (err != ERR_OK) return ERR_JSON_SERIALISE;
const char *str = json_dumps(json, 0);
@ -149,7 +278,7 @@ enum error_t world_serialise_buf(struct world_t *self, char *buf, size_t len) {
assert(len > 0);
struct json_t *json;
enum error_t err = serialise(self, &json);
enum error_t err = world_serialise(self, &json);
if (err != ERR_OK) return ERR_JSON_SERIALISE;
size_t bytes_written = json_dumpb(json, buf, len, 0);
@ -159,15 +288,14 @@ enum error_t world_serialise_buf(struct world_t *self, char *buf, size_t len) {
}
enum error_t world_deserialise(struct world_t *self, char const *str) {
enum error_t world_deserialise_str(struct world_t *self, char const *str) {
assert(self != NULL);
assert(str != NULL);
struct json_t *json = json_loads(str, 0, NULL);
if (json == NULL) return ERR_JSON_DESERIALISE;
enum error_t err = deserialise(self, json);
enum error_t err = world_deserialise(self, json);
if (err != ERR_OK) return ERR_JSON_DESERIALISE;
return ERR_OK;
@ -181,7 +309,7 @@ enum error_t world_register_entity(
assert(self != NULL);
assert(name != NULL);
enum error_t err;
enum error_t err = ERR_OK;
struct entity_registry_t *registry = &self->registered_entities;
struct entity_registrant_t registrant;
@ -189,7 +317,10 @@ enum error_t world_register_entity(
if (err != ERR_OK) return err;
err = entity_registry_append(registry, &registrant);
if (err != ERR_OK) return err;
if (err != ERR_OK) {
entity_registrant_free(&registrant);
return err;
}
return ERR_OK;
}

View File

@ -34,7 +34,7 @@ enum error_t game_loop(struct data_t *data) {
char ibuf[8192] = { 0 };
read(data->socket_accept, ibuf, 8192);
err = world_deserialise(&data->world, ibuf);
err = world_deserialise_str(&data->world, ibuf);
if (err != ERR_OK) return err;
printf("SERVER: \"%s\" -> \"%s\"\n", obuf, ibuf);