]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
moving playerstats code from server to common
authorJan Behrens <zykure@web.de>
Sat, 3 Aug 2013 19:49:44 +0000 (21:49 +0200)
committerJan Behrens <zykure@web.de>
Sat, 3 Aug 2013 19:49:44 +0000 (21:49 +0200)
qcsrc/common/playerstats.qc [new file with mode: 0644]
qcsrc/common/playerstats.qh [new file with mode: 0644]
qcsrc/menu/progs.src
qcsrc/server/defs.qh
qcsrc/server/miscfunctions.qc
qcsrc/server/playerstats.qc [deleted file]
qcsrc/server/playerstats.qh [deleted file]
qcsrc/server/progs.src

diff --git a/qcsrc/common/playerstats.qc b/qcsrc/common/playerstats.qc
new file mode 100644 (file)
index 0000000..b6f795e
--- /dev/null
@@ -0,0 +1,537 @@
+#ifdef SVQC
+
+float playerstats_db;
+string teamstats_last;
+string playerstats_last;
+string events_last;
+.float playerstats_addedglobalinfo;
+.string playerstats_id;
+
+void PlayerStats_Init() // initiated before InitGameplayMode so that scores are added properly
+{
+       string uri;
+       playerstats_db = -1;
+       playerstats_waitforme = TRUE;
+       uri = autocvar_g_playerstats_uri;
+       if(uri == "")
+               return;
+       playerstats_db = db_create();
+       if(playerstats_db >= 0)
+               playerstats_waitforme = FALSE; // must wait for it at match end
+
+       serverflags |= SERVERFLAG_PLAYERSTATS;
+
+       PlayerStats_AddEvent(PLAYERSTATS_ALIVETIME);
+       PlayerStats_AddEvent(PLAYERSTATS_AVGLATENCY);
+       PlayerStats_AddEvent(PLAYERSTATS_WINS);
+       PlayerStats_AddEvent(PLAYERSTATS_MATCHES);
+       PlayerStats_AddEvent(PLAYERSTATS_JOINS);
+       PlayerStats_AddEvent(PLAYERSTATS_SCOREBOARD_VALID);
+       PlayerStats_AddEvent(PLAYERSTATS_SCOREBOARD_POS);
+       PlayerStats_AddEvent(PLAYERSTATS_RANK);
+
+    // accuracy stats
+    entity w;
+    float i;
+    for(i = WEP_FIRST; i <= WEP_LAST; ++i)
+    {
+        w = get_weaponinfo(i);
+
+        PlayerStats_AddEvent(strcat("acc-", w.netname, "-hit"));
+        PlayerStats_AddEvent(strcat("acc-", w.netname, "-fired"));
+
+        PlayerStats_AddEvent(strcat("acc-", w.netname, "-cnt-hit"));
+        PlayerStats_AddEvent(strcat("acc-", w.netname, "-cnt-fired"));
+
+        PlayerStats_AddEvent(strcat("acc-", w.netname, "-frags"));
+    }
+
+       PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_3);
+       PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_5);
+       PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_10);
+       PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_15);
+       PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_20);
+       PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_25);
+       PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_30);
+       PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_BOTLIKE);
+       PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD);
+       PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM);
+}
+
+void PlayerStats_AddPlayer(entity e)
+{
+       string s;
+
+       if(playerstats_db < 0)
+               return;
+       if(e.playerstats_id)
+               return;
+
+       s = string_null;
+       if(e.crypto_idfp != "" && e.cvar_cl_allow_uidtracking == 1)
+               s = e.crypto_idfp;
+       else if(IS_BOT_CLIENT(e))
+               s = sprintf("bot#%g#%s", skill, e.cleanname);
+
+       if((s == "") || find(world, playerstats_id, s)) // already have one of the ID - next one can't be tracked then!
+       {
+               if(IS_BOT_CLIENT(e))
+                       s = sprintf("bot#%d", e.playerid);
+               else
+                       s = sprintf("player#%d", e.playerid);
+       }
+
+       e.playerstats_id = strzone(s);
+
+       string key;
+       key = sprintf("%s:*", e.playerstats_id);
+
+       string p;
+       p = db_get(playerstats_db, key);
+       if(p == "")
+       {
+               if(playerstats_last)
+               {
+                       db_put(playerstats_db, key, playerstats_last);
+                       strunzone(playerstats_last);
+               }
+               else
+                       db_put(playerstats_db, key, "#");
+               playerstats_last = strzone(e.playerstats_id);
+       }
+}
+
+void PlayerStats_AddTeam(float t)
+{
+       if(playerstats_db < 0)
+               return;
+
+       string key;
+       key = sprintf("%d", t);
+
+       string p;
+       p = db_get(playerstats_db, key);
+       if(p == "")
+       {
+               if(teamstats_last)
+               {
+                       db_put(playerstats_db, key, teamstats_last);
+                       strunzone(teamstats_last);
+               }
+               else
+                       db_put(playerstats_db, key, "#");
+               teamstats_last = strzone(key);
+       }
+}
+
+void PlayerStats_AddEvent(string event_id)
+{
+       if(playerstats_db < 0)
+               return;
+
+       string key;
+       key = sprintf("*:%s", event_id);
+
+       string p;
+       p = db_get(playerstats_db, key);
+       if(p == "")
+       {
+               if(events_last)
+               {
+                       db_put(playerstats_db, key, events_last);
+                       strunzone(events_last);
+               }
+               else
+                       db_put(playerstats_db, key, "#");
+               events_last = strzone(event_id);
+       }
+}
+
+float PlayerStats_Event(entity e, string event_id, float value)
+{
+       if((e.playerstats_id == "") || playerstats_db < 0)
+               return 0;
+
+       string key;
+       float val;
+       key = sprintf("%s:%s", e.playerstats_id, event_id);
+       val = stof(db_get(playerstats_db, key));
+       val += value;
+       db_put(playerstats_db, key, ftos(val));
+       return val;
+}
+
+float PlayerStats_TeamScore(float t, string event_id, float value)
+{
+       if(playerstats_db < 0)
+               return 0;
+
+       string key;
+       float val;
+       key = sprintf("team#%d:%s", t, event_id);
+       val = stof(db_get(playerstats_db, key));
+       val += value;
+       db_put(playerstats_db, key, ftos(val));
+       return val;
+}
+
+/*
+       format spec:
+
+       A collection of lines of the format <key> SPACE <value> NEWLINE, where
+       <key> is always a single character.
+
+       The following keys are defined:
+
+       V: format version (always a fixed number) - this MUST be the first line!
+       #: comment (MUST be ignored by any parser)
+       R: release information on the server
+       T: time at which the game ended
+       G: game type
+       O: mod name (icon request) as in server browser
+       M: map name
+       I: match ID (see "matchid" in g_world.qc
+       S: "hostname" of the server
+       C: number of "unpure" cvar changes
+       U: UDP port number of the server
+       D: duration of the match
+       P: player ID of an existing player; this also sets the owner for all following "n", "e" and "t" lines (lower case!)
+       Q: team number of an existing team (format: team#NN); this also sets the owner for all following "e" lines (lower case!)
+       n: nickname of the player (optional)
+       t: team ID
+       i: player index
+       e: followed by an event name, a space, and the event count/score
+               event names can be:
+                       alivetime: total playing time of the player
+                       avglatency: average network latency compounded throughout the match
+                       wins: number of games won (can only be set if matches is set)
+                       matches: number of matches played to the end (not aborted by map switch)
+                       joins: number of matches joined (always 1 unless player never played during the match)
+                       scoreboardvalid: set to 1 if the player was there at the end of the match
+                       total-<scoreboardname>: total score of that scoreboard item
+                       scoreboard-<scoreboardname>: end-of-game score of that scoreboard item (can differ in non-team games)
+                       achievement-<achievementname>: achievement counters (their "count" is usually 1 if nonzero at all)
+                       kills-<index>: number of kills against the indexed player
+                       rank <number>: rank of player
+                       acc-<weapon netname>-hit: total damage dealt
+                       acc-<weapon netname>-fired: total damage that all fired projectiles *could* have dealt
+                       acc-<weapon netname>-cnt-hit: amount of shots that actually hit
+                       acc-<weapon netname>-cnt-fired: amount of fired shots
+                       acc-<weapon netname>-frags: amount of frags dealt by weapon
+
+       Response format (not used yet): see https://gist.github.com/4284222
+*/
+
+void PlayerStats_ready(entity fh, entity pass, float status)
+{
+       string t, tn;
+       string p, pn;
+       string e, en;
+       string nn, tt;
+       string s;
+
+       switch(status)
+       {
+               case URL_READY_CANWRITE:
+                       url_fputs(fh, "V 7\n");
+#ifdef WATERMARK
+                       url_fputs(fh, sprintf("R %s\n", WATERMARK));
+#endif
+                       url_fputs(fh, sprintf("T %s.%06d\n", strftime(FALSE, "%s"), floor(random() * 1000000)));
+                       url_fputs(fh, sprintf("G %s\n", GetGametype()));
+                       url_fputs(fh, sprintf("O %s\n", modname));
+                       url_fputs(fh, sprintf("M %s\n", GetMapname()));
+                       url_fputs(fh, sprintf("I %s\n", matchid));
+                       url_fputs(fh, sprintf("S %s\n", cvar_string("hostname")));
+                       url_fputs(fh, sprintf("C %d\n", cvar_purechanges_count));
+                       url_fputs(fh, sprintf("U %d\n", cvar("port")));
+                       url_fputs(fh, sprintf("D %f\n", max(0, time - game_starttime)));
+                       if(teamplay)
+                       {
+                               for(t = teamstats_last; (tn = db_get(playerstats_db, sprintf("%d", stof(t)))) != ""; t = tn)
+                               {
+                                       url_fputs(fh, sprintf("Q team#%s\n", t));
+                                       for(e = events_last; (en = db_get(playerstats_db, sprintf("*:%s", e))) != ""; e = en)
+                                       {
+                                               float v;
+                                               v = stof(db_get(playerstats_db, sprintf("team#%d:%s", stof(t), e)));
+                                               if(v != 0)
+                                                       url_fputs(fh, sprintf("e %s %g\n", e, v));
+                                       }
+                               }
+                       }
+                       for(p = playerstats_last; (pn = db_get(playerstats_db, sprintf("%s:*", p))) != ""; p = pn)
+                       {
+                               url_fputs(fh, sprintf("P %s\n", p));
+                               nn = db_get(playerstats_db, sprintf("%s:_playerid", p));
+                               if(nn != "")
+                                       url_fputs(fh, sprintf("i %s\n", nn));
+                               nn = db_get(playerstats_db, sprintf("%s:_netname", p));
+                               if(nn != "")
+                                       url_fputs(fh, sprintf("n %s\n", nn));
+                               if(teamplay)
+                               {
+                                       tt = db_get(playerstats_db, sprintf("%s:_team", p));
+                                       url_fputs(fh, sprintf("t %s\n", tt));
+                               }
+                               for(e = events_last; (en = db_get(playerstats_db, sprintf("*:%s", e))) != ""; e = en)
+                               {
+                                       float v;
+                                       v = stof(db_get(playerstats_db, sprintf("%s:%s", p, e)));
+                                       if(v != 0)
+                                               url_fputs(fh, sprintf("e %s %g\n", e, v));
+                               }
+                       }
+                       url_fputs(fh, "\n");
+                       url_fclose(fh);
+                       break;
+               case URL_READY_CANREAD:
+                       // url_fclose is processing, we got a response for writing the data
+                       // this must come from HTTP
+                       print("Got response from player stats server:\n");
+                       while((s = url_fgets(fh)))
+                               print("  ", s, "\n");
+                       print("End of response.\n");
+                       url_fclose(fh);
+                       break;
+               case URL_READY_CLOSED:
+                       // url_fclose has finished
+                       print("Player stats written\n");
+                       playerstats_waitforme = TRUE;
+                       db_close(playerstats_db);
+                       playerstats_db = -1;
+                       break;
+               case URL_READY_ERROR:
+               default:
+                       print("Player stats writing failed: ", ftos(status), "\n");
+                       playerstats_waitforme = TRUE;
+                       if(playerstats_db >= 0)
+                       {
+                               db_close(playerstats_db);
+                               playerstats_db = -1;
+                       }
+                       break;
+       }
+}
+
+//#NO AUTOCVARS START
+void PlayerStats_Shutdown()
+{
+       string uri;
+
+       if(playerstats_db < 0)
+               return;
+
+       uri = autocvar_g_playerstats_uri;
+       if(uri != "")
+       {
+               playerstats_waitforme = FALSE;
+               url_multi_fopen(uri, FILE_APPEND, PlayerStats_ready, world);
+       }
+       else
+       {
+               playerstats_waitforme = TRUE;
+               db_close(playerstats_db);
+               playerstats_db = -1;
+       }
+}
+//#NO AUTOCVARS END
+
+void PlayerStats_Accuracy(entity p)
+{
+    entity a, w;
+    a = p.accuracy;
+    float i;
+
+    for(i = WEP_FIRST; i <= WEP_LAST; ++i)
+    {
+        w = get_weaponinfo(i);
+
+        PlayerStats_Event(p, strcat("acc-", w.netname, "-hit"), a.(accuracy_hit[i-1]));
+        PlayerStats_Event(p, strcat("acc-", w.netname, "-fired"), a.(accuracy_fired[i-1]));
+
+        PlayerStats_Event(p, strcat("acc-", w.netname, "-cnt-hit"), a.(accuracy_cnt_hit[i-1]));
+        PlayerStats_Event(p, strcat("acc-", w.netname, "-cnt-fired"), a.(accuracy_cnt_fired[i-1]));
+
+        PlayerStats_Event(p, strcat("acc-", w.netname, "-frags"), a.(accuracy_frags[i-1]));
+    }
+    //backtrace(strcat("adding player stat accuracy for ", p.netname, ".\n"));
+}
+
+void PlayerStats_AddGlobalInfo(entity p)
+{
+       if(playerstats_db < 0)
+               return;
+       if((p.playerstats_id == "") || playerstats_db < 0)
+               return;
+       p.playerstats_addedglobalinfo = TRUE;
+
+       // add global info!
+       if(p.alivetime)
+       {
+               PlayerStats_Event(p, PLAYERSTATS_ALIVETIME, time - p.alivetime);
+               p.alivetime = 0;
+       }
+
+       db_put(playerstats_db, sprintf("%s:_playerid", p.playerstats_id), ftos(p.playerid));
+
+       if(p.cvar_cl_allow_uid2name == 1 || IS_BOT_CLIENT(p))
+               db_put(playerstats_db, sprintf("%s:_netname", p.playerstats_id), p.netname);
+
+       if(teamplay)
+               db_put(playerstats_db, sprintf("%s:_team", p.playerstats_id), ftos(p.team));
+
+       if(stof(db_get(playerstats_db, sprintf("%d:%s", p.playerstats_id, PLAYERSTATS_ALIVETIME))) > 0)
+               PlayerStats_Event(p, PLAYERSTATS_JOINS, 1);
+
+       PlayerStats_Accuracy(p);
+
+       if(IS_REAL_CLIENT(p))
+       {
+               if(p.latency_cnt)
+               {
+                       float latency = (p.latency_sum / p.latency_cnt);
+                       if(latency) { PlayerStats_Event(p, PLAYERSTATS_AVGLATENCY, latency); }
+               }
+       }
+
+       strunzone(p.playerstats_id);
+       p.playerstats_id = string_null;
+}
+
+.float scoreboard_pos;
+void PlayerStats_EndMatch(float finished)
+{
+       entity p;
+       PlayerScore_Sort(score_dummyfield, 0, 0, 0);
+       PlayerScore_Sort(scoreboard_pos, 1, 1, 1);
+       if(teamplay)
+               PlayerScore_TeamStats();
+       FOR_EACH_CLIENT(p)
+       {
+               // add personal score rank
+               PlayerStats_Event(p, PLAYERSTATS_RANK, p.score_dummyfield);
+
+               if(!p.scoreboard_pos)
+                       continue;
+
+               // scoreboard is valid!
+               PlayerStats_Event(p, PLAYERSTATS_SCOREBOARD_VALID, 1);
+
+               // add scoreboard position
+               PlayerStats_Event(p, PLAYERSTATS_SCOREBOARD_POS, p.scoreboard_pos);
+
+               // add scoreboard data
+               PlayerScore_PlayerStats(p);
+
+               // if the match ended normally, add winning info
+               if(finished)
+               {
+                       PlayerStats_Event(p, PLAYERSTATS_WINS, p.winning);
+                       PlayerStats_Event(p, PLAYERSTATS_MATCHES, 1);
+               }
+       }
+}
+
+
+//// WIP -zykure
+
+/*
+       format spec:
+
+       A collection of lines of the format <key> SPACE <value> NEWLINE, where
+       <key> is always a single character.
+
+       The following keys are defined:
+
+       V: format version (always a fixed number) - this MUST be the first line!
+       #: comment (MUST be ignored by any parser)
+       R: release information on the server
+       T: time at which the game ended
+       G: game type
+       O: mod name (icon request) as in server browser
+       M: map name
+       I: match ID (see "matchid" in g_world.qc
+       S: "hostname" of the server
+       C: number of "unpure" cvar changes
+       U: UDP port number of the server
+       D: duration of the match
+       P: player ID of an existing player; this also sets the owner for all following "n", "e" and "t" lines (lower case!)
+       Q: team number of an existing team (format: team#NN); this also sets the owner for all following "e" lines (lower case!)
+       n: nickname of the player (optional)
+       t: team ID
+       i: player index
+       e: followed by an event name, a space, and the event count/score
+               event names can be:
+                       alivetime: total playing time of the player
+                       avglatency: average network latency compounded throughout the match
+                       wins: number of games won (can only be set if matches is set)
+                       matches: number of matches played to the end (not aborted by map switch)
+                       joins: number of matches joined (always 1 unless player never played during the match)
+                       scoreboardvalid: set to 1 if the player was there at the end of the match
+                       total-<scoreboardname>: total score of that scoreboard item
+                       scoreboard-<scoreboardname>: end-of-game score of that scoreboard item (can differ in non-team games)
+                       achievement-<achievementname>: achievement counters (their "count" is usually 1 if nonzero at all)
+                       kills-<index>: number of kills against the indexed player
+                       rank <number>: rank of player
+                       acc-<weapon netname>-hit: total damage dealt
+                       acc-<weapon netname>-fired: total damage that all fired projectiles *could* have dealt
+                       acc-<weapon netname>-cnt-hit: amount of shots that actually hit
+                       acc-<weapon netname>-cnt-fired: amount of fired shots
+                       acc-<weapon netname>-frags: amount of frags dealt by weapon
+
+       Response format (not used yet): see https://gist.github.com/4284222
+*/
+/*
+void PlayerStatsEntity_ready(entity fh, entity player_stats, float status)
+{
+       string s;
+       string key, value;
+
+       switch(status)
+       {
+               case URL_READY_CANREAD:
+                       print("Got response from player stats server:\n");
+                       while((s = url_fgets(fh)))
+                       {
+                               print("  ", s, "\n");
+
+                               key = substring(s, 0, 1);
+                               value = substring(s, 2, -1);
+                               if (key == "#")
+                                       continue;
+                               else if (key == "V")
+                                       // version
+                                       continue;
+                               else if (key == "P")
+                                       player_stats.playerid = stof(value);
+                               else if (key == "n")
+                                       player_stats.playernick = value;
+                       }
+                       print("End of response.\n");
+                       url_fclose(fh);
+                       break;
+               case URL_READY_CLOSED:
+                       // url_fclose has finished
+                       print("Player stats received from server\n");
+                       break;
+               case URL_READY_ERROR:
+               default:
+                       print("Receiving player stats failed: ", ftos(status), "\n");
+                       break;
+       }
+}
+
+void PlayerStats_GetPlayerInfo(entity p)
+{
+       string uri;
+
+       uri = autocvar_g_playerstats_uri;
+       if(uri != "")
+       {
+               url_single_fopen(strcat(uri, "/"), FILE_READ, PlayerStatsEntity_ready, p.player_stats);
+       }
+}
+*/
+
+#endif // SVQC
diff --git a/qcsrc/common/playerstats.qh b/qcsrc/common/playerstats.qh
new file mode 100644 (file)
index 0000000..1cc0885
--- /dev/null
@@ -0,0 +1,64 @@
+#ifdef SVQC
+
+// time the player was alive and kicking
+string PLAYERSTATS_ALIVETIME  = "alivetime";
+string PLAYERSTATS_AVGLATENCY = "avglatency";
+string PLAYERSTATS_WINS = "wins";
+string PLAYERSTATS_MATCHES = "matches";
+string PLAYERSTATS_JOINS = "joins";
+string PLAYERSTATS_SCOREBOARD_VALID = "scoreboardvalid";
+string PLAYERSTATS_RANK = "rank";
+string PLAYERSTATS_SCOREBOARD_POS = "scoreboardpos";
+
+string PLAYERSTATS_TOTAL = "total-";
+string PLAYERSTATS_SCOREBOARD = "scoreboard-";
+
+string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_3 = "achievement-kill-spree-3";
+string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_5 = "achievement-kill-spree-5";
+string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_10 = "achievement-kill-spree-10";
+string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_15 = "achievement-kill-spree-15";
+string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_20 = "achievement-kill-spree-20";
+string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_25 = "achievement-kill-spree-25";
+string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_30 = "achievement-kill-spree-30";
+string PLAYERSTATS_ACHIEVEMENT_BOTLIKE = "achievement-botlike";
+string PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD = "achievement-firstblood";
+string PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM = "achievement-firstvictim";
+
+// delay map switch until this is set
+float playerstats_waitforme;
+
+// call at initialization
+void PlayerStats_Init();
+
+// add a new player
+void PlayerStats_AddPlayer(entity e);
+
+// add a new team
+void PlayerStats_AddTeam(float t);
+
+// add a new event
+void PlayerStats_AddEvent(string event_id);
+
+// call on each event to track, or at player disconnect OR match end for "global stuff"
+float PlayerStats_Event(entity e, string event_id, float value);
+
+// add a team score
+float PlayerStats_TeamScore(float t, string event_id, float value);
+
+// call at game over
+void PlayerStats_Shutdown(); // send stats to the server
+
+void PlayerStats_Accuracy(entity p);
+
+// call this whenever a player leaves
+void PlayerStats_AddGlobalInfo(entity p);
+
+// call this at the end of the match
+void PlayerStats_EndMatch(float finished);
+
+
+//// WIP -zykure
+
+//void PlayerStats_GetPlayerInfo(entity p);
+
+#endif //SVQC
index 3036278c1fcfbe09ed31da9f56795e2be7f8dbe6..b6ea698cdf7e96352d2af6de20ec0620eddfad75 100644 (file)
@@ -11,6 +11,7 @@ config.qh
 ../warpzonelib/mathlib.qh
 ../common/util.qh
 ../common/test.qh
+../common/playerstats.qh
 
 oo/base.h
 
@@ -39,6 +40,7 @@ oo/implementation.h
 
 ../common/util.qc
 ../common/test.qc
+../common/playerstats.qc
 ../common/command/markup.qc
 ../common/command/rpn.qc
 ../common/command/generic.qc
index 7d3585725d2544ecd4dab000d907c51cbab6354e..f694b7d1fdb10b01e4ba98a65568f7a3bd2c285f 100644 (file)
@@ -55,7 +55,7 @@ float team1_score, team2_score, team3_score, team4_score;
 float maxclients;
 
 // flag set on worldspawn so that the code knows if it is dedicated or not
-float server_is_dedicated; 
+float server_is_dedicated;
 
 // Fields
 
@@ -473,7 +473,7 @@ void target_voicescript_clear(entity pl);
 .float target_random;
 .float trigger_reverse;
 
-// Nexball 
+// Nexball
 .entity ballcarried; // Also used for keepaway
 .float metertime;
 float g_nexball_meter_period;
@@ -514,8 +514,8 @@ string matchid;
 
 .float last_pickup;
 
-.float hit_time; 
-.float typehit_time; 
+.float hit_time;
+.float typehit_time;
 
 .float stat_leadlimit;
 
@@ -614,3 +614,12 @@ string modname;
 #define MISSILE_IS_CONFUSABLE(m) ((m.missile_flags & MIF_GUIDED_CONFUSABLE) ? TRUE : FALSE)
 #define MISSILE_IS_GUIDED(m) ((m.missile_flags & MIF_GUIDED_ALL) ? TRUE : FALSE)
 #define MISSILE_IS_TRACKING(m) ((m.missile_flags & MIF_GUIDED_TRACKING) ? TRUE : FALSE)
+
+
+////
+
+.entity player_stats;
+//.float playerid;
+.string playernick;
+.float elos;
+.float ranks;
index df09032139b62e7158c6ef20d95a841b037c7c9a..af8067ca04d075ae527004eb455eeb598f3bb063 100644 (file)
@@ -647,16 +647,16 @@ float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
                d = 0; // weapon is set a few lines later
        else
                d = (i == WEP_LASER || i == WEP_SHOTGUN);
-               
+
        if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
                d |= (i == WEP_HOOK);
        if(weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED) // never default mutator blocked guns
                d = 0;
 
        var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
-       
+
        //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
-       
+
        // bit order in t:
        // 1: want or not
        // 2: is default?
@@ -907,7 +907,7 @@ void readlevelcvars(void)
        // load mutators
        #define CHECK_MUTATOR_ADD(mut_cvar,mut_name,dependence) \
                { if(cvar(mut_cvar) && dependence) { MUTATOR_ADD(mut_name); } }
-               
+
        CHECK_MUTATOR_ADD("g_dodging", mutator_dodging, 1);
        CHECK_MUTATOR_ADD("g_spawn_near_teammate", mutator_spawn_near_teammate, 1);
        CHECK_MUTATOR_ADD("g_physical_items", mutator_physical_items, 1);
@@ -920,9 +920,9 @@ void readlevelcvars(void)
        CHECK_MUTATOR_ADD("g_vampire", mutator_vampire, !cvar("g_minstagib"));
        CHECK_MUTATOR_ADD("g_superspectate", mutator_superspec, 1);
        CHECK_MUTATOR_ADD("g_sandbox", sandbox, 1);
-       
+
        #undef CHECK_MUTATOR_ADD
-       
+
        if(cvar("sv_allow_fullbright"))
                serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
 
@@ -941,7 +941,7 @@ void readlevelcvars(void)
     g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
     g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
     g_bugrigs_steer = cvar("g_bugrigs_steer");
-       
+
        g_minstagib = cvar("g_minstagib");
 
        sv_clones = cvar("sv_clones");
@@ -1816,7 +1816,7 @@ string uid2name(string myuid) {
                        db_put(ServerProgsDB, strcat("uid2name", myuid), "");
                }
        }
-       
+
        if(s == "")
                s = "^1Unregistered Player";
        return s;
diff --git a/qcsrc/server/playerstats.qc b/qcsrc/server/playerstats.qc
deleted file mode 100644 (file)
index 354b521..0000000
+++ /dev/null
@@ -1,432 +0,0 @@
-float playerstats_db;
-string teamstats_last;
-string playerstats_last;
-string events_last;
-.float playerstats_addedglobalinfo;
-.string playerstats_id;
-
-void PlayerStats_Init() // initiated before InitGameplayMode so that scores are added properly
-{
-       string uri;
-       playerstats_db = -1;
-       playerstats_waitforme = TRUE;
-       uri = autocvar_g_playerstats_uri;
-       if(uri == "")
-               return;
-       playerstats_db = db_create();
-       if(playerstats_db >= 0)
-               playerstats_waitforme = FALSE; // must wait for it at match end
-
-       serverflags |= SERVERFLAG_PLAYERSTATS;  
-
-       PlayerStats_AddEvent(PLAYERSTATS_ALIVETIME);
-       PlayerStats_AddEvent(PLAYERSTATS_AVGLATENCY);
-       PlayerStats_AddEvent(PLAYERSTATS_WINS);
-       PlayerStats_AddEvent(PLAYERSTATS_MATCHES);
-       PlayerStats_AddEvent(PLAYERSTATS_JOINS);
-       PlayerStats_AddEvent(PLAYERSTATS_SCOREBOARD_VALID);
-       PlayerStats_AddEvent(PLAYERSTATS_SCOREBOARD_POS);
-       PlayerStats_AddEvent(PLAYERSTATS_RANK);
-
-    // accuracy stats
-    entity w;
-    float i;
-    for(i = WEP_FIRST; i <= WEP_LAST; ++i)
-    {
-        w = get_weaponinfo(i);
-
-        PlayerStats_AddEvent(strcat("acc-", w.netname, "-hit"));
-        PlayerStats_AddEvent(strcat("acc-", w.netname, "-fired"));
-
-        PlayerStats_AddEvent(strcat("acc-", w.netname, "-cnt-hit"));
-        PlayerStats_AddEvent(strcat("acc-", w.netname, "-cnt-fired"));
-
-        PlayerStats_AddEvent(strcat("acc-", w.netname, "-frags"));
-    }
-
-       PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_3);
-       PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_5);
-       PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_10);
-       PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_15);
-       PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_20);
-       PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_25);
-       PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_30);
-       PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_BOTLIKE);
-       PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD);
-       PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM);
-}
-
-void PlayerStats_AddPlayer(entity e)
-{
-       string s;
-
-       if(playerstats_db < 0)
-               return;
-       if(e.playerstats_id)
-               return;
-
-       s = string_null;
-       if(e.crypto_idfp != "" && e.cvar_cl_allow_uidtracking == 1)
-               s = e.crypto_idfp;
-       else if(IS_BOT_CLIENT(e))
-               s = sprintf("bot#%g#%s", skill, e.cleanname);
-
-       if((s == "") || find(world, playerstats_id, s)) // already have one of the ID - next one can't be tracked then!
-       {
-               if(IS_BOT_CLIENT(e))
-                       s = sprintf("bot#%d", e.playerid);
-               else
-                       s = sprintf("player#%d", e.playerid);
-       }
-
-       e.playerstats_id = strzone(s);
-
-       string key;
-       key = sprintf("%s:*", e.playerstats_id);
-       
-       string p;
-       p = db_get(playerstats_db, key);
-       if(p == "")
-       {
-               if(playerstats_last)
-               {
-                       db_put(playerstats_db, key, playerstats_last);
-                       strunzone(playerstats_last);
-               }
-               else
-                       db_put(playerstats_db, key, "#");
-               playerstats_last = strzone(e.playerstats_id);
-       }
-}
-
-void PlayerStats_AddTeam(float t)
-{
-       if(playerstats_db < 0)
-               return;
-
-       string key;
-       key = sprintf("%d", t);
-       
-       string p;
-       p = db_get(playerstats_db, key);
-       if(p == "")
-       {
-               if(teamstats_last)
-               {
-                       db_put(playerstats_db, key, teamstats_last);
-                       strunzone(teamstats_last);
-               }
-               else
-                       db_put(playerstats_db, key, "#");
-               teamstats_last = strzone(key);
-       }
-}
-
-void PlayerStats_AddEvent(string event_id)
-{
-       if(playerstats_db < 0)
-               return;
-       
-       string key;
-       key = sprintf("*:%s", event_id);
-       
-       string p;
-       p = db_get(playerstats_db, key);
-       if(p == "")
-       {
-               if(events_last)
-               {
-                       db_put(playerstats_db, key, events_last);
-                       strunzone(events_last);
-               }
-               else
-                       db_put(playerstats_db, key, "#");
-               events_last = strzone(event_id);
-       }
-}
-
-float PlayerStats_Event(entity e, string event_id, float value)
-{
-       if((e.playerstats_id == "") || playerstats_db < 0)
-               return 0;
-       
-       string key;
-       float val;
-       key = sprintf("%s:%s", e.playerstats_id, event_id);
-       val = stof(db_get(playerstats_db, key));
-       val += value;
-       db_put(playerstats_db, key, ftos(val));
-       return val;
-}
-
-float PlayerStats_TeamScore(float t, string event_id, float value)
-{
-       if(playerstats_db < 0)
-               return 0;
-
-       string key;
-       float val;
-       key = sprintf("team#%d:%s", t, event_id);
-       val = stof(db_get(playerstats_db, key));
-       val += value;
-       db_put(playerstats_db, key, ftos(val));
-       return val;
-}
-
-/*
-       format spec:
-
-       A collection of lines of the format <key> SPACE <value> NEWLINE, where
-       <key> is always a single character.
-
-       The following keys are defined:
-
-       V: format version (always a fixed number) - this MUST be the first line!
-       #: comment (MUST be ignored by any parser)
-       R: release information on the server
-       T: time at which the game ended
-       G: game type
-       O: mod name (icon request) as in server browser
-       M: map name
-       I: match ID (see "matchid" in g_world.qc
-       S: "hostname" of the server
-       C: number of "unpure" cvar changes
-       U: UDP port number of the server
-       D: duration of the match
-       P: player ID of an existing player; this also sets the owner for all following "n", "e" and "t" lines (lower case!)
-       Q: team number of an existing team (format: team#NN); this also sets the owner for all following "e" lines (lower case!) 
-       n: nickname of the player (optional)
-       t: team ID
-       i: player index
-       e: followed by an event name, a space, and the event count/score
-               event names can be:
-                       alivetime: total playing time of the player
-                       avglatency: average network latency compounded throughout the match
-                       wins: number of games won (can only be set if matches is set)
-                       matches: number of matches played to the end (not aborted by map switch)
-                       joins: number of matches joined (always 1 unless player never played during the match)
-                       scoreboardvalid: set to 1 if the player was there at the end of the match
-                       total-<scoreboardname>: total score of that scoreboard item
-                       scoreboard-<scoreboardname>: end-of-game score of that scoreboard item (can differ in non-team games)
-                       achievement-<achievementname>: achievement counters (their "count" is usually 1 if nonzero at all)
-                       kills-<index>: number of kills against the indexed player
-                       rank <number>: rank of player
-                       acc-<weapon netname>-hit: total damage dealt
-                       acc-<weapon netname>-fired: total damage that all fired projectiles *could* have dealt
-                       acc-<weapon netname>-cnt-hit: amount of shots that actually hit
-                       acc-<weapon netname>-cnt-fired: amount of fired shots
-                       acc-<weapon netname>-frags: amount of frags dealt by weapon
-
-       Response format (not used yet): see https://gist.github.com/4284222
-*/
-
-void PlayerStats_ready(entity fh, entity pass, float status)
-{
-       string t, tn;
-       string p, pn;
-       string e, en;
-       string nn, tt;
-       string s;
-
-       switch(status)
-       {
-               case URL_READY_CANWRITE:
-                       url_fputs(fh, "V 7\n");
-#ifdef WATERMARK
-                       url_fputs(fh, sprintf("R %s\n", WATERMARK));
-#endif
-                       url_fputs(fh, sprintf("T %s.%06d\n", strftime(FALSE, "%s"), floor(random() * 1000000)));
-                       url_fputs(fh, sprintf("G %s\n", GetGametype()));
-                       url_fputs(fh, sprintf("O %s\n", modname));
-                       url_fputs(fh, sprintf("M %s\n", GetMapname()));
-                       url_fputs(fh, sprintf("I %s\n", matchid));
-                       url_fputs(fh, sprintf("S %s\n", cvar_string("hostname")));
-                       url_fputs(fh, sprintf("C %d\n", cvar_purechanges_count));
-                       url_fputs(fh, sprintf("U %d\n", cvar("port")));
-                       url_fputs(fh, sprintf("D %f\n", max(0, time - game_starttime)));
-                       if(teamplay)
-                       {
-                               for(t = teamstats_last; (tn = db_get(playerstats_db, sprintf("%d", stof(t)))) != ""; t = tn)
-                               {
-                                       url_fputs(fh, sprintf("Q team#%s\n", t));
-                                       for(e = events_last; (en = db_get(playerstats_db, sprintf("*:%s", e))) != ""; e = en)
-                                       {
-                                               float v;
-                                               v = stof(db_get(playerstats_db, sprintf("team#%d:%s", stof(t), e)));
-                                               if(v != 0)
-                                                       url_fputs(fh, sprintf("e %s %g\n", e, v));
-                                       }
-                               }
-                       }
-                       for(p = playerstats_last; (pn = db_get(playerstats_db, sprintf("%s:*", p))) != ""; p = pn)
-                       {
-                               url_fputs(fh, sprintf("P %s\n", p));
-                               nn = db_get(playerstats_db, sprintf("%s:_playerid", p));
-                               if(nn != "")
-                                       url_fputs(fh, sprintf("i %s\n", nn));
-                               nn = db_get(playerstats_db, sprintf("%s:_netname", p));
-                               if(nn != "")
-                                       url_fputs(fh, sprintf("n %s\n", nn));
-                               if(teamplay)
-                               {
-                                       tt = db_get(playerstats_db, sprintf("%s:_team", p));
-                                       url_fputs(fh, sprintf("t %s\n", tt));
-                               }
-                               for(e = events_last; (en = db_get(playerstats_db, sprintf("*:%s", e))) != ""; e = en)
-                               {
-                                       float v;
-                                       v = stof(db_get(playerstats_db, sprintf("%s:%s", p, e)));
-                                       if(v != 0)
-                                               url_fputs(fh, sprintf("e %s %g\n", e, v));
-                               }
-                       }
-                       url_fputs(fh, "\n");
-                       url_fclose(fh);
-                       break;
-               case URL_READY_CANREAD:
-                       // url_fclose is processing, we got a response for writing the data
-                       // this must come from HTTP
-                       print("Got response from player stats server:\n");
-                       while((s = url_fgets(fh)))
-                               print("  ", s, "\n");
-                       print("End of response.\n");
-                       url_fclose(fh);
-                       break;
-               case URL_READY_CLOSED:
-                       // url_fclose has finished
-                       print("Player stats written\n");
-                       playerstats_waitforme = TRUE;
-                       db_close(playerstats_db);
-                       playerstats_db = -1;
-                       break;
-               case URL_READY_ERROR:
-               default:
-                       print("Player stats writing failed: ", ftos(status), "\n");
-                       playerstats_waitforme = TRUE;
-                       if(playerstats_db >= 0)
-                       {
-                               db_close(playerstats_db);
-                               playerstats_db = -1;
-                       }
-                       break;
-       }
-}
-
-//#NO AUTOCVARS START
-void PlayerStats_Shutdown()
-{
-       string uri;
-
-       if(playerstats_db < 0)
-               return;
-
-       uri = autocvar_g_playerstats_uri;
-       if(uri != "")
-       {
-               playerstats_waitforme = FALSE;
-               url_multi_fopen(uri, FILE_APPEND, PlayerStats_ready, world);
-       }
-       else
-       {
-               playerstats_waitforme = TRUE;
-               db_close(playerstats_db);
-               playerstats_db = -1;
-       }
-}
-//#NO AUTOCVARS END
-
-void PlayerStats_Accuracy(entity p)
-{
-    entity a, w;
-    a = p.accuracy;
-    float i;
-
-    for(i = WEP_FIRST; i <= WEP_LAST; ++i)
-    {
-        w = get_weaponinfo(i);
-
-        PlayerStats_Event(p, strcat("acc-", w.netname, "-hit"), a.(accuracy_hit[i-1]));
-        PlayerStats_Event(p, strcat("acc-", w.netname, "-fired"), a.(accuracy_fired[i-1]));
-
-        PlayerStats_Event(p, strcat("acc-", w.netname, "-cnt-hit"), a.(accuracy_cnt_hit[i-1]));
-        PlayerStats_Event(p, strcat("acc-", w.netname, "-cnt-fired"), a.(accuracy_cnt_fired[i-1]));
-
-        PlayerStats_Event(p, strcat("acc-", w.netname, "-frags"), a.(accuracy_frags[i-1]));
-    }
-    //backtrace(strcat("adding player stat accuracy for ", p.netname, ".\n"));
-}
-
-void PlayerStats_AddGlobalInfo(entity p)
-{
-       if(playerstats_db < 0)
-               return;
-       if((p.playerstats_id == "") || playerstats_db < 0)
-               return;
-       p.playerstats_addedglobalinfo = TRUE;
-
-       // add global info!
-       if(p.alivetime)
-       {
-               PlayerStats_Event(p, PLAYERSTATS_ALIVETIME, time - p.alivetime);
-               p.alivetime = 0;
-       }
-
-       db_put(playerstats_db, sprintf("%s:_playerid", p.playerstats_id), ftos(p.playerid));
-
-       if(p.cvar_cl_allow_uid2name == 1 || IS_BOT_CLIENT(p))
-               db_put(playerstats_db, sprintf("%s:_netname", p.playerstats_id), p.netname);
-
-       if(teamplay)
-               db_put(playerstats_db, sprintf("%s:_team", p.playerstats_id), ftos(p.team));
-
-       if(stof(db_get(playerstats_db, sprintf("%d:%s", p.playerstats_id, PLAYERSTATS_ALIVETIME))) > 0)
-               PlayerStats_Event(p, PLAYERSTATS_JOINS, 1);
-
-       PlayerStats_Accuracy(p);
-
-       if(IS_REAL_CLIENT(p))
-       {
-               if(p.latency_cnt)
-               {
-                       float latency = (p.latency_sum / p.latency_cnt);
-                       if(latency) { PlayerStats_Event(p, PLAYERSTATS_AVGLATENCY, latency); }
-               }
-       }
-
-       strunzone(p.playerstats_id);
-       p.playerstats_id = string_null;
-}
-
-.float scoreboard_pos;
-void PlayerStats_EndMatch(float finished)
-{
-       entity p;
-       PlayerScore_Sort(score_dummyfield, 0, 0, 0);
-       PlayerScore_Sort(scoreboard_pos, 1, 1, 1);
-       if(teamplay)
-               PlayerScore_TeamStats();
-       FOR_EACH_CLIENT(p)
-       {
-               // add personal score rank
-               PlayerStats_Event(p, PLAYERSTATS_RANK, p.score_dummyfield);
-
-               if(!p.scoreboard_pos)
-                       continue;
-
-               // scoreboard is valid!
-               PlayerStats_Event(p, PLAYERSTATS_SCOREBOARD_VALID, 1);
-
-               // add scoreboard position
-               PlayerStats_Event(p, PLAYERSTATS_SCOREBOARD_POS, p.scoreboard_pos);
-
-               // add scoreboard data
-               PlayerScore_PlayerStats(p);
-
-               // if the match ended normally, add winning info
-               if(finished)
-               {
-                       PlayerStats_Event(p, PLAYERSTATS_WINS, p.winning);
-                       PlayerStats_Event(p, PLAYERSTATS_MATCHES, 1);
-               }
-       }
-}
diff --git a/qcsrc/server/playerstats.qh b/qcsrc/server/playerstats.qh
deleted file mode 100644 (file)
index ab28b3a..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-// time the player was alive and kicking
-string PLAYERSTATS_ALIVETIME  = "alivetime";
-string PLAYERSTATS_AVGLATENCY = "avglatency";
-string PLAYERSTATS_WINS = "wins";
-string PLAYERSTATS_MATCHES = "matches";
-string PLAYERSTATS_JOINS = "joins";
-string PLAYERSTATS_SCOREBOARD_VALID = "scoreboardvalid";
-string PLAYERSTATS_RANK = "rank";
-string PLAYERSTATS_SCOREBOARD_POS = "scoreboardpos";
-
-string PLAYERSTATS_TOTAL = "total-";
-string PLAYERSTATS_SCOREBOARD = "scoreboard-";
-
-string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_3 = "achievement-kill-spree-3";
-string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_5 = "achievement-kill-spree-5";
-string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_10 = "achievement-kill-spree-10";
-string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_15 = "achievement-kill-spree-15";
-string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_20 = "achievement-kill-spree-20";
-string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_25 = "achievement-kill-spree-25";
-string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_30 = "achievement-kill-spree-30";
-string PLAYERSTATS_ACHIEVEMENT_BOTLIKE = "achievement-botlike";
-string PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD = "achievement-firstblood";
-string PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM = "achievement-firstvictim";
-
-// delay map switch until this is set
-float playerstats_waitforme;
-
-// call at initialization
-void PlayerStats_Init();
-
-// add a new player
-void PlayerStats_AddPlayer(entity e);
-
-// add a new team
-void PlayerStats_AddTeam(float t);
-
-// add a new event
-void PlayerStats_AddEvent(string event_id);
-
-// call on each event to track, or at player disconnect OR match end for "global stuff"
-float PlayerStats_Event(entity e, string event_id, float value);
-
-// add a team score
-float PlayerStats_TeamScore(float t, string event_id, float value);
-
-// call at game over
-void PlayerStats_Shutdown(); // send stats to the server
-
-// call this whenever a player leaves
-void PlayerStats_AddGlobalInfo(entity p);
-
-// call this at the end of the match
-void PlayerStats_EndMatch(float finished);
index 367e8609ded2741ab10a74503dbf7888a82bd1a6..2997bd251330d9abd1cbb81c71060297b919abf7 100644 (file)
@@ -43,7 +43,7 @@ mutators/gamemode_ctf.qh
 mutators/gamemode_domination.qh
 mutators/gamemode_keyhunt.qh // TODO fix this
 mutators/gamemode_keepaway.qh
-mutators/gamemode_nexball.qh 
+mutators/gamemode_nexball.qh
 mutators/gamemode_lms.qh
 mutators/mutator_dodging.qh
 
@@ -72,7 +72,7 @@ csqceffects.qc
 
 anticheat.qh
 cheats.qh
-playerstats.qh
+../common/playerstats.qh
 
 portals.qh
 
@@ -219,7 +219,7 @@ playerdemo.qc
 
 anticheat.qc
 cheats.qc
-playerstats.qc
+../common/playerstats.qc
 
 round_handler.qc