From c31078ebd5881c56f3b1e768a70c7f802d58361d Mon Sep 17 00:00:00 2001 From: snit Date: Sun, 13 Oct 2024 15:04:35 -0500 Subject: [PATCH] refactor a lot of the code because it was getting messy --- TODO.gmi | 4 +- client/src/main.c | 2 +- common/include/entity/registry.h | 17 +- common/include/world.h | 7 +- common/src/entity/registry.c | 287 +++++++++++++++++++++++-------- common/src/world.c | 285 +++++++++++++++++++++--------- server/src/main.c | 2 +- 7 files changed, 452 insertions(+), 152 deletions(-) diff --git a/TODO.gmi b/TODO.gmi index 6b6f997..30f4671 100644 --- a/TODO.gmi +++ b/TODO.gmi @@ -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 diff --git a/client/src/main.c b/client/src/main.c index 72d0107..8eb5317 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -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 ++); diff --git a/common/include/entity/registry.h b/common/include/entity/registry.h index 6a885b4..c652f14 100644 --- a/common/include/entity/registry.h +++ b/common/include/entity/registry.h @@ -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 * diff --git a/common/include/world.h b/common/include/world.h index 70847d1..9eb4822 100644 --- a/common/include/world.h +++ b/common/include/world.h @@ -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); diff --git a/common/src/entity/registry.c b/common/src/entity/registry.c index fc0fe9b..6d89cd0 100644 --- a/common/src/entity/registry.c +++ b/common/src/entity/registry.c @@ -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, + ®istry_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; } diff --git a/common/src/world.c b/common/src/world.c index cc4335d..1dcff81 100644 --- a/common/src/world.c +++ b/common/src/world.c @@ -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, ®istered_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", ®istered_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, + ®istered_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, ®istrant); - if (err != ERR_OK) return err; + if (err != ERR_OK) { + entity_registrant_free(®istrant); + return err; + } return ERR_OK; } diff --git a/server/src/main.c b/server/src/main.c index 5e70365..a7a00f0 100644 --- a/server/src/main.c +++ b/server/src/main.c @@ -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);