-#ifdef SVQC
-//float PS_PM_IN_DB; // playerstats_prematch_in_db // db for info COLLECTED at the beginning of a match
-float PS_GR_OUT_DB; // playerstats_gamereport_out_db // db of info SENT at the end of a match
-//float PS_GR_IN_DB; // playerstats_gamereport_in_db // db for info COLLECTED at the end of a match
-//float PS_B_IN_DB; // playerstats_playerbasic_in_db // db for info COLLECTED for basic player info (ELO)
-// http://stats.xonotic.org/player/GgXRw6piDtFIbMArMuiAi8JG4tiin8VLjZgsKB60Uds=/elo.txt
-#endif
-
-#ifdef MENUQC
-//float PS_D_IN_DB; // playerstats_playerdetail_in_db // db for info COLLECTED for detailed player profile display
-// http://stats.xonotic.org/player/me
-#endif
-
-#ifdef SVQC
-//string PS_PM_IN_EVL; // playerstats_prematch_in_events_last
-string PS_GR_OUT_TL; // playerstats_gamereport_out_teams_last
-string PS_GR_OUT_PL; // playerstats_gamereport_out_players_las
-string PS_GR_OUT_EVL; // playerstats_gamereport_out_events_last
-//string PS_GR_IN_PL; // playerstats_gamereport_in_players_last
-//string PS_GR_IN_EVL; // playerstats_gamereport_in_events_last
-//string PS_B_IN_PL; // playerstats_playerbasic_in_players_las
-//string PS_B_IN_EVL; // playerstats_playerbasic_in_events_last
-#endif
-
-#ifdef MENUQC
-//string PS_D_IN_EVL; // playerstats_playerdetail_in_events_last
-#endif
-
#ifdef SVQC
void PlayerStats_Prematch(void)
{
void PlayerStats_GameReport_Accuracy(entity p)
{
- entity w;
- float i;
-
- for(i = WEP_FIRST; i <= WEP_LAST; ++i)
- {
- w = get_weaponinfo(i);
-
- #define ACCMAC(suffix,field) \
+ entity w;
+ float i;
+
+ for(i = WEP_FIRST; i <= WEP_LAST; ++i)
+ {
+ w = get_weaponinfo(i);
+
+ #define ACCMAC(suffix,field) \
PS_GR_P_ADDVAL(p, sprintf("acc-%s-%s", w.netname, suffix), p.accuracy.(field[i-1]));
-
- ACCMAC("hit", accuracy_hit)
- ACCMAC("fired", accuracy_fired)
- ACCMAC("cnt-hit", accuracy_cnt_hit)
- ACCMAC("cnt-fired", accuracy_cnt_fired)
- ACCMAC("frags", accuracy_frags)
-
- #undef ACCMAC
- }
+
+ ACCMAC("hit", accuracy_hit)
+ ACCMAC("fired", accuracy_fired)
+ ACCMAC("cnt-hit", accuracy_cnt_hit)
+ ACCMAC("cnt-fired", accuracy_cnt_fired)
+ ACCMAC("frags", accuracy_frags)
+
+ #undef ACCMAC
+ }
}
void PlayerStats_GameReport_FinalizePlayer(entity p)
p.playerstats_id = string_null;
}
-void PlayerStats_GameReport_EndMatch(float finished)
+void PlayerStats_GameReport(float finished)
{
if(PS_GR_OUT_DB < 0) { return; }
if(autocvar_g_playerstats_gamereport_uri != "")
{
- PlayerStats_GameReport_DelayMapVote = TRUE;
+ PlayerStats_GameReport_DelayMapVote = true;
url_multi_fopen(
autocvar_g_playerstats_gamereport_uri,
FILE_APPEND,
}
else
{
- PlayerStats_GameReport_DelayMapVote = FALSE;
+ PlayerStats_GameReport_DelayMapVote = false;
db_close(PS_GR_OUT_DB);
PS_GR_OUT_DB = -1;
}
{
if(autocvar_g_playerstats_gamereport_uri == "") { return; }
- PS_GR_OUT_DB = -1;
PS_GR_OUT_DB = db_create();
if(PS_GR_OUT_DB >= 0)
{
- PlayerStats_GameReport_DelayMapVote = TRUE;
+ PlayerStats_GameReport_DelayMapVote = true;
serverflags |= SERVERFLAG_PLAYERSTATS;
PlayerStats_GameReport_AddEvent(PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD);
PlayerStats_GameReport_AddEvent(PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM);
}
- else { PlayerStats_GameReport_DelayMapVote = FALSE; }
+ else { PlayerStats_GameReport_DelayMapVote = false; }
}
void PlayerStats_GameReport_Handler(entity fh, entity pass, float status)
{
// 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");
+ dprint("Got response from player stats server:\n");
+ while((s = url_fgets(fh))) { dprint(" ", s, "\n"); }
+ dprint("End of response.\n");
url_fclose(fh);
break;
}
case URL_READY_CLOSED:
{
// url_fclose has finished
- print("Player stats written\n");
- PlayerStats_GameReport_DelayMapVote = FALSE;
- db_close(PS_GR_OUT_DB);
- PS_GR_OUT_DB = -1;
+ dprint("Player stats written\n");
+ PlayerStats_GameReport_DelayMapVote = false;
+ if(PS_GR_OUT_DB >= 0)
+ {
+ db_close(PS_GR_OUT_DB);
+ PS_GR_OUT_DB = -1;
+ }
break;
}
default:
{
print("Player stats writing failed: ", ftos(status), "\n");
- PlayerStats_GameReport_DelayMapVote = FALSE;
+ PlayerStats_GameReport_DelayMapVote = false;
if(PS_GR_OUT_DB >= 0)
{
db_close(PS_GR_OUT_DB);
}
}
}
-#endif // SVQC
-#ifdef MENUQC
-void PlayerStats_PlayerDetail_Handler(entity fh, entity p, float status)
+void PlayerStats_PlayerBasic(entity joiningplayer, float newrequest)
+{
+ // http://stats.xonotic.org/player/GgXRw6piDtFIbMArMuiAi8JG4tiin8VLjZgsKB60Uds=/elo.txt
+ if(autocvar_g_playerstats_playerbasic_uri != "")
+ {
+ string uri = autocvar_g_playerstats_playerbasic_uri;
+ if(joiningplayer.crypto_idfp != "")
+ {
+ // create the database if it doesn't already exist
+ if(PS_B_IN_DB < 0)
+ PS_B_IN_DB = db_create();
+
+ // now request the information
+ uri = strcat(uri, "/player/", uri_escape(uri_escape(joiningplayer.crypto_idfp)), "/elo.txt");
+ dprint("Retrieving playerstats from URL: ", uri, "\n");
+ url_single_fopen(
+ uri,
+ FILE_APPEND,
+ PlayerStats_PlayerBasic_Handler,
+ joiningplayer
+ );
+
+ // set status appropriately // todo: check whether the player info exists in the database previously
+ if(newrequest)
+ {
+ // database still contains useful information, so don't clear it of a useful status
+ joiningplayer.playerstats_basicstatus = PS_B_STATUS_WAITING;
+ }
+ else
+ {
+ // database was previously empty or never hit received status for some reason
+ joiningplayer.playerstats_basicstatus = PS_B_STATUS_UPDATING;
+ }
+ }
+ }
+ else
+ {
+ // server has this disabled, kill the DB and set status to idle
+ if(PS_B_IN_DB >= 0)
+ {
+ entity player;
+
+ db_close(PS_B_IN_DB);
+ PS_B_IN_DB = -1;
+
+ FOR_EACH_REALCLIENT(player) { player.playerstats_basicstatus = PS_B_STATUS_IDLE; }
+ }
+ }
+}
+
+void PlayerStats_PlayerBasic_CheckUpdate(entity joiningplayer)
+{
+ // determine whether we should retrieve playerbasic information again
+
+ #if 0
+ printf("PlayerStats_PlayerBasic_CheckUpdate('%s'): %f\n",
+ joiningplayer.netname,
+ time
+ );
+ #endif
+
+ // TODO: check to see if this playerid is inside the database already somehow...
+ // for now we'll just check the field, but this won't work for players who disconnect and reconnect properly
+ // although maybe we should just submit another request ANYWAY?
+ if(!joiningplayer.playerstats_basicstatus)
+ {
+ PlayerStats_PlayerBasic(
+ joiningplayer,
+ (joiningplayer.playerstats_basicstatus == PS_B_STATUS_RECEIVED)
+ );
+ }
+}
+
+void PlayerStats_PlayerBasic_Handler(entity fh, entity p, float status)
{
switch(status)
{
case URL_READY_CANWRITE:
{
- print("-- Sending data to player stats server\n");
+ dprint("-- Sending data to player stats server\n");
/*url_fputs(fh, "V 1\n");
#ifdef WATERMARK
url_fputs(fh, sprintf("R %s\n", WATERMARK));
case URL_READY_CANREAD:
{
string s = "";
- print("-- Got response from player stats server:\n");
+ dprint("-- Got response from player stats server:\n");
//string gametype = string_null;
while((s = url_fgets(fh)))
{
- print(" ", s, "\n");
+ dprint(" ", s, "\n");
/*
string key = "", value = "", data = "";
continue;
*/
}
- print("-- End of response.\n");
+ dprint("-- End of response.\n");
url_fclose(fh);
break;
}
}
}
}
+#endif // SVQC
+
+#ifdef MENUQC
+
+
+#if 0 // reading the entire DB at once
+ string e = "", en = "";
+ float i = 0;
+ for(e = PS_D_IN_EVL; (en = db_get(PS_D_IN_DB, e)) != ""; e = en)
+ {
+ print(sprintf("%d:%s:%s\n", i, e, db_get(PS_D_IN_DB, sprintf("#%s", e))));
+ ++i;
+ }
+#endif
-void PlayerStats_PlayerDetail()
+void PlayerStats_PlayerDetail_AddItem(string event, string data)
{
- //PS_D_IN_DB = -1;
- //PS_D_IN_DB = db_create();
+ if(PS_D_IN_DB < 0) { return; }
- //if(PS_D_IN_DB < 0) { return; }
+ // create a marker for the event so that we can access it later
+ string marker = sprintf("%s", event);
+ if(db_get(PS_D_IN_DB, marker) == "")
+ {
+ if(PS_D_IN_EVL)
+ {
+ db_put(PS_D_IN_DB, marker, PS_D_IN_EVL);
+ strunzone(PS_D_IN_EVL);
+ }
+ else { db_put(PS_D_IN_DB, marker, "#"); }
+ PS_D_IN_EVL = strzone(marker);
+ }
+ // now actually set the event data
+ db_put(PS_D_IN_DB, sprintf("#%s", event), data);
+ dprint("Added item ", sprintf("#%s", event), "=", data, " to PS_D_IN_DB\n");
+}
+
+void PlayerStats_PlayerDetail(void)
+{
+ // http://stats.xonotic.org/player/me
if((autocvar_g_playerstats_playerdetail_uri != "") && (crypto_getmyidstatus(0) > 0))
{
+ // create the database if it doesn't already exist
+ if(PS_D_IN_DB < 0)
+ PS_D_IN_DB = db_create();
+
//uri = strcat(uri, "/player/", uri_escape(crypto_getmyidfp(0)));
- print("Retrieving playerstats from URL: ", autocvar_g_playerstats_playerdetail_uri, "\n");
+ dprint("Retrieving playerstats from URL: ", autocvar_g_playerstats_playerdetail_uri, "\n");
url_single_fopen(
autocvar_g_playerstats_playerdetail_uri,
FILE_APPEND,
PlayerStats_PlayerDetail_Handler,
world
);
+
+ PlayerStats_PlayerDetail_Status = PS_D_STATUS_WAITING;
+ }
+ else
+ {
+ // player has this disabled, kill the DB and set status to idle
+ if(PS_D_IN_DB >= 0)
+ {
+ db_close(PS_D_IN_DB);
+ PS_D_IN_DB = -1;
+ }
+
+ PlayerStats_PlayerDetail_Status = PS_D_STATUS_IDLE;
+ }
+}
+
+void PlayerStats_PlayerDetail_CheckUpdate(void)
+{
+ // determine whether we should retrieve playerdetail information again
+ float gamecount = cvar("cl_matchcount");
+
+ #if 0
+ printf("PlayerStats_PlayerDetail_CheckUpdate(): %f >= %f, %d > %d\n",
+ time,
+ PS_D_NEXTUPDATETIME,
+ PS_D_LASTGAMECOUNT,
+ gamecount
+ );
+ #endif
+
+ if(
+ (time >= PS_D_NEXTUPDATETIME)
+ ||
+ (gamecount > PS_D_LASTGAMECOUNT)
+ )
+ {
+ PlayerStats_PlayerDetail();
+ PS_D_NEXTUPDATETIME = (time + autocvar_g_playerstats_playerdetail_autoupdatetime);
+ PS_D_LASTGAMECOUNT = gamecount;
+ }
+}
+
+void PlayerStats_PlayerDetail_Handler(entity fh, entity unused, float status)
+{
+ switch(status)
+ {
+ case URL_READY_CANWRITE:
+ {
+ dprint("PlayerStats_PlayerDetail_Handler(): Sending data to player stats server...\n");
+ url_fputs(fh, "V 1\n");
+ #ifdef WATERMARK
+ url_fputs(fh, sprintf("R %s\n", WATERMARK));
+ #endif
+ url_fputs(fh, sprintf("l %s\n", cvar_string("_menu_prvm_language"))); // language
+ //url_fputs(fh, sprintf("c %s\n", cvar_string("_cl_country"))); // country
+ //url_fputs(fh, sprintf("g %s\n", cvar_string("_cl_gender"))); // gender
+ url_fputs(fh, sprintf("n %s\n", cvar_string("_cl_name"))); // name
+ url_fputs(fh, sprintf("m %s %s\n", cvar_string("_cl_playermodel"), cvar_string("_cl_playerskin"))); // model/skin
+ url_fputs(fh, "\n");
+ url_fclose(fh);
+ break;
+ }
+
+ case URL_READY_CANREAD:
+ {
+ //print("PlayerStats_PlayerDetail_Handler(): Got response from player stats server:\n");
+ string input = "";
+ string gametype = "overall";
+ while((input = url_fgets(fh)))
+ {
+ float count = tokenizebyseparator(input, " ");
+ string key = "", event = "", data = "";
+
+ if(argv(0) == "#") { continue; }
+
+ if(count == 2)
+ {
+ key = argv(0);
+ data = substring(input, argv_start_index(1), strlen(input) - argv_start_index(1));
+ }
+ else if(count >= 3)
+ {
+ key = argv(0);
+ event = argv(1);
+ data = substring(input, argv_start_index(2), strlen(input) - argv_start_index(2));
+ }
+ else { continue; }
+
+ switch(key)
+ {
+ // general info
+ case "V": PlayerStats_PlayerDetail_AddItem("version", data); break;
+ case "R": PlayerStats_PlayerDetail_AddItem("release", data); break;
+ case "T": PlayerStats_PlayerDetail_AddItem("time", data); break;
+
+ // player info
+ case "S": PlayerStats_PlayerDetail_AddItem("statsurl", data); break;
+ case "P": PlayerStats_PlayerDetail_AddItem("hashkey", data); break;
+ case "n": PlayerStats_PlayerDetail_AddItem("playernick", data); break;
+ case "i": PlayerStats_PlayerDetail_AddItem("playerid", data); break;
+
+ // other/event info
+ case "G": gametype = data; break;
+ case "e":
+ {
+ if(event != "" && data != "")
+ {
+ PlayerStats_PlayerDetail_AddItem(
+ sprintf(
+ "%s/%s",
+ gametype,
+ event
+ ),
+ data
+ );
+ }
+ break;
+ }
+
+ default:
+ {
+ printf(
+ "PlayerStats_PlayerDetail_Handler(): ERROR: "
+ "Key went unhandled? Is our version outdated?\n"
+ "PlayerStats_PlayerDetail_Handler(): "
+ "Key '%s', Event '%s', Data '%s'\n",
+ key,
+ event,
+ data
+ );
+ break;
+ }
+ }
+
+ #if 0
+ print(sprintf(
+ "PlayerStats_PlayerDetail_Handler(): "
+ "Key '%s', Event '%s', Data '%s'\n",
+ key,
+ event,
+ data
+ ));
+ #endif
+ }
+ //print("PlayerStats_PlayerDetail_Handler(): End of response.\n");
+ url_fclose(fh);
+ PlayerStats_PlayerDetail_Status = PS_D_STATUS_RECEIVED;
+ statslist.getStats(statslist);
+ break;
+ }
+
+ case URL_READY_CLOSED:
+ {
+ // url_fclose has finished
+ print("PlayerStats_PlayerDetail_Handler(): Player stats synchronized with server.\n");
+ break;
+ }
+
+ case URL_READY_ERROR:
+ default:
+ {
+ print("PlayerStats_PlayerDetail_Handler(): Receiving player stats failed: ", ftos(status), "\n");
+ PlayerStats_PlayerDetail_Status = PS_D_STATUS_ERROR;
+ if(PS_D_IN_DB >= 0)
+ {
+ db_close(PS_D_IN_DB);
+ PS_D_IN_DB = -1;
+ }
+ break;
+ }
}
}
#endif
void PlayerInfo_Init()
{
- playerinfo_db = -1;
playerinfo_db = db_create();
}