From 42828381d04ae4a068854b12280119c5b026769b Mon Sep 17 00:00:00 2001 From: TimePath Date: Thu, 31 Mar 2016 14:53:13 +1100 Subject: [PATCH] json: fix array parsing matrix.org integration --- qcsrc/lib/_all.inc | 2 + qcsrc/lib/_mod.inc | 1 + qcsrc/lib/json.qc | 14 ++- qcsrc/lib/matrix/_mod.inc | 3 + qcsrc/lib/matrix/command.qc | 48 +++++++++ qcsrc/lib/matrix/command.qh | 3 + qcsrc/lib/matrix/matrix.qc | 204 ++++++++++++++++++++++++++++++++++++ qcsrc/lib/matrix/matrix.qh | 17 +++ qcsrc/lib/test.qh | 10 +- qcsrc/lib/urllib.qc | 6 +- qcsrc/menu/_mod.inc | 1 + qcsrc/menu/matrix.qc | 26 +++++ qcsrc/server/_mod.inc | 1 + qcsrc/server/cl_player.qc | 86 +++++++-------- qcsrc/server/matrix.qc | 22 ++++ qcsrc/server/matrix.qh | 1 + 16 files changed, 393 insertions(+), 52 deletions(-) create mode 100644 qcsrc/lib/matrix/_mod.inc create mode 100644 qcsrc/lib/matrix/command.qc create mode 100644 qcsrc/lib/matrix/command.qh create mode 100644 qcsrc/lib/matrix/matrix.qc create mode 100644 qcsrc/lib/matrix/matrix.qh create mode 100644 qcsrc/menu/matrix.qc create mode 100644 qcsrc/server/matrix.qc create mode 100644 qcsrc/server/matrix.qh diff --git a/qcsrc/lib/_all.inc b/qcsrc/lib/_all.inc index fb7122fd8..c5e020660 100644 --- a/qcsrc/lib/_all.inc +++ b/qcsrc/lib/_all.inc @@ -103,3 +103,5 @@ void isnt_bool( float this) { print(ftos(this)); } #include "urllib.qc" #include "vector.qh" #include "yenc.qh" + +#include "matrix/_mod.inc" diff --git a/qcsrc/lib/_mod.inc b/qcsrc/lib/_mod.inc index fd811e7a9..ecec32a05 100644 --- a/qcsrc/lib/_mod.inc +++ b/qcsrc/lib/_mod.inc @@ -1,5 +1,6 @@ // generated file; do not modify #include "angle.qc" +#include "json.qc" #include "p2mathlib.qc" #include "random.qc" #include "sortlist.qc" diff --git a/qcsrc/lib/json.qc b/qcsrc/lib/json.qc index 555c4b10c..acdf198e8 100644 --- a/qcsrc/lib/json.qc +++ b/qcsrc/lib/json.qc @@ -78,7 +78,7 @@ bool _json_parse_array() { int it = bufstr_add(_json_buffer, key, 0); bool ret = false; WITH(string, _json_ns, key, ret = _json_parse_value()); if (!ret) { - bufstr_set(_json_buffer, it, string_null); + bufstr_free(_json_buffer, it); if (required) JSON_FAIL("expected value"); else break; } bufstr_set(_json_buffer, len, ftos(n + 1)); @@ -157,6 +157,7 @@ bool _json_parse_string(bool add) { case 'n': esc = "\n"; break; case 't': esc = "\t"; break; case 'u': esc = "\\u"; break; // TODO + case '/': esc = "/"; break; } s = strcat(s, esc); } else { @@ -271,6 +272,13 @@ void json_del(int buf) buf_del(buf); } +void json_dump(int buf) +{ + for (int i = 0, n = buf_getsize(buf); i < n; ++i) { + print(bufstr_get(buf, i), "\n"); + } +} + #undef JSON_BEGIN #undef JSON_FAIL #undef JSON_END @@ -287,8 +295,6 @@ TEST(json, Parse) print(s, "\n"); int buf = json_parse(s, _json_parse_object); EXPECT_NE(-1, buf); - for (int i = 0, n = buf_getsize(buf); i < n; ++i) { - print(bufstr_get(buf, i), "\n"); - } + json_dump(buf); SUCCEED(); } diff --git a/qcsrc/lib/matrix/_mod.inc b/qcsrc/lib/matrix/_mod.inc new file mode 100644 index 000000000..84522f199 --- /dev/null +++ b/qcsrc/lib/matrix/_mod.inc @@ -0,0 +1,3 @@ +// generated file; do not modify +#include "command.qc" +#include "matrix.qc" diff --git a/qcsrc/lib/matrix/command.qc b/qcsrc/lib/matrix/command.qc new file mode 100644 index 000000000..27c7ec959 --- /dev/null +++ b/qcsrc/lib/matrix/command.qc @@ -0,0 +1,48 @@ +#include "command.qh" + +#include + +GENERIC_COMMAND(mx, "Send a matrix command") { + switch (argv(1)) { + case "user": + if (matrix_user) strunzone(matrix_user); + matrix_user = strzone(substring(command, argv_start_index(2), -1)); + break; + case "token": + if (matrix_access_token) strunzone(matrix_access_token); + matrix_access_token = strzone(substring(command, argv_start_index(2), -1)); + break; + case "messages": + MX_Messages(string_null); + break; + case "nick": + MX_Nick(substring(command, argv_start_index(2), -1)); + break; + case "join": + string s = substring(command, argv_start_index(2), -1); + if (s != matrix_room && matrix_room != "") { + MX_Leave(matrix_room); + strunzone(matrix_room); + } + matrix_room = strzone(s); + MX_Join(matrix_room); + break; + case "sync": + MX_Sync(string_null); + break; + case "typing": + MX_Typing(true); + break; + case "say": + MX_Say(substring(command, argv_start_index(2), -1)); + break; + case "leave": + MX_Leave(matrix_room); + matrix_room = string_null; + break; + case "forget": + MX_Forget(matrix_room); + matrix_room = ""; + break; + } +} diff --git a/qcsrc/lib/matrix/command.qh b/qcsrc/lib/matrix/command.qh new file mode 100644 index 000000000..76459540f --- /dev/null +++ b/qcsrc/lib/matrix/command.qh @@ -0,0 +1,3 @@ +#pragma once + +#include "matrix.qh" diff --git a/qcsrc/lib/matrix/matrix.qc b/qcsrc/lib/matrix/matrix.qc new file mode 100644 index 000000000..a5ae3dc98 --- /dev/null +++ b/qcsrc/lib/matrix/matrix.qc @@ -0,0 +1,204 @@ +#include "matrix.qh" + +.string message; + +void MX_Nick_(entity fh, entity pass, int status); +void MX_Nick(string name) +{ + entity pass = new_pure(mx); + pass.message = name; + url_single_fopen( + sprintf("%s/_matrix/client/r0/profile/%s/displayname?access_token=%s", autocvar_matrix_server, matrix_user, matrix_access_token), + FILE_WRITE, + MX_Nick_, + pass + ); +} +void MX_Nick_(entity fh, entity pass, int status) +{ + switch (status) { + case URL_READY_CANWRITE: { + fh.url_verb = "PUT"; + fh.url_content_type = "application/json"; + url_fputs(fh, sprintf("{\"displayname\": \"%s\"}", pass.message)); + remove(pass); + url_fclose(fh); + break; + } + } +} + + +void MX_Messages_(entity fh, entity pass, int status); +void MX_Messages(string from) +{ + string s = sprintf("%s/_matrix/client/r0/events?room_id=%s&limit=50&timeout=30000&from=%s&access_token=%s", autocvar_matrix_server, matrix_room, from, matrix_access_token); + url_single_fopen( + s, + FILE_READ, + MX_Messages_, + NULL + ); +} +void MX_Messages_(entity fh, entity pass, int status) +{ + switch (status) { + default: { + LOG_WARNINGF("status: %d", status); + break; + } + case URL_READY_CLOSED: break; + case URL_READY_CANREAD: { + string json = ""; + for (string s; (s = url_fgets(fh)); ) { json = strcat(json, s, "\n"); } + url_fclose(fh); + int buf = json_parse(json, _json_parse_object); + EXPECT_NE(-1, buf); + for (int i = 0, n = stof(json_get(buf, "chunk.length")); i < n; ++i) { + MX_Handle(buf, sprintf("chunk.%d", i)); + } + MX_Messages(json_get(buf, "end")); + break; + } + } +} + + +void MX_Sync_(entity fh, entity pass, int status); +void MX_Sync(string since) +{ + string s = strcat(autocvar_matrix_server, "/_matrix/client/r0/sync?"); + if (since) { + s = strcat(s, + "since=", since, "&", + "timeout=30000&", + sprintf("filter={\"account_data\":{\"types\":[]},\"presence\":{\"types\":[]},\"room\":{\"rooms\":[\"%s\"]}}&", matrix_room) + ); + } else { + s = strcat(s, + "timeout=0&", + "filter={\"account_data\":{\"types\":[]},\"presence\":{\"types\":[]},\"room\":{\"rooms\":[]}}&" + ); + } + s = strcat(s, "access_token=", matrix_access_token); + url_single_fopen(s, FILE_READ, MX_Sync_, NULL); +} +void MX_Sync_(entity fh, entity pass, int status) +{ + switch (status) { + default: { + LOG_WARNINGF("status: %d", status); + break; + } + case URL_READY_CLOSED: break; + case URL_READY_CANREAD: { + string json = ""; + for (string s; (s = url_fgets(fh)); ) { json = strcat(json, s, "\n"); } + url_fclose(fh); + int buf = json_parse(json, _json_parse_object); + EXPECT_NE(-1, buf); + string arr = sprintf("rooms.join.%s.timeline.events", matrix_room); + for (int i = 0, n = stof(json_get(buf, sprintf("%s.length", arr))); i < n; ++i) { + MX_Handle(buf, sprintf("%s.%d", arr, i)); + } + MX_Sync(json_get(buf, "next_batch")); + break; + } + } +} + + +void MX_JLF_(entity fh, entity pass, int status); +void MX_Join(string room) +{ + url_single_fopen( + sprintf("%s/_matrix/client/r0/rooms/%s/join?access_token=%s", autocvar_matrix_server, matrix_room, matrix_access_token), + FILE_WRITE, + MX_JLF_, + NULL + ); +} +void MX_Leave(string room) +{ + url_single_fopen( + sprintf("%s/_matrix/client/r0/rooms/%s/leave?access_token=%s", autocvar_matrix_server, matrix_room, matrix_access_token), + FILE_WRITE, + MX_JLF_, + NULL + ); +} +void MX_Forget(string room) +{ + url_single_fopen( + sprintf("%s/_matrix/client/r0/rooms/%s/forget?access_token=%s", autocvar_matrix_server, matrix_room, matrix_access_token), + FILE_WRITE, + MX_JLF_, + NULL + ); +} +void MX_JLF_(entity fh, entity pass, int status) +{ + switch (status) { + case URL_READY_CANWRITE: { + fh.url_content_type = "application/json"; + url_fputs(fh, sprintf("{}", pass.message)); + url_fclose(fh); + break; + } + } +} + + +void MX_Typing_(entity fh, entity pass, int status); +void MX_Typing(bool state) +{ + entity pass = new_pure(mx); + pass.message = state ? "true" : "false"; + url_single_fopen( + sprintf("%s/_matrix/client/r0/rooms/%s/typing/%s?access_token=%s", autocvar_matrix_server, matrix_room, matrix_user, matrix_access_token), + FILE_WRITE, + MX_Typing_, + pass + ); +} +void MX_Typing_(entity fh, entity pass, int status) +{ + switch (status) { + case URL_READY_CANWRITE: { + fh.url_verb = "PUT"; + fh.url_content_type = "application/json"; + url_fputs(fh, sprintf("{\"typing\": %s, \"timeout\": 30000}", pass.message)); + remove(pass); + url_fclose(fh); + break; + } + } +} + + +void MX_Say_(entity fh, entity pass, int status); +void MX_Say(string body) +{ + static int txnid; + entity pass = new_pure(mx); + pass.message = strzone(body); + url_single_fopen( + sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message/%d?access_token=%s", autocvar_matrix_server, matrix_room, ++txnid, matrix_access_token), + FILE_WRITE, + MX_Say_, + pass + ); +} +void MX_Say_(entity fh, entity pass, int status) +{ + switch (status) { + case URL_READY_CANWRITE: { + fh.url_verb = "PUT"; + fh.url_content_type = "application/json"; + url_fputs(fh, sprintf("{\"msgtype\": \"m.text\", \"body\": \"%s\"}", pass.message)); + strunzone(pass.message); remove(pass); + url_fclose(fh); + break; + } + } +} diff --git a/qcsrc/lib/matrix/matrix.qh b/qcsrc/lib/matrix/matrix.qh new file mode 100644 index 000000000..076d8a293 --- /dev/null +++ b/qcsrc/lib/matrix/matrix.qh @@ -0,0 +1,17 @@ +#pragma once + +string autocvar_matrix_server = "http://matrix.org"; +string matrix_user; +string matrix_access_token; +string matrix_room; + +void MX_Messages(string from); +void MX_Nick(string name); +void MX_Join(string room); +void MX_Sync(string since); +void MX_Typing(bool state); +void MX_Say(string body); +void MX_Leave(string room); +void MX_Forget(string room); + +var void(int buf, string ancestor) MX_Handle; diff --git a/qcsrc/lib/test.qh b/qcsrc/lib/test.qh index af00f979c..d05ae28cd 100644 --- a/qcsrc/lib/test.qh +++ b/qcsrc/lib/test.qh @@ -46,19 +46,19 @@ bool RUN_ALL_TESTS(); #define EXPECT_FALSE(condition) EXPECT_EQ(false, condition) #define ASSERT_FALSE(condition) ASSERT_EQ(false, condition) -#define EXPECT_NE(val1, val2) EXPECT_TRUE(val1 != val2) +#define EXPECT_NE(val1, val2) EXPECT_TRUE((val1) != (val2)) #define ASSERT_NE(val1, val2) _TEST_ASSERT(EXPECT_NE(val1, val2)) -#define EXPECT_LT(val1, val2) EXPECT_TRUE(val1 < val2) +#define EXPECT_LT(val1, val2) EXPECT_TRUE((val1) < (val2)) #define ASSERT_LT(val1, val2) _TEST_ASSERT(EXPECT_LT(val1, val2)) -#define EXPECT_LE(val1, val2) EXPECT_TRUE(val1 <= val2) +#define EXPECT_LE(val1, val2) EXPECT_TRUE((val1) <= (val2)) #define ASSERT_LE(val1, val2) _TEST_ASSERT(EXPECT_LE(val1, val2)) -#define EXPECT_GT(val1, val2) EXPECT_TRUE(val1 > val2) +#define EXPECT_GT(val1, val2) EXPECT_TRUE((val1) > (val2)) #define ASSERT_GT(val1, val2) _TEST_ASSERT(EXPECT_GT(val1, val2)) -#define EXPECT_GE(val1, val2) EXPECT_TRUE(val1 >= val2) +#define EXPECT_GE(val1, val2) EXPECT_TRUE((val1) >= (val2)) #define ASSERT_GE(val1, val2) _TEST_ASSERT(EXPECT_GE(val1, val2)) #define EXPECT_NO_FATAL_FAILURE(statement) EXPECT_NO_FATAL_FAILURE_(statement, { }) diff --git a/qcsrc/lib/urllib.qc b/qcsrc/lib/urllib.qc index 80145d8b8..c4c700a72 100644 --- a/qcsrc/lib/urllib.qc +++ b/qcsrc/lib/urllib.qc @@ -7,6 +7,8 @@ const float URL_FH_STDOUT = -2; // URLs .string url_url; +.string url_content_type; +.string url_verb; .float url_wbuf; .float url_wbufpos; .float url_rbuf; @@ -96,6 +98,8 @@ void url_single_fopen(string url, int mode, url_ready_func rdy, entity pass) // create a writing end that does nothing yet e = new_pure(url_single_fopen_file); e.url_url = strzone(url); + e.url_content_type = "text/plain"; + e.url_verb = ""; e.url_fh = URL_FH_CURL; e.url_wbuf = buf_create(); if (e.url_wbuf < 0) @@ -231,7 +235,7 @@ void url_fclose(entity e) } // POST the data - if (!crypto_uri_postbuf(e.url_url, i + MIN_URL_ID, "text/plain", "", e.url_wbuf, 0)) + if (!crypto_uri_postbuf(e.url_url, i + MIN_URL_ID, e.url_content_type, e.url_verb, e.url_wbuf, 0)) { LOG_INFO("url_fclose: failure in crypto_uri_postbuf\n"); e.url_ready(e, e.url_ready_pass, URL_READY_ERROR); diff --git a/qcsrc/menu/_mod.inc b/qcsrc/menu/_mod.inc index 589808ec1..8fdd71d84 100644 --- a/qcsrc/menu/_mod.inc +++ b/qcsrc/menu/_mod.inc @@ -1,4 +1,5 @@ // generated file; do not modify #include "draw.qc" #include "item.qc" +#include "matrix.qc" #include "menu.qc" diff --git a/qcsrc/menu/matrix.qc b/qcsrc/menu/matrix.qc new file mode 100644 index 000000000..b0c4ec880 --- /dev/null +++ b/qcsrc/menu/matrix.qc @@ -0,0 +1,26 @@ +var void MX_Handle(int buf, string ancestor) +{ + string type = json_get(buf, strcat(ancestor, ".type")); + switch (type) { + case "m.typing": { + string arr = strcat(ancestor, ".content.user_ids"); + for (int i = 0, n = stof(json_get(buf, sprintf("%s.length", arr))); i < n; ++i) { + string s = json_get(buf, sprintf("%s.%d", arr, i)); + print("\{1}", s, " is typing...\n"); + } + break; + } + case "m.room.message": { + string msgtype = json_get(buf, strcat(ancestor, ".content.msgtype")); + switch (msgtype) { + case "m.text": { + string sender = json_get(buf, strcat(ancestor, ".sender")); + string body = json_get(buf, strcat(ancestor, ".content.body")); + if (body) print("\{1}", sender, ": ", body, "\n"); + break; + } + } + break; + } + } +} diff --git a/qcsrc/server/_mod.inc b/qcsrc/server/_mod.inc index d502c48ed..9d0531ac6 100644 --- a/qcsrc/server/_mod.inc +++ b/qcsrc/server/_mod.inc @@ -15,6 +15,7 @@ #include "ipban.qc" #include "item_key.qc" #include "mapvoting.qc" +#include "matrix.qc" #include "miscfunctions.qc" #include "playerdemo.qc" #include "portals.qc" diff --git a/qcsrc/server/cl_player.qc b/qcsrc/server/cl_player.qc index 42904695c..a749ec2de 100644 --- a/qcsrc/server/cl_player.qc +++ b/qcsrc/server/cl_player.qc @@ -664,20 +664,14 @@ void MoveToTeam(entity client, int team_colour, int type) * 0 = reject * -1 = fake accept */ -int Say(entity source, float teamsay, entity privatesay, string msgin, bool floodcontrol) +int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodcontrol) { - string msgstr, colorstr, cmsgstr, namestr, fullmsgstr, sourcemsgstr, fullcmsgstr, sourcecmsgstr, colorprefix; - float flood; - var .float flood_field; - float ret; - string privatemsgprefix = string_null; float privatemsgprefixlen = 0; - - if(!teamsay && !privatesay) - if(substring(msgin, 0, 1) == " ") - msgin = substring(msgin, 1, strlen(msgin) - 1); // work around DP say bug (say_team does not have this!) + if (!teamsay && !privatesay) if (substring(msgin, 0, 1) == " ") + msgin = substring(msgin, 1, -1); // work around DP say bug (say_team does not have this!) msgin = formatmessage(msgin); + string colorstr; if (!IS_PLAYER(source)) colorstr = "^0"; // black for spectators else if(teamplay) @@ -691,6 +685,11 @@ int Say(entity source, float teamsay, entity privatesay, string msgin, bool floo if(intermission_running) teamsay = false; + if (!source) { + colorstr = ""; + teamsay = false; + } + if(msgin != "") msgin = trigger_magicear_processmessage_forallears(source, teamsay, privatesay, msgin); @@ -705,18 +704,18 @@ int Say(entity source, float teamsay, entity privatesay, string msgin, bool floo } */ - if(autocvar_g_chat_teamcolors) - namestr = playername(source); - else - namestr = source.netname; + string namestr = ""; + if (source) + namestr = autocvar_g_chat_teamcolors ? playername(source) : source.netname; - if(strdecolorize(namestr) == namestr) - colorprefix = "^3"; - else - colorprefix = "^7"; + string colorprefix = (strdecolorize(namestr) == namestr) ? "^3" : "^7"; - if(msgin != "") - { + string msgstr, cmsgstr; + string privatemsgprefix = string_null; + int privatemsgprefixlen = 0; + if (msgin == "") { + msgstr = cmsgstr = ""; + } else { if(privatesay) { msgstr = strcat("\{1}\{13}* ", colorprefix, namestr, "^3 tells you: ^7"); @@ -750,23 +749,22 @@ int Say(entity source, float teamsay, entity privatesay, string msgin, bool floo msgin = strreplace("/me", strcat(colorprefix, namestr), msgin); msgstr = strcat("\{1}^4* ", "^7", msgin); } - else - msgstr = strcat("\{1}", colorprefix, namestr, "^7: ", msgin); + else { + msgstr = "\{1}"; + msgstr = strcat(msgstr, (namestr != "") ? strcat(colorprefix, namestr, "^7: ") : "^7"); + msgstr = strcat(msgstr, msgin); + } cmsgstr = ""; } msgstr = strcat(strreplace("\n", " ", msgstr), "\n"); // newlines only are good for centerprint } - else - { - msgstr = cmsgstr = ""; - } - fullmsgstr = msgstr; - fullcmsgstr = cmsgstr; + string fullmsgstr = msgstr; + string fullcmsgstr = cmsgstr; // FLOOD CONTROL - flood = 0; - flood_field = floodcontrol_chat; + int flood = 0; + var .float flood_field = floodcontrol_chat; if(floodcontrol) { float flood_spl; @@ -838,6 +836,7 @@ int Say(entity source, float teamsay, entity privatesay, string msgin, bool floo source.(flood_field) = flood = 0; } + string sourcemsgstr, sourcecmsgstr; if(flood == 2) // cannot happen for empty msgstr { if(autocvar_g_chat_flood_notify_flooder) @@ -873,6 +872,7 @@ int Say(entity source, float teamsay, entity privatesay, string msgin, bool floo if(privatesay) sourcemsgstr = strcat(privatemsgprefix, substring(sourcemsgstr, privatemsgprefixlen, -1)); + int ret; if(source.muted) { // always fake the message @@ -913,7 +913,7 @@ int Say(entity source, float teamsay, entity privatesay, string msgin, bool floo { sprint(source, sourcemsgstr); dedicated_print(msgstr); // send to server console too - FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && it.active_minigame == source.active_minigame, LAMBDA(sprint(it, msgstr))); + FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && it.active_minigame == source.active_minigame, sprint(it, msgstr)); } else if(teamsay > 0) // team message, only sent to team mates { @@ -921,26 +921,28 @@ int Say(entity source, float teamsay, entity privatesay, string msgin, bool floo dedicated_print(msgstr); // send to server console too if(sourcecmsgstr != "") centerprint(source, sourcecmsgstr); - FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source && it.team == source.team, LAMBDA( + FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source && it.team == source.team, { sprint(it, msgstr); if(cmsgstr != "") centerprint(it, cmsgstr); - )); + }); } else if(teamsay < 0) // spectator message, only sent to spectators { sprint(source, sourcemsgstr); dedicated_print(msgstr); // send to server console too - FOREACH_CLIENT(!IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source, LAMBDA(sprint(it, msgstr))); + FOREACH_CLIENT(!IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source, sprint(it, msgstr)); } - else if(sourcemsgstr != msgstr) // trimmed/server fixed message, sent to all players - { - sprint(source, sourcemsgstr); - dedicated_print(msgstr); // send to server console too - FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source, LAMBDA(sprint(it, msgstr))); - } - else - bprint(msgstr); // entirely normal message, sent to all players -- bprint sends to server console too. + else { + if (sourcemsgstr != msgstr) { // trimmed/server fixed message, sent to all players + sprint(source, sourcemsgstr); + dedicated_print(msgstr); // send to server console too + FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source, sprint(it, msgstr)); + } else { // entirely normal message, sent to all players -- bprint sends to server console too. + bprint(msgstr); + } + if (source) MX_Say(strcat(playername(source), "^7: ", msgin)); + } } return ret; diff --git a/qcsrc/server/matrix.qc b/qcsrc/server/matrix.qc new file mode 100644 index 000000000..b7d26decf --- /dev/null +++ b/qcsrc/server/matrix.qc @@ -0,0 +1,22 @@ +#include "matrix.qh" + +#include "cl_player.qh" + +var void MX_Handle(int buf, string ancestor) +{ + string type = json_get(buf, strcat(ancestor, ".type")); + switch (type) { + case "m.room.message": { + string msgtype = json_get(buf, strcat(ancestor, ".content.msgtype")); + switch (msgtype) { + case "m.text": { + string sender = json_get(buf, strcat(ancestor, ".sender")); + string body = json_get(buf, strcat(ancestor, ".content.body")); + if (sender != matrix_user && body) Say(NULL, false, NULL, body, false); + break; + } + } + break; + } + } +} diff --git a/qcsrc/server/matrix.qh b/qcsrc/server/matrix.qh new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/qcsrc/server/matrix.qh @@ -0,0 +1 @@ +#pragma once -- 2.39.2