summaryrefslogtreecommitdiff
path: root/mcproto/status/response.ha
diff options
context:
space:
mode:
Diffstat (limited to 'mcproto/status/response.ha')
-rw-r--r--mcproto/status/response.ha111
1 files changed, 111 insertions, 0 deletions
diff --git a/mcproto/status/response.ha b/mcproto/status/response.ha
new file mode 100644
index 0000000..e93df47
--- /dev/null
+++ b/mcproto/status/response.ha
@@ -0,0 +1,111 @@
+use dejson;
+use encoding::json;
+use mcproto;
+use strings;
+use trace;
+use uuid;
+
+export type Response = struct {
+ description: str,
+ version_name: str,
+ version_protocol: i32,
+ players_online: u32,
+ players_max: u32,
+ players_sample: [](str, uuid::uuid),
+ previews_chat: bool,
+ forces_reportable_chat: bool,
+};
+
+export fn decode_response(ctx: *mcproto::Context) (Response | trace::failed) = {
+ const ctx_ = mcproto::context(ctx, "json");
+ const json = mcproto::decode_string(&ctx_, 32767)?;
+ mcproto::expect_end(ctx)?;
+
+ const json = match (json::loadstr(json)) {
+ case let json: json::value =>
+ yield json;
+ case let err: json::error =>
+ return mcproto::error(&ctx_, "Invalid JSON: {}", json::strerror(err));
+ };
+ defer json::finish(json);
+
+ const deser = dejson::newdeser(&json);
+ match (deser_response(&deser)) {
+ case let res: Response =>
+ return res;
+ case let err: dejson::error =>
+ defer free(err);
+ return mcproto::error(&ctx_, "Invalid response contents: {}", err);
+ };
+};
+
+export fn deser_response(de: *dejson::deser) (Response | dejson::error) = {
+ let success = false;
+
+ let res = Response { ... };
+ defer if (!success) response_finish(res);
+
+ res.description = match (dejson::optfield(de, "description")?) {
+ case let de_description: dejson::deser =>
+ yield json::dumpstr(*de_description.val);
+ case void =>
+ yield "";
+ };
+
+ const de_version = dejson::field(de, "version")?;
+ res.version_name = strings::dup(dejson::string(
+ &dejson::field(&de_version, "name")?)?);
+ res.version_protocol = dejson::number_i32(
+ &dejson::field(&de_version, "protocol")?)?;
+
+ const de_players = dejson::field(de, "players")?;
+ res.players_online = dejson::number_u32(
+ &dejson::field(&de_players, "online")?)?;
+ res.players_max = dejson::number_u32(
+ &dejson::field(&de_players, "max")?)?;
+ match (dejson::optfield(&de_players, "sample")?) {
+ case let de_sample: dejson::deser =>
+ const nsample = dejson::length(&de_sample)?;
+ for (let i = 0z; i < nsample; i += 1) {
+ const de_player = dejson::index(&de_sample, i)?;
+ const name = dejson::string(
+ &dejson::field(&de_player, "name")?)?;
+ const de_id = dejson::field(&de_player, "id")?;
+ const id = dejson::string(&de_id)?;
+ const id = match (uuid::decodestr(id)) {
+ case let id: uuid::uuid =>
+ yield id;
+ case uuid::invalid =>
+ return dejson::fail(&de_id, "Invalid UUID");
+ };
+ append(res.players_sample, (strings::dup(name), id));
+ };
+ case void => void;
+ };
+
+ res.previews_chat = match (dejson::optfield(de, "previewsChat")) {
+ case let de_previews_chat: dejson::deser =>
+ yield dejson::boolean(&de_previews_chat)?;
+ case void =>
+ yield false;
+ };
+ res.forces_reportable_chat = match (
+ dejson::optfield(de, "enforcesSecureChat")) {
+ case let de_forces_reportable_chat: dejson::deser =>
+ yield dejson::boolean(&de_forces_reportable_chat)?;
+ case void =>
+ yield false;
+ };
+
+ success = true;
+ return res;
+};
+
+export fn response_finish(res: Response) void = {
+ free(res.description);
+ free(res.version_name);
+ for (let i = 0z; i < len(res.players_sample); i += 1) {
+ free(res.players_sample[i].0);
+ };
+ free(res.players_sample);
+};