Merge branch 'terencehill/scoreboard_columns_fix' into 'master'
authorMario <zacjardine@y7mail.com>
Thu, 31 Mar 2016 06:06:06 +0000 (06:06 +0000)
committerMario <zacjardine@y7mail.com>
Thu, 31 Mar 2016 06:06:06 +0000 (06:06 +0000)
Scoreboard columns fix

Fix various warnings starting race, cts and invasion (some score types are optional in those game modes)

See merge request !302

26 files changed:
.gitlab-ci.yml
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 77d4214..2e13641 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 87623da..85ba9f5 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 6cc322e..165ae07 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 da25978..ded599b 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 fb7122f..c5e0206 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 fd811e7..ecec32a 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 555c4b1..acdf198 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 cf3a51f..fd6ca20 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 af00f97..d05ae28 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 80145d8..c4c700a 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 589808e..8fdd71d 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 1b0666c..0f5c5a4 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 1ec53c6..9320a6e 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 d502c48..9d0531a 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 1847108..225ae85 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 9edcf8d..45d8b53 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 4290469..a749ec2 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 39ada82..0dfad90 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!