]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'terencehill/warmup_limit_fix' into 'master'
authorMario <zacjardine@y7mail.com>
Thu, 31 Mar 2016 06:06:37 +0000 (06:06 +0000)
committerMario <zacjardine@y7mail.com>
Thu, 31 Mar 2016 06:06:37 +0000 (06:06 +0000)
Warmup limit fix

See merge request !301

27 files changed:
.gitlab-ci.yml
qcsrc/client/scoreboard.qc
qcsrc/common/t_items.qc
qcsrc/common/vehicles/sv_vehicles.qh
qcsrc/common/vehicles/vehicle/racer.qc
qcsrc/lib/_all.inc
qcsrc/lib/_mod.inc
qcsrc/lib/json.qc
qcsrc/lib/matrix/_mod.inc [new file with mode: 0644]
qcsrc/lib/matrix/command.qc [new file with mode: 0644]
qcsrc/lib/matrix/command.qh [new file with mode: 0644]
qcsrc/lib/matrix/matrix.qc [new file with mode: 0644]
qcsrc/lib/matrix/matrix.qh [new file with mode: 0644]
qcsrc/lib/oo.qh
qcsrc/lib/test.qh
qcsrc/lib/urllib.qc
qcsrc/menu/_mod.inc
qcsrc/menu/matrix.qc [new file with mode: 0644]
qcsrc/menu/xonotic/dialog_settings_effects.qc
qcsrc/menu/xonotic/dialog_settings_video.qc
qcsrc/server/_mod.inc
qcsrc/server/bot/waypoints.qc
qcsrc/server/cl_client.qc
qcsrc/server/cl_player.qc
qcsrc/server/matrix.qc [new file with mode: 0644]
qcsrc/server/matrix.qh [new file with mode: 0644]
serverbench.cfg

index 77d421436fd236b2ec9944b2b6aee7fc1a8e7bb9..2e13641a06b69d0bd6e52a5c70d5510def66516c 100644 (file)
@@ -17,9 +17,14 @@ test_sv_game:
     - cd darkplaces && make sv-debug -j $(nproc) && export ENGINE="$PWD/darkplaces-dedicated -xonotic"
     - cd ..
 
+    - mkdir -p data/maps
     - wget -O data/g-23.pk3 http://beta.xonotic.org/autobuild-bsp/latest/g-23.pk3
+    - wget -O data/maps/g-23.mapinfo https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/g-23.mapinfo
+    - wget -O data/maps/g-23.waypoints https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/g-23.waypoints
+    - wget -O data/maps/g-23.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/g-23.waypoints.cache
+    - wget -O data/maps/g-23.waypoints.hardwired https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/g-23.waypoints.hardwired
     - make
-    - EXPECT=07fdfd9a19025920a599995730eb2a78
+    - EXPECT=814b0e609bcf01dc2a44f2b17fa366b4
     - HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg
       | tee /dev/stderr
       | grep '^:'
index 7dcf9876983761fb500f562da5fd62c026c1218f..cf43e7c1d585cf565f7dad46e614216e3f9c3137 100644 (file)
@@ -303,16 +303,24 @@ void Cmd_HUD_Help()
                "other gamemodes except DM.\n"));
 }
 
-#define HUD_DefaultColumnLayout() \
-"ping pl name | " \
-"-teams,cts,lms/kills +ft,tdm/kills -teams,lms/deaths +ft,tdm/deaths -teams,lms,rc,ka/suicides +ft,tdm/suicides -cts,dm,tdm,ka,ft/frags " /* tdm already has this in "score" */ \
-"-rc,cts,nb/dmg -rc,cts,nb/dmgtaken " \
-"+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes " \
-"+lms/lives +lms/rank " \
-"+kh/caps +kh/pushes +kh/destroyed " \
-"?+rc/laps ?+rc/time ?+rc/fastest " \
-"+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \
-"-lms,rc,nb/score"
+// NOTE: adding a gametype with ? to not warn for an optional field
+// make sure it's excluded in a previous exclusive rule, if any
+// otherwise the previous exclusive rule warns anyway
+// e.g. -teams,rc,cts,lms/kills ?+rc/kills
+#define SCOREBOARD_DEFAULT_COLUMNS \
+"ping pl name |" \
+" -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
+" -teams,lms/deaths +ft,tdm/deaths" \
+" -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
+" -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \
+" -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
+" +ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes" \
+" +lms/lives +lms/rank" \
+" +kh/caps +kh/pushes +kh/destroyed" \
+" ?+rc/laps ?+rc/time +rc,cts/fastest" \
+" +as/objectives +nb/faults +nb/goals" \
+" +ka/pickups +ka/bckills +ka/bctime +ft/revivals" \
+" -lms,rc,cts,inv,nb/score"
 
 void Cmd_HUD_SetFields(int argc)
 {
@@ -337,12 +345,12 @@ void Cmd_HUD_SetFields(int argc)
                argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
 
        if(argc < 3)
-               argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
+               argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
 
        if(argc == 3)
        {
                if(argv(2) == "default")
-                       argc = tokenizebyseparator(strcat("0 1 ", HUD_DefaultColumnLayout()), " ");
+                       argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
                else if(argv(2) == "all")
                {
                        string s;
@@ -397,7 +405,6 @@ void Cmd_HUD_SetFields(int argc)
                {
                        case "ping": hud_field[hud_num_fields] = SP_PING; break;
                        case "pl": hud_field[hud_num_fields] = SP_PL; break;
-                       case "pl": hud_field[hud_num_fields] = SP_PL; break;
                        case "kd": case "kdr": case "kdratio": case "k/d": hud_field[hud_num_fields] = SP_KDRATIO; break;
                        case "sum": case "diff": case "k-d": hud_field[hud_num_fields] = SP_SUM; break;
                        case "name": case "nick": hud_field[hud_num_fields] = SP_NAME; have_name = true; break;
index 87623da3630d62f7a98e1e20bea14cbf960accf6..85ba9f51b3471bac94a42f0d967f1dd7f2e7961b 100644 (file)
@@ -816,7 +816,7 @@ LABEL(pickup)
        other.last_pickup = time;
 
        Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(this), '0 0 0', 1);
-       _sound (other, CH_TRIGGER, (this.item_pickupsound ? this.item_pickupsound : Sound_fixpath(this.item_pickupsound_ent)), VOL_BASE, ATTEN_NORM);
+       _sound (other, (this.itemdef.instanceOfPowerup ? CH_TRIGGER_SINGLE : CH_TRIGGER), (this.item_pickupsound ? this.item_pickupsound : Sound_fixpath(this.item_pickupsound_ent)), VOL_BASE, ATTEN_NORM);
 
        if (this.classname == "droppedweapon")
                remove (this);
index 6cc322ef9ca610a8ead5dcd8543723d5a059aa4a..165ae07e6d92a7bb4eb4e0a3ec8ca9494237dcfa 100644 (file)
@@ -26,6 +26,8 @@ float autocvar_g_vehicles_vaporizer_damagerate = 0.001;
 float autocvar_g_vehicles_tag_damagerate = 5;
 float autocvar_g_vehicles_weapon_damagerate = 1;
 
+.float vehicle_last_trace;
+
 // flags:
 .int vehicle_flags;
 
index da259784f64a5c7c45a2bfe5192571b49b96a3d8..ded599b7d2a77cc5b481f65a36dd8ef858f646f3 100644 (file)
@@ -31,6 +31,8 @@ REGISTER_VEHICLE(RACER, NEW(Racer));
 
 bool autocvar_g_vehicle_racer = true;
 
+float autocvar_g_vehicle_racer_thinkrate = 0.05; // TODO: any higher causes it to sink in liquids
+
 float autocvar_g_vehicle_racer_speed_afterburn = 3000;
 // energy consumed per second
 float autocvar_g_vehicle_racer_afterburn_cost = 100;
@@ -182,7 +184,9 @@ bool racer_frame(entity this)
 
        vehicles_frame(vehic, this);
 
-       if(pointcontents(vehic.origin) != CONTENT_WATER)
+       traceline(vehic.origin, vehic.origin + '0 0 1', MOVE_NOMONSTERS, this);
+       int cont = trace_dpstartcontents;
+       if(cont & DPCONTENTS_WATER)
                vehic.air_finished = time + autocvar_g_vehicle_racer_water_time;
 
        if(IS_DEAD(vehic))
@@ -195,7 +199,11 @@ bool racer_frame(entity this)
 
        PHYS_INPUT_BUTTON_ZOOM(this) = PHYS_INPUT_BUTTON_CROUCH(this) = false;
 
-       crosshair_trace(this);
+       if(time >= vehic.vehicle_last_trace)
+       {
+               crosshair_trace(this);
+               vehic.vehicle_last_trace = time + autocvar_g_vehicle_racer_thinkrate;
+       }
 
        vehic.angles_x *= -1;
 
@@ -219,10 +227,9 @@ bool racer_frame(entity this)
        vector df = vehic.velocity * -autocvar_g_vehicle_racer_friction;
        //vehic.velocity_z = ftmp;
 
-       int cont = pointcontents(vehic.origin);
        if(this.movement)
        {
-               if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
+               if(cont & DPCONTENTS_LIQUIDSMASK)
                {
                        if(this.movement_x) { df += v_forward * ((this.movement_x > 0) ? autocvar_g_vehicle_racer_water_speed_forward : -autocvar_g_vehicle_racer_water_speed_forward); }
                        if(this.movement_y) { df += v_right * ((this.movement_y > 0) ? autocvar_g_vehicle_racer_water_speed_strafe : -autocvar_g_vehicle_racer_water_speed_strafe); }
@@ -264,7 +271,7 @@ bool racer_frame(entity this)
 
                vehic.wait = time;
 
-               if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
+               if(cont & DPCONTENTS_LIQUIDSMASK)
                {
                        vehic.vehicle_energy -= autocvar_g_vehicle_racer_waterburn_cost * PHYS_INPUT_TIMELENGTH;
                        df += (v_forward * autocvar_g_vehicle_racer_waterburn_speed);
@@ -298,7 +305,7 @@ bool racer_frame(entity this)
                sound (vehic.tur_head, CH_TRIGGER_SINGLE, SND_Null, VOL_VEHICLEENGINE, ATTEN_NORM);
        }
 
-       if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
+       if(cont & DPCONTENTS_LIQUIDSMASK)
                vehic.racer_watertime = time;
 
        float dforce = autocvar_g_vehicle_racer_downforce;
@@ -333,6 +340,7 @@ bool racer_frame(entity this)
 
        if(autocvar_g_vehicle_racer_rocket_locktarget)
        {
+               if(vehic.vehicle_last_trace == time + autocvar_g_vehicle_racer_thinkrate)
                vehicles_locktarget(vehic, (1 / autocvar_g_vehicle_racer_rocket_locking_time) * frametime,
                                                 (1 / autocvar_g_vehicle_racer_rocket_locking_releasetime) * frametime,
                                                 autocvar_g_vehicle_racer_rocket_locked_time);
@@ -400,35 +408,32 @@ bool racer_frame(entity this)
 
 void racer_think()
 {SELFPARAM();
-       self.nextthink = time;
-
-       float pushdeltatime = time - self.lastpushtime;
-       if (pushdeltatime > 0.15) pushdeltatime = 0;
-       self.lastpushtime = time;
-       if(!pushdeltatime) return;
+       this.nextthink = time + autocvar_g_vehicle_racer_thinkrate;
 
-       tracebox(self.origin, self.mins, self.maxs, self.origin - ('0 0 1' * autocvar_g_vehicle_racer_springlength), MOVE_NOMONSTERS, self);
+       tracebox(this.origin, this.mins, this.maxs, this.origin - ('0 0 1' * autocvar_g_vehicle_racer_springlength), MOVE_NOMONSTERS, this);
 
-       vector df = self.velocity * -autocvar_g_vehicle_racer_friction;
+       vector df = this.velocity * -autocvar_g_vehicle_racer_friction;
        df_z += (1 - trace_fraction) * autocvar_g_vehicle_racer_hoverpower + sin(time * 2) * (autocvar_g_vehicle_racer_springlength * 2);
 
        float forced = autocvar_g_vehicle_racer_upforcedamper;
 
-       int cont = pointcontents(self.origin - '0 0 64');
-       if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
+       //int cont = pointcontents(self.origin - '0 0 64');
+       traceline(this.origin - '0 0 64', this.origin - '0 0 64', MOVE_NOMONSTERS, this);
+       //if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
+       if(trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK)
        {
                forced = autocvar_g_vehicle_racer_water_upforcedamper;
-               self.velocity_z += 200;
+               this.velocity_z += 200;
        }
 
-       self.velocity += df * pushdeltatime;
-       if(self.velocity_z > 0)
-               self.velocity_z *= 1 - forced * pushdeltatime;
+       this.velocity += df * autocvar_g_vehicle_racer_thinkrate;
+       if(this.velocity_z > 0)
+               this.velocity_z *= 1 - forced * autocvar_g_vehicle_racer_thinkrate;
 
-       self.angles_x *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * pushdeltatime);
-       self.angles_z *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * pushdeltatime);
+       this.angles_x *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * autocvar_g_vehicle_racer_thinkrate);
+       this.angles_z *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * autocvar_g_vehicle_racer_thinkrate);
 
-       CSQCMODEL_AUTOUPDATE(self);
+       CSQCMODEL_AUTOUPDATE(this);
 }
 
 void racer_exit(float eject)
index fb7122fd8be2c5f5b6a3d7ee8dd7dc0bf059922c..c5e020660d3980f5f029bf97af8112b17603f6f6 100644 (file)
@@ -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"
index fd811e7a9caaa642ab6e60f96217b50ba0532974..ecec32a053299816c41560b64faa6d92e013d4f0 100644 (file)
@@ -1,5 +1,6 @@
 // generated file; do not modify
 #include "angle.qc"
+#include "json.qc"
 #include "p2mathlib.qc"
 #include "random.qc"
 #include "sortlist.qc"
index 555c4b10cc75a6877eb3450bedcde8a29aab87cb..acdf198e835bc83206df152766913225a27bb90c 100644 (file)
@@ -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 (file)
index 0000000..84522f1
--- /dev/null
@@ -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 (file)
index 0000000..27c7ec9
--- /dev/null
@@ -0,0 +1,48 @@
+#include "command.qh"
+
+#include <common/command/all.qh>
+
+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 (file)
index 0000000..7645954
--- /dev/null
@@ -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 (file)
index 0000000..a5ae3dc
--- /dev/null
@@ -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 (file)
index 0000000..076d8a2
--- /dev/null
@@ -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;
index cf3a51f3aeaa10dc503b14a63dcad4c4220ec0a2..fd6ca20365000cc0fa7553e57b560823452eec24 100644 (file)
@@ -50,10 +50,10 @@ entity __spawn(string _classname, string _sourceLoc, bool pure)
        if (pure) {
                make_pure(this);
                #ifdef CSQC
-               setorigin(this, world.mins + '0 0 20');
+               setorigin(this, (world.mins + world.maxs) * 0.5);
                #endif
                #ifdef SVQC
-        setorigin(this, world.maxs - '0 0 20');
+        setorigin(this, (world.mins + world.maxs) * 0.5);
         #endif
        }
        return this;
index af00f979ca0175dd8a5316e64525763b819adbf4..d05ae28cd903b76edc4770204b142d5f8689c589 100644 (file)
@@ -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, { })
index 80145d8b84c15142b97be992968e38601e1f7c5f..c4c700a72d6b8341e9e561af642d638853c78e9d 100644 (file)
@@ -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);
index 589808ec117bdeb2cf8924137193e0c4bc34766d..8fdd71d84cfc8388c4d0d472aa77c9f047ca3f55 100644 (file)
@@ -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 (file)
index 0000000..b0c4ec8
--- /dev/null
@@ -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;
+        }
+    }
+}
index 1b0666c75afd59f3c4ad86f10ad13a034bf6b709..0f5c5a4af225bf2dd34cf7c9f31b7975d9076159 100644 (file)
@@ -135,6 +135,7 @@ void XonoticEffectsSettingsTab_fill(entity me)
        me.TR(me);
                me.TD(me, 1, 1, e = makeXonoticCheckBox_T(1, "mod_q3bsp_nolightmaps", _("Use lightmaps"),
                        _("Use high resolution lightmaps, which will look pretty but use up some extra video memory (default: enabled)")));
+                       e.applyButton = effectsApplyButton;
                me.TD(me, 1, 1, e = makeXonoticCheckBox_T(0, "r_glsl_deluxemapping", _("Deluxe mapping"),
                        _("Use per-pixel lighting effects (default: enabled)")));
                        setDependentAND(e, "vid_gl20", 1, 1, "mod_q3bsp_nolightmaps", 0, 0);
index 1ec53c623eebe6bec086520cf9e52b2c7f788958..9320a6ebf2101603fe79e478d3b1abf347f8fe7e 100644 (file)
@@ -86,6 +86,7 @@ void XonoticVideoSettingsTab_fill(entity me)
                        e.addValue(e, _("4x"), "4");
                        e.configureXonoticTextSliderValues(e);
                        setDependent(e, "r_viewfbo", 0, 0);
+                       e.applyButton = videoApplyButton;
        me.TR(me);
                me.TD(me, 1, 3, e = makeXonoticCheckBoxEx(2, 0, "r_viewfbo", _("High-quality frame buffer")));
                        setDependent(e, "vid_samples", 1, 1);
@@ -154,6 +155,7 @@ void XonoticVideoSettingsTab_fill(entity me)
                        _("Make the CPU wait for the GPU to finish each frame, can help with some strange input or video lag on some machines (default: disabled)")));
        me.TR(me);
                me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "vid_gl20", _("Use OpenGL 2.0 shaders (GLSL)")));
+                       e.applyButton = videoApplyButton;
        me.TR(me);
                me.TDempty(me, 0.2);
                me.TD(me, 1, 2.8, e = makeXonoticCheckBox_T(0, "v_glslgamma", _("Use GLSL to handle color control"),
index d502c48ed35b35c8f28c2806364bb0fd13db00bb..9d0531ac62c5a9bca1211a1bfb71a29bf7797a0b 100644 (file)
@@ -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"
index 184710818c1aac266a32e620695cd97fbbd66b1f..225ae859754952e1ca5b6e411bb6ca61d2f84084 100644 (file)
@@ -32,6 +32,7 @@ entity waypoint_spawn(vector m1, vector m2, float f)
        w = new(waypoint);
        w.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
        w.wpflags = f;
+       w.solid = SOLID_TRIGGER;
        setorigin(w, (m1 + m2) * 0.5);
        setsize(w, m1 - w.origin, m2 - w.origin);
        if (vlen(w.size) > 0)
index 9edcf8d5f0806097e3ea7b058d28e4f4c8e662ad..45d8b53582ec0df2d7ec7406240a464a14a6e7de 100644 (file)
@@ -569,7 +569,8 @@ void PutClientInServer()
                this.oldorigin = this.origin;
                this.prevorigin = this.origin;
                this.lastteleporttime = time; // prevent insane speeds due to changing origin
-        this.hud = HUD_NORMAL;
+               this.conveyor = NULL; // prevent conveyors at the previous location from moving a freshly spawned player
+               this.hud = HUD_NORMAL;
 
                this.event_damage = PlayerDamage;
 
index 42904695c693b35e39835bed217e3c78ef2635c8..a749ec2def81b8b60c1faaf8b9bf55d4270fda0a 100644 (file)
@@ -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 (file)
index 0000000..b7d26de
--- /dev/null
@@ -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 (file)
index 0000000..6f70f09
--- /dev/null
@@ -0,0 +1 @@
+#pragma once
index 39ada82f782b62bbeae93db7926f7be05f9f49d9..0dfad90ec964e6396afcc79dbfd51644a1c873c9 100644 (file)
@@ -18,6 +18,7 @@ sv_eventlog 1
 sv_logscores_bots 1
 g_weaponarena all
 g_weaponarena_random 2
+g_playerstats_gamereport_uri ""
 
 // Start!