/* * 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: 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 = NULL; if ((r = get_child(obj, "player", json_type_object, false, &arg))) { ac_object_free((ac_obj_t *) disconnect); goto fail; } if (arg) { if ((r = SALLOC(ac_entity_t, &disconnect->player))) { 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: 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) { r = deserialize_response(obj, type_str, id, (ac_response_t **) out); } else { r = deserialize_event(obj, type_str, (ac_event_t **) out); } json_object_put(obj); return r; } 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); return AC_E_OK; }