From b4afa06e383325f4a0c751a64ca896d769db07a8 Mon Sep 17 00:00:00 2001 From: Trumeet Date: Wed, 20 Jul 2022 18:12:22 -0700 Subject: libac: First Commit --- client/libacron/private/serializer.c | 572 +++++++++++++++++++++++++++++++++++ 1 file changed, 572 insertions(+) create mode 100644 client/libacron/private/serializer.c (limited to 'client/libacron/private/serializer.c') diff --git a/client/libacron/private/serializer.c b/client/libacron/private/serializer.c new file mode 100644 index 0000000..c34c81f --- /dev/null +++ b/client/libacron/private/serializer.c @@ -0,0 +1,572 @@ +/* + * Created by yuuta on 7/13/22. + */ + +#include "libac.h" +#include "config.h" +#include "serializer.h" +#include "helpers.h" +#include "log.h" + +#include +#include +#include +#include + +static int assert_type(const json_object *obj, const json_type type, const char *location) { + if (json_object_is_type(obj, type)) { + return AC_E_OK; + } + LOGEV("Cannot deserialize JSON at '%s': It should be a(n) %s, but it is a(n) %s.", + location, + json_type_to_name(type), + json_type_to_name(json_object_get_type(obj))); + return AC_E_INVALID_RESPONSE; +} + +static int get_child(const json_object *obj, + const char *key, + const json_type type, + const bool mandatory, + json_object **out) { + if (!json_object_object_get_ex(obj, key, out)) { + if (!mandatory) { + *out = NULL; + return AC_E_OK; + } + LOGEV("Cannot deserialize JSON: A mandatory child '%s' is missing.", + key); + return AC_E_INVALID_RESPONSE; + } + return assert_type(*out, type, key); +} + +static int deserialize_pre(const char *string, + unsigned int len, + json_object **out_json, + const char **out_type, + bool id_mandatory, + bool *out_has_id, + int32_t *out_id) { + AC_CHECK_INIT; + json_object *obj = NULL; + if (!(obj = json_tokener_parse_ex(config->tok, + string, + (int) len))) { + enum json_tokener_error err = json_tokener_get_error(config->tok); + if (err == json_tokener_continue) { + return AC_E_SERIALIZER_CONTINUE; + } + LOGEV("Cannot parse JSON response '%s': %s.", + string, + json_tokener_error_desc(err)); + json_tokener_reset(config->tok); + return AC_E_INVALID_RESPONSE; + } + int r; + if ((r = assert_type(obj, json_type_object, "."))) { + json_object_put(obj); + return r; + } + json_object *arg; + if ((r = get_child(obj, "type", json_type_string, true, &arg))) { + json_object_put(obj); + return r; + } + *out_json = obj; + *out_type = json_object_get_string(arg); + if ((r = get_child(obj, "id", json_type_int, id_mandatory, &arg))) { + json_object_put(obj); + *out_has_id = false; + return r; + } else { + *out_id = json_object_get_int(arg); + /* If is_mandatory is false, get_child will return 0 even it arg == NULL. */ + *out_has_id = arg; + } + return AC_E_OK; +} + +static int deserialize_entity(const json_object *json, ac_entity_t *entity) { + int r; + json_object *arg; + if ((r = get_child(json, "name", json_type_string, true, &arg))) { + return r; + } + if ((r = strdup2(json_object_get_string(arg), &entity->name))) { + return r; + } + + if ((r = get_child(json, "uuid", json_type_string, true, &arg))) { + return r; + } + if ((r = strdup2(json_object_get_string(arg), &entity->uuid))) { + return r; + } + + if ((r = get_child(json, "world", json_type_string, false, &arg))) { + return r; + } + if (arg) { + const char *world_str = json_object_get_string(arg); + if (!strcmp("overworld", world_str)) { + entity->world = overworld; + } else if (!strcmp("nether", world_str)) { + entity->world = nether; + } else if (!strcmp("end", world_str)) { + entity->world = end; + } else { + LOGEV("Server returned an invalid world: %s.", world_str); + return r; + } + } + + json_object *vec3d; + if ((r = get_child(json, "pos", json_type_object, true, &vec3d))) { + return r; + } + json_object *d; + if ((r = get_child(vec3d, "x", json_type_double, true, &d))) { + return r; + } + entity->pos.x = json_object_get_double(d); + + if ((r = get_child(vec3d, "y", json_type_double, true, &d))) { + return r; + } + entity->pos.y = json_object_get_double(d); + + if ((r = get_child(vec3d, "z", json_type_double, true, &d))) { + return r; + } + entity->pos.z = json_object_get_double(d); + return AC_E_OK; +} + +static int deserialize_response(json_object *obj, const char *type_str, const int id, ac_response_t **out) { + int r; + + if (!strcmp("ok", type_str)) { + ac_response_ok_t *ok; + if ((r = SALLOC(ac_response_t, &ok))) { + goto fail; + } + ok->type = AC_RESPONSE_OK; + ok->id = id; + + *out = (ac_response_t *) ok; + } else if (!strcmp("error", type_str)) { + ac_response_error_t *error; + if ((r = SALLOC(ac_response_error_t, &error))) { + goto fail; + } + error->type = AC_RESPONSE_ERROR; + error->id = id; + json_object *arg; + + if ((r = get_child(obj, "code", json_type_int, true, &arg))) { + ac_object_free((ac_obj_t *) error); + goto fail; + } + error->code = json_object_get_int(arg); + + if ((r = get_child(obj, "message", json_type_string, true, &arg))) { + ac_object_free((ac_obj_t *) error); + goto fail; + } + if ((r = strdup2(json_object_get_string(arg), &error->message))) { + ac_object_free((ac_obj_t *) error); + goto fail; + } + + *out = (ac_response_t *) error; + } else if (!strcmp("cmd_out", type_str)) { + ac_response_cmd_out_t *o; + if ((r = SALLOC(ac_response_cmd_out_t, &o))) { + goto fail; + } + o->type = AC_RESPONSE_CMD_OUT; + o->id = id; + json_object *arg; + + if ((r = get_child(obj, "sender", json_type_string, true, &arg))) { + ac_object_free((ac_obj_t *) o); + goto fail; + } + if ((r = strdup2(json_object_get_string(arg), &o->sender))) { + ac_object_free((ac_obj_t *) o); + goto fail; + } + + if ((r = get_child(obj, "out", json_type_string, true, &arg))) { + ac_object_free((ac_obj_t *) o); + goto fail; + } + if ((r = strdup2(json_object_get_string(arg), &o->out))) { + ac_object_free((ac_obj_t *) o); + goto fail; + } + + *out = (ac_response_t *) o; + } else if (!strcmp("cmd_result", type_str)) { + ac_response_cmd_result_t *res; + if ((r = SALLOC(ac_response_cmd_result_t, &res))) { + goto fail; + } + res->type = AC_RESPONSE_CMD_RESULT; + res->id = id; + json_object *arg; + + if ((r = get_child(obj, "result", json_type_int, true, &arg))) { + ac_object_free((ac_obj_t *) res); + goto fail; + } + res->result = json_object_get_int(arg); + + if ((r = get_child(obj, "success", json_type_boolean, true, &arg))) { + ac_object_free((ac_obj_t *) res); + goto fail; + } + res->success = json_object_get_boolean(arg); + + *out = (ac_response_t *) res; + } else { + LOGEV("Invalid response type: '%s'.", type_str); + r = AC_E_INVALID_RESPONSE; + goto fail; + } + return AC_E_OK; + fail: + if (obj) { + json_object_put(obj); + } + return r; +} + +static int deserialize_event(json_object *obj, const char *type_str, ac_event_t **out) { + LOGDV("Deserializing event type '%s'", type_str); + int r; + + if (!strcmp("join", type_str)) { + ac_event_player_join_t *join; + if ((r = SALLOC(ac_event_player_join_t, &join))) { + goto fail; + } + join->type = AC_EVENT_PLAYER_JOIN; + + json_object *arg; + if ((r = get_child(obj, "player", json_type_object, true, &arg))) { + ac_object_free((ac_obj_t *) join); + goto fail; + } + if ((r = deserialize_entity(arg, &join->player))) { + ac_object_free((ac_obj_t *) join); + goto fail; + } + + *out = (ac_event_t *) join; + } else if (!strcmp("disconnect", type_str)) { + ac_event_player_disconnect_t *disconnect; + if ((r = SALLOC(ac_event_player_disconnect_t, &disconnect))) { + goto fail; + } + disconnect->type = AC_EVENT_PLAYER_DISCONNECT; + + json_object *arg; + if ((r = get_child(obj, "player", json_type_object, true, &arg))) { + ac_object_free((ac_obj_t *) disconnect); + goto fail; + } + if ((r = deserialize_entity(arg, &disconnect->player))) { + ac_object_free((ac_obj_t *) disconnect); + goto fail; + } + + if ((r = get_child(obj, "reason", json_type_string, true, &arg))) { + ac_object_free((ac_obj_t *) disconnect); + goto fail; + } + if ((r = strdup2(json_object_get_string(arg), &disconnect->reason))) { + ac_object_free((ac_obj_t *) disconnect); + goto fail; + } + + *out = (ac_event_t *) disconnect; + } else if (!strcmp("message", type_str)) { + ac_event_player_message_t *message; + if ((r = SALLOC(ac_event_player_message_t, &message))) { + goto fail; + } + message->type = AC_EVENT_PLAYER_MESSAGE; + + json_object *arg; + if ((r = get_child(obj, "player", json_type_object, true, &arg))) { + ac_object_free((ac_obj_t *) message); + goto fail; + } + if ((r = deserialize_entity(arg, &message->player))) { + ac_object_free((ac_obj_t *) message); + goto fail; + } + + if ((r = get_child(obj, "text", json_type_string, true, &arg))) { + ac_object_free((ac_obj_t *) message); + goto fail; + } + if ((r = strdup2(json_object_get_string(arg), &message->text))) { + ac_object_free((ac_obj_t *) message); + goto fail; + } + + *out = (ac_event_t *) message; + } else if (!strcmp("death", type_str)) { + ac_event_entity_death_t *death; + if ((r = SALLOC(ac_event_entity_death_t, &death))) { + goto fail; + } + death->type = AC_EVENT_ENTITY_DEATH; + + json_object *arg; + if ((r = get_child(obj, "entity", json_type_object, true, &arg))) { + ac_object_free((ac_obj_t *) death); + goto fail; + } + if ((r = deserialize_entity(arg, &death->entity))) { + ac_object_free((ac_obj_t *) death); + goto fail; + } + + if ((r = get_child(obj, "message", json_type_string, true, &arg))) { + ac_object_free((ac_obj_t *) death); + goto fail; + } + if ((r = strdup2(json_object_get_string(arg), &death->message))) { + ac_object_free((ac_obj_t *) death); + goto fail; + } + + *out = (ac_event_t *) death; + } else if (!strcmp("lagging", type_str)) { + ac_event_lagging_t *lagging; + if ((r = SALLOC(ac_event_lagging_t, &lagging))) { + goto fail; + } + lagging->type = AC_EVENT_LAGGING; + + json_object *arg; + if ((r = get_child(obj, "ms", json_type_int, true, &arg))) { + goto fail; + } + lagging->ms = json_object_get_int64(arg); + + if ((r = get_child(obj, "ticks", json_type_int, true, &arg))) { + goto fail; + } + lagging->ticks = json_object_get_int64(arg); + + *out = (ac_event_t *) lagging; + } else { + LOGEV("Invalid event type: '%s'.", type_str); + r = AC_E_INVALID_RESPONSE; + goto fail; + } + + return AC_E_OK; + fail: + if (obj) { + json_object_put(obj); + } + return r; +} + +int deserialize(const char *string, unsigned int len, ac_obj_t **out) { + AC_CHECK_INIT; + json_object *obj; + const char *type_str; + int id; + bool has_id; + int r; + + if ((r = deserialize_pre(string, + len, + &obj, + &type_str, + false, + &has_id, + &id))) { + return r; + } + + if (has_id) { + return deserialize_response(obj, type_str, id, (ac_response_t **) out); + } else { + return deserialize_event(obj, type_str, (ac_event_t **) out); + } +} + +static int json_add(json_object *obj, const char *key, json_object *val) { + if (!val) { + LOGEV("Cannot initialize JSON child '%s'.", key); + return AC_E_MEMORY_ALLOCATION; + } + if ((json_object_object_add(obj, key, val))) { + LOGEV("Cannot put '%s' to the JSON object.", key); + json_object_put(val); + return AC_E_MEMORY_ALLOCATION; + } + return AC_E_OK; +} + +static int serialize_config(const ac_config_t *c, json_object *obj) { + int r; + if (c->pos) { + ac_vec3d_t p = *c->pos; + json_object *pos; + if (!(pos = json_object_new_object())) { + LOGE("Cannot initialize a new JSON object."); + return AC_E_MEMORY_ALLOCATION; + } + if ((r = json_add(pos, "x", json_object_new_double(p.x)))) { + json_object_put(pos); + return r; + } + if ((r = json_add(pos, "y", json_object_new_double(p.y)))) { + json_object_put(pos); + return r; + } + if ((r = json_add(pos, "z", json_object_new_double(p.z)))) { + json_object_put(pos); + return r; + } + if ((r = json_add(obj, "pos", pos))) { + return r; + } + } + if (c->world) { + enum ac_world world = *c->world; + const char *str; + switch (world) { + case overworld: + str = "overworld"; + break; + case nether: + str = "nether"; + break; + case end: + str = "end"; + break; + default: + LOGE("Invalid world supplied."); + return AC_E_INVALID_REQUEST; + } + if ((r = json_add(obj, "world", json_object_new_string(str)))) { + return r; + } + } + if (c->name) { + if ((r = json_add(obj, "name", json_object_new_string(c->name)))) { + return r; + } + } + if (c->rot) { + ac_vec2f_t t = *c->rot; + json_object *rot; + if (!(rot = json_object_new_object())) { + LOGE("Cannot initialize a new JSON object."); + return AC_E_MEMORY_ALLOCATION; + } + if ((r = json_add(rot, "x", json_object_new_double(t.x)))) { + json_object_put(rot); + return r; + } + if ((r = json_add(rot, "y", json_object_new_double(t.y)))) { + json_object_put(rot); + return r; + } + if ((r = json_add(obj, "rot", rot))) { + return r; + } + } + return AC_E_OK; +} + +int serialize_request(const ac_request_t *req, json_object **out) { + AC_CHECK_INIT; + if ((req->type & 192) != AC_TYPE_REQUEST) { + LOGE("Invalid argument to serialize_request. It should be a request."); + return AC_E_INVALID_REQUEST; + } + json_object *obj; + if (!(obj = json_object_new_object())) { + LOGE("Cannot initialize a new JSON object."); + return AC_E_MEMORY_ALLOCATION; + } + int r; + if ((r = json_add(obj, "id", json_object_new_int(req->id)))) { + goto fail; + } + switch (req->type) { + case AC_REQUEST_CMD: { + if ((r = json_add(obj, "type", json_object_new_string("cmd")))) { + goto fail; + } + ac_request_cmd_t *cmd = (ac_request_cmd_t *) req; + if ((r = json_add(obj, "cmd", json_object_new_string(cmd->cmd)))) { + goto fail; + } + if (cmd->config) { + json_object *c; + if (!(c = json_object_new_object())) { + LOGE("Cannot initialize a new JSON object."); + goto fail; + } + if ((r = serialize_config(cmd->config, c))) { + json_object_put(c); + goto fail; + } + if ((r = json_add(obj, "config", c))) { + goto fail; + } + } + break; + } + case AC_REQUEST_SET_CONFIG: { + if ((r = json_add(obj, "set_config", json_object_new_string("cmd")))) { + goto fail; + } + ac_request_set_config_t *set_config = (ac_request_set_config_t *) req; + json_object *c; + if (!(c = json_object_new_object())) { + LOGE("Cannot initialize a new JSON object."); + goto fail; + } + if ((r = serialize_config(&set_config->config, c))) { + json_object_put(c); + goto fail; + } + if ((r = json_add(obj, "config", c))) { + goto fail; + } + break; + } + default: { + LOGE("Invalid request supplied."); + r = AC_E_INVALID_REQUEST; + goto fail; + } + } + *out = obj; + return AC_E_OK; + fail: + { + if (obj) { + json_object_put(obj); + } + return r; + } +} + +int serializer_finalize(void) { + AC_CHECK_INIT; + json_tokener_reset(config->tok); +} -- cgit v1.2.3