summaryrefslogtreecommitdiff
path: root/mcproto/status/response.ha
blob: e93df47aa9a5801278ce9c6e0886a4f127f64853 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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);
};