]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'terencehill/player_sorting' into 'master'
authorterencehill <piuntn@gmail.com>
Sun, 16 Oct 2022 21:28:09 +0000 (21:28 +0000)
committerterencehill <piuntn@gmail.com>
Sun, 16 Oct 2022 21:28:09 +0000 (21:28 +0000)
Fix mismatching player sorting between server and client

See merge request xonotic/xonotic-data.pk3dir!1026

.gitlab-ci.yml
qcsrc/client/hud/panel/scoreboard.qc
qcsrc/common/scores.qh
qcsrc/server/scores.qc
qcsrc/server/scores_rules.qc

index 5d394c8ea6101e2ba286d4894e36559c4aa4a14a..6a12c61c1f291c6ae49247e39ee27cbfdadb7be4 100644 (file)
@@ -55,7 +55,7 @@ test_sv_game:
     - wget -O data/maps/stormkeep.waypoints https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints\r
     - wget -O data/maps/stormkeep.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints.cache\r
 \r
-    - EXPECT=5bbaf1e67c9a18ac159614a7e59b8d1e\r
+    - EXPECT=757e5413dab5085242502ca47740b83c\r
     - HASH=$(${ENGINE} +timestamps 1 +exec serverbench.cfg\r
       | tee /dev/stderr\r
       | sed -e 's,^\[[^]]*\] ,,'\r
index f0661d6d7581856d6b3fdd9fde5383a88fc5bf8f..9256d162bc83721c86c06e94f075a5fb4d81391f 100644 (file)
@@ -499,8 +499,6 @@ float HUD_Scoreboard_InputEvent(float bInputType, float nPrimary, float nSeconda
 void PrintScoresLabels() { Label_getInfo(string_null, 1); }
 string TranslateScoresLabel(string label) { return Label_getInfo(label, 0); }
 
-#define SB_EXTRA_SORTING_FIELDS 5
-PlayerScoreField sb_extra_sorting_field[SB_EXTRA_SORTING_FIELDS];
 void Scoreboard_InitScores()
 {
        int i, f;
@@ -508,18 +506,13 @@ void Scoreboard_InitScores()
        ps_primary = ps_secondary = NULL;
        ts_primary = ts_secondary = -1;
        FOREACH(Scores, true, {
+               if(scores_flags(it) & SFL_NOT_SORTABLE)
+                       continue;
                f = (scores_flags(it) & SFL_SORT_PRIO_MASK);
                if(f == SFL_SORT_PRIO_PRIMARY)
                        ps_primary = it;
                if(f == SFL_SORT_PRIO_SECONDARY)
                        ps_secondary = it;
-               if(ps_primary == it || ps_secondary == it)
-                       continue;
-               if (scores_label(it) == "kills")      sb_extra_sorting_field[0] = it;
-               if (scores_label(it) == "deaths")     sb_extra_sorting_field[1] = it;
-               if (scores_label(it) == "suicides")   sb_extra_sorting_field[2] = it;
-               if (scores_label(it) == "dmg")        sb_extra_sorting_field[3] = it;
-               if (scores_label(it) == "dmgtaken")   sb_extra_sorting_field[4] = it;
        });
        if(ps_secondary == NULL)
                ps_secondary = ps_primary;
@@ -607,27 +600,20 @@ float Scoreboard_ComparePlayerScores(entity left, entity right)
                return false;
        }
 
-       entity fld = NULL;
-       int r;
-       for (int i = -2; i < SB_EXTRA_SORTING_FIELDS; ++i)
-       {
-               if (i < 0)
-               {
-                       if (!fld) fld = ps_primary;
-                       else if (ps_secondary == ps_primary) continue;
-                       else fld = ps_secondary;
-               }
-               else
-               {
-                       fld = sb_extra_sorting_field[i];
-                       if (fld == ps_primary || fld == ps_secondary) continue;
-               }
-               if (!fld) continue;
+       int res = Scoreboard_CompareScore(left.scores(ps_primary), right.scores(ps_primary), scores_flags(ps_primary));
+       if (res >= 0) return res;
 
-               r = Scoreboard_CompareScore(left.scores(fld), right.scores(fld), scores_flags(fld));
-               if (r >= 0) return r;
+       if (ps_secondary && ps_secondary != ps_primary)
+       {
+               res = Scoreboard_CompareScore(left.scores(ps_secondary), right.scores(ps_secondary), scores_flags(ps_secondary));
+               if (res >= 0) return res;
        }
 
+       FOREACH(Scores, (it != ps_primary && it != ps_secondary), {
+               res = Scoreboard_CompareScore(left.scores(it), right.scores(it), scores_flags(it));
+               if (res >= 0) return res;
+       });
+
        if (left.sv_entnum < right.sv_entnum)
                return true;
 
index 45af93992aacdf33f7d8338867fe73a43807b2a3..3449f2b53ee5b950b2d2916d8e593509decdece3 100644 (file)
@@ -5,7 +5,8 @@
 #define REGISTER_SP(id) REGISTER(Scores, SP, id, m_id, new_pure(PlayerScoreField))
 REGISTRY(Scores, MAX_SCORE);
 REGISTER_REGISTRY(Scores)
-REGISTRY_SORT(Scores);
+// do not sort alphabetically, player sort priority is based on score registration order
+//REGISTRY_SORT(Scores);
 REGISTRY_CHECK(Scores);
 
 REGISTRY_DEFINE_GET(Scores, NULL)
@@ -14,70 +15,41 @@ STATIC_INIT(Scores_renumber) { FOREACH(Scores, true, it.m_id = i); }
 /*
  * Score indices
  */
-
 #ifdef GAMEQC
-// fields not networked via the score system
-REGISTER_SP(END);
-
-REGISTER_SP(PING);
-REGISTER_SP(PL);
-REGISTER_SP(NAME);
-REGISTER_SP(SEPARATOR);
-
-REGISTER_SP(KDRATIO); // kills / deaths
-REGISTER_SP(SUM); // kills - deaths
-REGISTER_SP(FRAGS); // kills - suicides
-
 // networked fields
+// NOTE: score registration order is used as player sort priority (after primary and secondary)
 
-REGISTER_SP(SCORE);
-
-REGISTER_SP(DMG);
-REGISTER_SP(DMGTAKEN);
+// TODO: move gamemode scores to gamemode files
+// TODO: allow gamemodes to fully customize player sorting priority, even the common ones
 
-REGISTER_SP(KILLS);
-REGISTER_SP(DEATHS);
-REGISTER_SP(SUICIDES);
-REGISTER_SP(TEAMKILLS);
-
-REGISTER_SP(ELO);
-
-REGISTER_SP(FPS);
-
-// TODO: move to common mutators
-
-REGISTER_SP(RACE_TIME);
 REGISTER_SP(RACE_LAPS);
+REGISTER_SP(RACE_TIME);
 REGISTER_SP(RACE_FASTEST);
 
-//REGISTER_SP(CTS_TIME);
-//REGISTER_SP(CTS_LAPS);
-//REGISTER_SP(CTS_FASTEST);
-
 REGISTER_SP(ASSAULT_OBJECTIVES);
 
-REGISTER_SP(CTF_PICKUPS);
+REGISTER_SP(CTF_CAPS);
 REGISTER_SP(CTF_FCKILLS);
 REGISTER_SP(CTF_RETURNS);
-REGISTER_SP(CTF_CAPS);
-REGISTER_SP(CTF_CAPTIME);
 REGISTER_SP(CTF_DROPS);
+REGISTER_SP(CTF_PICKUPS);
+REGISTER_SP(CTF_CAPTIME);
 
 REGISTER_SP(DOM_TAKES);
 REGISTER_SP(DOM_TICKS);
 
 REGISTER_SP(FREEZETAG_REVIVALS);
 
-REGISTER_SP(KEEPAWAY_PICKUPS);
 REGISTER_SP(KEEPAWAY_BCTIME);
 REGISTER_SP(KEEPAWAY_CARRIERKILLS);
+REGISTER_SP(KEEPAWAY_PICKUPS);
 
-REGISTER_SP(KH_PICKUPS);
 REGISTER_SP(KH_CAPS);
 REGISTER_SP(KH_KCKILLS);
-REGISTER_SP(KH_PUSHES);
-REGISTER_SP(KH_DESTROYS);
 REGISTER_SP(KH_LOSSES);
+REGISTER_SP(KH_DESTROYS);
+REGISTER_SP(KH_PUSHES);
+REGISTER_SP(KH_PICKUPS);
 
 REGISTER_SP(LMS_RANK);
 REGISTER_SP(LMS_LIVES);
@@ -85,8 +57,31 @@ REGISTER_SP(LMS_LIVES);
 REGISTER_SP(NEXBALL_GOALS);
 REGISTER_SP(NEXBALL_FAULTS);
 
-REGISTER_SP(ONS_TAKES);
 REGISTER_SP(ONS_CAPS);
+REGISTER_SP(ONS_TAKES);
+
+REGISTER_SP(SCORE);
+REGISTER_SP(KILLS);
+REGISTER_SP(DEATHS);
+REGISTER_SP(TEAMKILLS);
+REGISTER_SP(SUICIDES);
+REGISTER_SP(DMG);
+REGISTER_SP(DMGTAKEN);
+
+REGISTER_SP(ELO); // not sortable
+REGISTER_SP(FPS); // not sortable
+
+// fields not networked via the score system
+REGISTER_SP(END);
+
+REGISTER_SP(PING);
+REGISTER_SP(PL);
+REGISTER_SP(NAME);
+REGISTER_SP(SEPARATOR);
+
+REGISTER_SP(KDRATIO); // kills / deaths
+REGISTER_SP(SUM); // kills - deaths
+REGISTER_SP(FRAGS); // kills - suicides
 #endif
 
 
@@ -117,15 +112,18 @@ const int SFL_RANK = BIT(5);
  */
 const int SFL_TIME = BIT(6);
 
+const int SFL_NOT_SORTABLE = BIT(7); // don't sort by this field
+
 // not an extra constant yet
 #define SFL_ZERO_IS_WORST SFL_TIME
 
 /**
  * Scoring priority (NOTE: PRIMARY is used for fraglimit)
+ * NOTE: SFL_SORT_PRIO_SECONDARY value must be lower than SFL_SORT_PRIO_PRIMARY's
  */
-const int SFL_SORT_PRIO_SECONDARY = 4;
-const int SFL_SORT_PRIO_PRIMARY = 8;
-const int SFL_SORT_PRIO_MASK = 12;
+const int SFL_SORT_PRIO_SECONDARY = BIT(2);
+const int SFL_SORT_PRIO_PRIMARY = BIT(3);
+const int SFL_SORT_PRIO_MASK = SFL_SORT_PRIO_PRIMARY | SFL_SORT_PRIO_SECONDARY;
 
 #define IS_INCREASING(x) ( (x) & SFL_LOWER_IS_BETTER )
 #define IS_DECREASING(x) ( !((x) & SFL_LOWER_IS_BETTER) )
index 512f61ad231ae5d4255ec197a7b4d0667416220a..2cedc4aded31c9abce24c8fa5bafdcbe92987bac 100644 (file)
@@ -23,36 +23,35 @@ var .float scores_primary;
 var .float teamscores_primary;
 float scores_flags_primary;
 float teamscores_flags_primary;
+var .float scores_secondary;
+float scores_flags_secondary;
 
-vector ScoreField_Compare(entity t1, entity t2, .float field, float fieldflags, vector previous, bool strict) // returns: cmp value, best prio
+// returns cmp value
+int ScoreField_Compare(entity t1, entity t2, .float field, float fieldflags, int previous)
 {
-       if(!strict && !(fieldflags & SFL_SORT_PRIO_MASK)) // column does not sort
-               return previous;
-       if((fieldflags & SFL_SORT_PRIO_MASK) < previous.y)
+       if(fieldflags & SFL_NOT_SORTABLE) // column does not sort
                return previous;
        if (t1.(field) == t2.(field))
                return previous;
 
-       previous.y = fieldflags & SFL_SORT_PRIO_MASK;
-
        if(fieldflags & SFL_ZERO_IS_WORST)
        {
                if (t1.(field) == 0)
                {
-                       previous.x = -1;
+                       previous = -1;
                        return previous;
                }
                else if (t2.(field) == 0)
                {
-                       previous.x = +1;
+                       previous = +1;
                        return previous;
                }
        }
 
        if (fieldflags & SFL_LOWER_IS_BETTER)
-               previous.x = (t2.(field) - t1.(field));
+               previous = (t2.(field) - t1.(field));
        else
-               previous.x = (t1.(field) - t2.(field));
+               previous = (t1.(field) - t2.(field));
 
        return previous;
 }
@@ -139,23 +138,28 @@ float TeamScore_Add(entity player, float scorefield, float score)
        return TeamScore_AddToTeam(player.team, scorefield, score);
 }
 
-float TeamScore_Compare(entity t1, entity t2, bool strict)
+// strict: compare others fields too besides primary and secondary
+int TeamScore_Compare(entity t1, entity t2, bool strict)
 {
        if(!t1 || !t2) return (!t2) - !t1;
 
-       vector result = '0 0 0';
-       float i;
-       for(i = 0; i < MAX_TEAMSCORE; ++i)
+       // supporting MAX_TEAMSCORE > 2 requires keeping track of primary and secondary teamscore
+       if (MAX_TEAMSCORE > 2)
+               error("MAX_TEAMSCORE > 2 not supported");
+
+       // first compare primary, then others (don't check secondary flag since there are only 2 teamscores)
+       int result = 0;
+       int i = boolean(teamscores_primary && teamscores_primary == teamscores(1));
+       result = ScoreField_Compare(t1, t2, teamscores(i), teamscores_flags(i), result);
+       if (result == 0 && strict)
        {
-               var .float f;
-               f = teamscores(i);
-               result = ScoreField_Compare(t1, t2, f, teamscores_flags(i), result, strict);
+               i = (i + 1) % MAX_TEAMSCORE;
+               result = ScoreField_Compare(t1, t2, teamscores(i), teamscores_flags(i), result);
+               if (result == 0)
+                       result = t1.team - t2.team;
        }
 
-       if (result.x == 0 && strict)
-               result.x = t1.team - t2.team;
-
-       return result.x;
+       return result;
 }
 
 /*
@@ -171,6 +175,11 @@ void ScoreInfo_SetLabel_PlayerScore(PlayerScoreField i, string label, float scor
                scores_primary = scores(i);
                scores_flags_primary = scoreflags;
        }
+       else if((scoreflags & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_SECONDARY)
+       {
+               scores_secondary = scores(i);
+               scores_flags_secondary = scoreflags;
+       }
        if(label != "")
        {
                PlayerStats_GameReport_AddEvent(strcat(PLAYERSTATS_TOTAL, label));
@@ -403,20 +412,33 @@ float PlayerTeamScore_Add(entity player, PlayerScoreField pscorefield, float tsc
        return r;
 }
 
+// strict: compare others fields too besides primary and secondary
 float PlayerScore_Compare(entity t1, entity t2, bool strict)
 {
        if(!t1 || !t2) return (!t2) - !t1;
 
-       vector result = '0 0 0';
-       FOREACH(Scores, true, {
-               var .float f = scores(it);
-               result = ScoreField_Compare(t1, t2, f, scores_flags(it), result, strict);
-       });
+       int result = 0;
+       result = ScoreField_Compare(t1, t2, scores_primary, scores_flags_primary, result);
+       // NOTE: if (scores_secondary) doesn't work because it's a field pointer
+       if (result == 0 && scores_flags_secondary)
+               result = ScoreField_Compare(t1, t2, scores_secondary, scores_flags_secondary, result);
 
-       if (result.x == 0 && strict)
-               result.x = t1.owner.playerid - t2.owner.playerid;
+       if (result == 0 && strict)
+       {
+               FOREACH(Scores, true, {
+                       if (scores_flags(it) & SFL_SORT_PRIO_MASK)
+                               continue;
+                       if (scores_label(it) == "")
+                               continue;
+                       var .float f = scores(it);
+                       result = ScoreField_Compare(t1, t2, f, scores_flags(it), result);
+                       if (result) break;
+               });
+               if (result == 0)
+                       result = t1.owner.playerid - t2.owner.playerid;
+       }
 
-       return result.x;
+       return result;
 }
 
 void WinningConditionHelper(entity this)
@@ -461,7 +483,7 @@ void WinningConditionHelper(entity this)
                for(t = 0; t < 16; ++t)
                {
                        sk = teamscorekeepers[t];
-                       c = TeamScore_Compare(winnerscorekeeper, sk, 1);
+                       c = TeamScore_Compare(winnerscorekeeper, sk, true);
                        if(c < 0)
                        {
                                WinningConditionHelper_secondteam = WinningConditionHelper_winnerteam;
@@ -471,7 +493,7 @@ void WinningConditionHelper(entity this)
                        }
                        else
                        {
-                               c = TeamScore_Compare(secondscorekeeper, sk, 1);
+                               c = TeamScore_Compare(secondscorekeeper, sk, true);
                                if(c < 0)
                                {
                                        WinningConditionHelper_secondteam = t + 1;
@@ -480,7 +502,7 @@ void WinningConditionHelper(entity this)
                        }
                }
 
-               WinningConditionHelper_equality = (TeamScore_Compare(winnerscorekeeper, secondscorekeeper, 0) == 0);
+               WinningConditionHelper_equality = (TeamScore_Compare(winnerscorekeeper, secondscorekeeper, false) == 0);
                if(WinningConditionHelper_equality)
                        WinningConditionHelper_winnerteam = WinningConditionHelper_secondteam = -1;
 
@@ -708,7 +730,8 @@ string GetTeamScoreString(float tm, float shortString)
        return out;
 }
 
-float PlayerTeamScore_Compare(entity p1, entity p2, float teams, bool strict)
+// strict: compare others fields too besides primary and secondary
+int PlayerTeamScore_Compare(entity p1, entity p2, float teams, bool strict)
 {
        if(teams && teamscores_entities_count)
        {
index 64dfbb03b20529ceebfd1f7bbf1140fbe71e00b6..c1b7f4c5689a13b877641ae9b7ea252a22089fd5 100644 (file)
@@ -43,7 +43,8 @@ void ScoreRules_basics(int teams, float sprio, float stprio, float score_enabled
        if (!INDEPENDENT_PLAYERS)
        {
                ScoreInfo_SetLabel_PlayerScore(SP_SUICIDES, "suicides", SFL_LOWER_IS_BETTER);
-               ScoreInfo_SetLabel_PlayerScore(SP_TEAMKILLS, "teamkills", SFL_LOWER_IS_BETTER);
+               if (teamplay)
+                       ScoreInfo_SetLabel_PlayerScore(SP_TEAMKILLS, "teamkills", SFL_LOWER_IS_BETTER);
        }
 
        if(score_enabled)
@@ -51,10 +52,10 @@ void ScoreRules_basics(int teams, float sprio, float stprio, float score_enabled
 
        ScoreInfo_SetLabel_PlayerScore(SP_DMG, "dmg", 0);
        ScoreInfo_SetLabel_PlayerScore(SP_DMGTAKEN, "dmgtaken", SFL_LOWER_IS_BETTER);
-       ScoreInfo_SetLabel_PlayerScore(SP_ELO, "elo", 0);
 
+       ScoreInfo_SetLabel_PlayerScore(SP_ELO, "elo", SFL_NOT_SORTABLE);
        if(STAT(SHOWFPS))
-               ScoreInfo_SetLabel_PlayerScore(SP_FPS, "fps", 0);
+               ScoreInfo_SetLabel_PlayerScore(SP_FPS, "fps", SFL_NOT_SORTABLE);
 }
 
 void ScoreRules_basics_end()