diff options
author | Lassi Pulkkinen <lassi@pulk.fi> | 2024-10-31 03:11:21 +0200 |
---|---|---|
committer | Lassi Pulkkinen <lassi@pulk.fi> | 2024-10-31 03:51:35 +0200 |
commit | ae44478b30d890fe0fb04022f44d474dcdcc3f9d (patch) | |
tree | 5f462459ae4b47d22114eed717d1382d08cf4dfe /mcproto/status/response.ha |
Diffstat (limited to 'mcproto/status/response.ha')
-rw-r--r-- | mcproto/status/response.ha | 111 |
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); +}; |